├── .clang-format ├── .github └── workflows │ └── build_psvita.yml ├── .gitignore ├── Makefile ├── README.md ├── RSDKv4.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── WorkspaceSettings.xcsettings │ └── xcuserdata │ │ └── rubberduckycooly.xcuserdatad │ │ └── WorkspaceSettings.xcsettings └── xcshareddata │ └── xcschemes │ └── RSDKv4.xcscheme ├── Sonic1.Vita ├── CMakeLists.txt ├── build-internal.sh ├── build.sh └── sce_sys │ └── livearea │ └── contents │ └── template.xml ├── Sonic12Decomp.sln ├── Sonic12Decomp ├── Animation.cpp ├── Animation.hpp ├── Audio.cpp ├── Audio.hpp ├── Collision.cpp ├── Collision.hpp ├── Debug.cpp ├── Debug.hpp ├── Drawing.cpp ├── Drawing.hpp ├── Ini.cpp ├── Ini.hpp ├── Input.cpp ├── Input.hpp ├── Math.cpp ├── Math.hpp ├── Network.cpp ├── Network.hpp ├── Object.cpp ├── Object.hpp ├── Palette.cpp ├── Palette.hpp ├── PauseMenu.cpp ├── PauseMenu.hpp ├── Reader.cpp ├── Reader.hpp ├── RetroEngine.cpp ├── RetroEngine.hpp ├── RetroGameLoop.cpp ├── RetroGameLoop.hpp ├── Scene.cpp ├── Scene.hpp ├── Scene3D.cpp ├── Scene3D.hpp ├── Script.cpp ├── Script.hpp ├── Sonic12Decomp.rc ├── Sonic12Decomp.vcxproj ├── Sonic12Decomp.vcxproj.filters ├── Sprite.cpp ├── Sprite.hpp ├── String.cpp ├── String.hpp ├── Text.cpp ├── Text.hpp ├── Userdata.cpp ├── Userdata.hpp ├── main.cpp └── resource.h ├── Sonic2.Vita ├── CMakeLists.txt ├── build-internal.sh ├── build.sh └── sce_sys │ └── livearea │ └── contents │ └── template.xml ├── dependencies ├── all │ └── dependencies.txt ├── mac │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── Contents.json │ ├── Base.lproj │ │ └── Main.storyboard │ ├── cocoaHelpers.hpp │ ├── cocoaHelpers.mm │ └── dependencies.txt └── windows │ └── dependencies.txt └── networking_code_proto.py /.clang-format: -------------------------------------------------------------------------------- 1 | IndentWidth: 4 2 | Language: Cpp 3 | AlignAfterOpenBracket: Align 4 | SortIncludes: false 5 | ColumnLimit: 150 6 | PointerAlignment: Right 7 | AccessModifierOffset: -4 8 | AllowShortFunctionsOnASingleLine: All 9 | AllowShortCaseLabelsOnASingleLine: true 10 | #AllowShortLambdasOnASingleLine: All 11 | AllowShortLoopsOnASingleLine: true 12 | BinPackArguments: true 13 | BinPackParameters: true 14 | SpaceAfterCStyleCast: false 15 | BreakBeforeBraces: Attach 16 | BreakBeforeTernaryOperators: true 17 | BreakBeforeBinaryOperators: NonAssignment 18 | Cpp11BracedListStyle: false 19 | IndentCaseLabels: true 20 | AlignTrailingComments: true 21 | AlignOperands: true 22 | AlignConsecutiveAssignments: true 23 | AlignConsecutiveDeclarations: false 24 | AlignConsecutiveMacros: true 25 | UseTab: Never 26 | BreakBeforeBraces: Custom 27 | BraceWrapping: 28 | AfterClass: true 29 | AfterControlStatement: false 30 | AfterEnum: false 31 | AfterFunction: true 32 | AfterNamespace: true 33 | AfterObjCDeclaration: false 34 | AfterStruct: false 35 | AfterUnion: false 36 | BeforeCatch: false 37 | BeforeElse: true 38 | IndentBraces: false 39 | -------------------------------------------------------------------------------- /.github/workflows/build_psvita.yml: -------------------------------------------------------------------------------- 1 | name: Build Sonic 1 and 2 for PS Vita 2 | on: 3 | push: 4 | branches: 5 | - master 6 | - main 7 | - psvita 8 | 9 | jobs: 10 | sonic1-psvita: 11 | runs-on: ubuntu-latest 12 | container: vitasdk/vitasdk:latest 13 | steps: 14 | - name: Checkout repository 15 | uses: actions/checkout@v2 16 | - name: Install dependencies 17 | run: apk add --no-cache make cmake 18 | - name: Build Sonic 1 19 | run: | 20 | cd Sonic1.Vita 21 | cmake . 22 | make -j$(nproc) 23 | - name: Upload VPK 24 | uses: actions/upload-artifact@v2 25 | with: 26 | name: Sonic1-vita 27 | path: Sonic1.Vita/Sonic1.vpk 28 | if-no-files-found: error 29 | sonic2-psvita: 30 | runs-on: ubuntu-latest 31 | container: vitasdk/vitasdk:latest 32 | steps: 33 | - name: Checkout repository 34 | uses: actions/checkout@v2 35 | - name: Install dependencies 36 | run: apk add --no-cache make cmake 37 | - name: Build Sonic 2 38 | run: | 39 | cd Sonic2.Vita 40 | cmake . 41 | make -j$(nproc) 42 | - name: Upload VPK 43 | uses: actions/upload-artifact@v2 44 | with: 45 | name: sonic2-vita 46 | path: Sonic2.Vita/Sonic2.vpk 47 | if-no-files-found: error 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # VitaSDK artifacts 5 | dependencies/vita 6 | *.self* 7 | *.vpk* 8 | *.velf 9 | 10 | # CMake artifacts 11 | CMakeFiles/ 12 | cmake_install.cmake 13 | CMakeCache.txt 14 | Sonic1.Vita/Makefile 15 | Sonic2.Vita/Makefile 16 | 17 | # User-specific files 18 | *.suo 19 | *.user 20 | *.userosscache 21 | *.sln.docstates 22 | 23 | # User-specific files (MonoDevelop/Xamarin Studio) 24 | *.userprefs 25 | 26 | # Build results 27 | [Dd]ebug/ 28 | [Dd]ebugPublic/ 29 | [Rr]elease/ 30 | [Rr]eleases/ 31 | [Bb]uild/ 32 | x64/ 33 | x86/ 34 | bld/ 35 | bin/ 36 | [Bb]in/ 37 | [Oo]bj/ 38 | 39 | #dependencies 40 | [Dd]ependencies/windows/* 41 | [Dd]ependencies/mac/* 42 | [Dd]ependencies/all/* 43 | ![Dd]ependencies/all/dependencies.txt 44 | 45 | #OSX files 46 | *.DS_Store 47 | .DS_Store 48 | *.xactivitylog 49 | *.xcuserstate 50 | *.dia 51 | *.xbuild 52 | *.db 53 | *.plist 54 | *.idx 55 | *.pcm 56 | *.timestamp 57 | *.xcbkptlist 58 | 59 | # Visual Studio 2015 cache/options directory 60 | .vs/ 61 | .vscode/ 62 | 63 | # MSTest test Results 64 | [Tt]est[Rr]esult*/ 65 | [Bb]uild[Ll]og.* 66 | 67 | # NUNIT 68 | *.VisualState.xml 69 | TestResult.xml 70 | 71 | # Build Results of an ATL Project 72 | [Dd]ebugPS/ 73 | [Rr]eleasePS/ 74 | dlldata.c 75 | 76 | # DNX 77 | project.lock.json 78 | artifacts/ 79 | 80 | *_i.c 81 | *_p.c 82 | *_i.h 83 | *.ilk 84 | *.meta 85 | *.obj 86 | *.pch 87 | *.pdb 88 | *.pgc 89 | *.pgd 90 | *.rsp 91 | *.sbr 92 | *.tlb 93 | *.tli 94 | *.tlh 95 | *.tmp 96 | *.tmp_proj 97 | *.log 98 | *.vspscc 99 | *.vssscc 100 | .builds 101 | *.pidb 102 | *.svclog 103 | *.scc 104 | 105 | # Chutzpah Test files 106 | _Chutzpah* 107 | 108 | # Visual C++ cache files 109 | ipch/ 110 | *.aps 111 | *.ncb 112 | *.opensdf 113 | *.sdf 114 | *.cachefile 115 | 116 | # Visual Studio profiler 117 | *.psess 118 | *.vsp 119 | *.vspx 120 | 121 | # TFS 2012 Local Workspace 122 | $tf/ 123 | 124 | # Guidance Automation Toolkit 125 | *.gpState 126 | 127 | # ReSharper is a .NET coding add-in 128 | _ReSharper*/ 129 | *.[Rr]e[Ss]harper 130 | *.DotSettings.user 131 | 132 | # JustCode is a .NET coding add-in 133 | .JustCode 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # NCrunch 142 | _NCrunch_* 143 | .*crunch*.local.xml 144 | 145 | # MightyMoose 146 | *.mm.* 147 | AutoTest.Net/ 148 | 149 | # Web workbench (sass) 150 | .sass-cache/ 151 | 152 | # Installshield output folder 153 | [Ee]xpress/ 154 | 155 | # DocProject is a documentation generator add-in 156 | DocProject/buildhelp/ 157 | DocProject/Help/*.HxT 158 | DocProject/Help/*.HxC 159 | DocProject/Help/*.hhc 160 | DocProject/Help/*.hhk 161 | DocProject/Help/*.hhp 162 | DocProject/Help/Html2 163 | DocProject/Help/html 164 | 165 | # Click-Once directory 166 | publish/ 167 | 168 | # Publish Web Output 169 | *.[Pp]ublish.xml 170 | *.azurePubxml 171 | ## TODO: Comment the next line if you want to checkin your 172 | ## web deploy settings but do note that will include unencrypted 173 | ## passwords 174 | #*.pubxml 175 | 176 | *.publishproj 177 | 178 | # NuGet Packages 179 | *.nupkg 180 | # The packages folder can be ignored because of Package Restore 181 | **/packages/* 182 | # except build/, which is used as an MSBuild target. 183 | !**/packages/build/ 184 | # Uncomment if necessary however generally it will be regenerated when needed 185 | #!**/packages/repositories.config 186 | 187 | # Windows Azure Build Output 188 | csx/ 189 | *.build.csdef 190 | 191 | # Windows Store app package directory 192 | AppPackages/ 193 | 194 | # Visual Studio cache files 195 | # files ending in .cache can be ignored 196 | *.[Cc]ache 197 | # but keep track of directories ending in .cache 198 | !*.[Cc]ache/ 199 | 200 | # Others 201 | ClientBin/ 202 | [Ss]tyle[Cc]op.* 203 | ~$* 204 | *~ 205 | *.dbmdl 206 | *.dbproj.schemaview 207 | *.pfx 208 | *.publishsettings 209 | node_modules/ 210 | orleans.codegen.cs 211 | 212 | # RIA/Silverlight projects 213 | Generated_Code/ 214 | 215 | # Backup & report files from converting an old project file 216 | # to a newer Visual Studio version. Backup files are not needed, 217 | # because we have git ;-) 218 | _UpgradeReport_Files/ 219 | Backup*/ 220 | UpgradeLog*.XML 221 | UpgradeLog*.htm 222 | 223 | # SQL Server files 224 | *.mdf 225 | *.ldf 226 | 227 | # Business Intelligence projects 228 | *.rdl.data 229 | *.bim.layout 230 | *.bim_*.settings 231 | 232 | # Microsoft Fakes 233 | FakesAssemblies/ 234 | 235 | # Node.js Tools for Visual Studio 236 | .ntvs_analysis.dat 237 | 238 | # Visual Studio 6 build log 239 | *.plg 240 | 241 | # Visual Studio 6 workspace options file 242 | *.opt 243 | 244 | # LightSwitch generated files 245 | GeneratedArtifacts/ 246 | _Pvt_Extensions/ 247 | ModelManifest.xml 248 | 249 | Ignored/ 250 | 251 | # Compiled object files 252 | objects/ 253 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CXXFLAGS_ALL = $(shell pkg-config --cflags sdl2 vorbisfile vorbis) $(CXXFLAGS) 2 | LDFLAGS_ALL = $(LDFLAGS) 3 | LIBS_ALL = $(shell pkg-config --libs sdl2 vorbisfile vorbis) -pthread $(LIBS) 4 | 5 | SOURCES = Sonic12Decomp/Animation.cpp \ 6 | Sonic12Decomp/Audio.cpp \ 7 | Sonic12Decomp/Collision.cpp \ 8 | Sonic12Decomp/Debug.cpp \ 9 | Sonic12Decomp/Drawing.cpp \ 10 | Sonic12Decomp/Ini.cpp \ 11 | Sonic12Decomp/Input.cpp \ 12 | Sonic12Decomp/main.cpp \ 13 | Sonic12Decomp/Math.cpp \ 14 | Sonic12Decomp/Network.cpp \ 15 | Sonic12Decomp/Object.cpp \ 16 | Sonic12Decomp/Palette.cpp \ 17 | Sonic12Decomp/PauseMenu.cpp \ 18 | Sonic12Decomp/Reader.cpp \ 19 | Sonic12Decomp/RetroEngine.cpp \ 20 | Sonic12Decomp/RetroGameLoop.cpp \ 21 | Sonic12Decomp/Scene.cpp \ 22 | Sonic12Decomp/Scene3D.cpp \ 23 | Sonic12Decomp/Script.cpp \ 24 | Sonic12Decomp/Sprite.cpp \ 25 | Sonic12Decomp/String.cpp \ 26 | Sonic12Decomp/Text.cpp \ 27 | Sonic12Decomp/Userdata.cpp \ 28 | 29 | objects/%.o: % 30 | mkdir -p $(@D) 31 | $(CXX) $(CXXFLAGS_ALL) $^ -o $@ -c 32 | 33 | bin/sonic2013: $(SOURCES:%=objects/%.o) 34 | mkdir -p $(@D) 35 | $(CXX) $(CXXFLAGS_ALL) $(LDFLAGS_ALL) $^ -o $@ $(LIBS_ALL) 36 | 37 | install: bin/sonic2013 38 | install -Dp -m755 bin/sonic2013 $(prefix)/bin/sonic2013 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sonic 1/2 2013 Decompilation 2 | A Full Decompilation of Sonic 1 & 2 (2013) 3 | 4 | # **SUPPORT THE OFFICIAL RELEASE OF SONIC 1 & SONIC 2** 5 | + Without assets from the official releases this decompilation will not run. 6 | + Video tutorial on how to find your legally obtained data.rsdk file: https://www.youtube.com/watch?v=gzIfRW91IxE 7 | 8 | + You can get the official release of sonic 1 & sonic 2 from: 9 | * [Sonic 1 (iOS, Via the App Store)](https://apps.apple.com/au/app/sonic-the-hedgehog-classic/id316050001) 10 | * [Sonic 2 (iOS, Via the App Store)](https://apps.apple.com/au/app/sonic-the-hedgehog-2-classic/id347415188) 11 | * [Sonic 1 (Android, Via Google Play)](https://play.google.com/store/apps/details?id=com.sega.sonic1px&hl=en_AU&gl=US) 12 | * [Sonic 2 (Android, Via Google Play)](https://play.google.com/store/apps/details?id=com.sega.sonic2.runner&hl=en_AU&gl=US) 13 | * [Sonic 1 (Android, Via Amazon)](https://www.amazon.com.au/Sega-of-America-Sonic-Hedgehog/dp/B00D74DVKM) 14 | * [Sonic 2 (Android, Via Amazon)](https://www.amazon.com.au/Sega-of-America-Sonic-Hedgehog/dp/B00HAPRVWS) 15 | 16 | Even if your platform isn't supported by the official releases, buy it for the assets (you don't need to run the official release, you just need the game assets) 17 | 18 | If you want to transfer your save from the **Android pre-forever versions,** you can go to `Android/data/com.sega.sonic1 or 2/SGame.bin` and copy it to the `SData.bin` in the EXE folder. 19 | 20 | # Additional Tweaks 21 | * added a built in script compiler, similar to CD, but tweaked up to match the new syntax for the scripts used in RSDKv4 22 | * There is now a settings.ini file that the game uses to load all settings, similar to Sonic Mania 23 | * Dev menu can now be accessed from anywhere by pressing the `ESC` key if enabled in the config 24 | * The `f12` pause, `f11` step over & fast forward debug features from sonic mania have all be ported and are enabled if devMenu is enabled in the config 25 | * If `devMenu` is enabled in the config, pressing `f10` will activate a palette overlay that shows the game's 8 internal palettes in real time 26 | 27 | # TODOs: 28 | * the "native object" system has been implimented, but the objects (aside from RetroGameLoop and a temporary pause menu) and the proper HW rendering system have yet to be added 29 | * probably some more bug fixes, because there always are a few stragglers 30 | * create a `cmakelists.txt` file for windows compiling so builds can be added automatically via git actions 31 | * S2 networking code, we attempted to write code to handle the 2PVS mode in S2 but we couldn't finish for many reasons, we did leave our WIP code in the game, so if you think you could do it by all means give it a shot! 32 | 33 | # How to build: 34 | ## Windows: 35 | * Clone the repo, then follow the instructions in the [depencencies readme for windows](./dependencies/windows/dependencies.txt) to setup dependencies, then build via the visual studio solution 36 | * or grab a prebuilt executable from the releases section 37 | 38 | ## Mac: 39 | * Clone the repo, then follow the instructions in the [depencencies readme for mac](./dependencies/mac/dependencies.txt) to setup dependencies, then build via the xcode project 40 | * a mac build of v1.0.0 by sappharad can be found [here](https://github.com/Sappharad/Sonic-1-2-2013-Decompilation/releases/tag/1.0.0mac) 41 | 42 | ## Switch: 43 | * head on over to [heyjoeway's fork](https://github.com/heyjoeway/Sonic-1-2-2013-Decompilation) and follow the installation instructions in the readme 44 | 45 | ## Other platforms: 46 | Currently the only supported platforms are the ones listed above, however the backend uses libogg, libvorbis & SDL2 to power it, so the codebase is very multiplatform. 47 | if you've cloned this repo and ported it to a platform not on the list or made some changes you'd like to see added to this repo, submit a pull request and it'll most likely be added 48 | 49 | # FAQ 50 | ### Q: The screen is tearing, how do I fix it? 51 | A: Try turning on vsync, that worked for me (tested on mac) 52 | 53 | ### Q: I found a bug/I have a feature request! 54 | A: Submit an issue in the issues tab and I'll fix/add (if possible) it as soon as I can 55 | 56 | ### Q: Will you do a decompilation for Sonic CD (2011)? 57 | A: I already have! you can find it [here](https://github.com/Rubberduckycooly/Sonic-CD-11-Decompilation)! 58 | 59 | ### Q: Will you do a decompilation for Sonic Mania? 60 | A: No. Sonic Mania is a ton bigger and requires that I'd decompile not only how the (far more complex) RSDKv5 works, but also all _600_+ objects work 61 | 62 | # Special Thanks 63 | * [RMGRich](https://github.com/MGRich): for helping me fix bugs, tweaking up my sometimes sloppy code and generally being really helpful and fun to work with on this project 64 | * Everyone in the [Retro Engine Modding Server](https://dc.railgun.works/retroengine): for being supportive of me and for giving me a place to show off these things that I've found 65 | 66 | # Contact: 67 | you can join the [Retro Engine Modding Discord Server](https://dc.railgun.works/retroengine) for any extra questions you may need to know about the decompilation or modding it 68 | -------------------------------------------------------------------------------- /RSDKv4.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /RSDKv4.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /RSDKv4.xcodeproj/project.xcworkspace/xcuserdata/rubberduckycooly.xcuserdatad/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildLocationStyle 6 | CustomLocation 7 | CustomBuildIntermediatesPath 8 | build/mac/intermediates.noindex 9 | CustomBuildLocationType 10 | RelativeToWorkspace 11 | CustomBuildProductsPath 12 | build/mac/ 13 | DerivedDataLocationStyle 14 | Default 15 | IssueFilterStyle 16 | ShowActiveSchemeOnly 17 | LiveSourceIssuesEnabled 18 | 19 | ShowSharedSchemesAutomaticallyEnabled 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /RSDKv4.xcodeproj/xcshareddata/xcschemes/RSDKv4.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 57 | 58 | 59 | 60 | 66 | 68 | 74 | 75 | 76 | 77 | 79 | 80 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /Sonic1.Vita/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ## This file is a quick tutorial on writing CMakeLists for targeting the Vita 2 | cmake_minimum_required(VERSION 2.8) 3 | 4 | ## This includes the Vita toolchain, must go before project definition 5 | # It is a convenience so you do not have to type 6 | # -DCMAKE_TOOLCHAIN_FILE=$VITASDK/share/vita.toolchain.cmake for cmake. It is 7 | # highly recommended that you include this block for all projects. 8 | if(NOT DEFINED CMAKE_TOOLCHAIN_FILE) 9 | if(DEFINED ENV{VITASDK}) 10 | set(CMAKE_TOOLCHAIN_FILE "$ENV{VITASDK}/share/vita.toolchain.cmake" CACHE PATH "toolchain file") 11 | else() 12 | message(FATAL_ERROR "Please define VITASDK to point to your SDK path!") 13 | endif() 14 | endif() 15 | 16 | ## Define project parameters here 17 | # Name of the project 18 | project(Sonic1) 19 | # This line adds Vita helper macros, must go after project definition in order 20 | # to build Vita specific artifacts (self/vpk). 21 | include("${VITASDK}/share/vita.cmake" REQUIRED) 22 | 23 | ## Configuration options for this app 24 | # Display name (under bubble in LiveArea) 25 | set(VITA_APP_NAME "Sonic 1") 26 | # Unique ID must be exactly 9 characters. Recommended: XXXXYYYYY where X = 27 | # unique string of developer and Y = a unique number for this app 28 | set(VITA_TITLEID "RSDK00002") 29 | # Optional version string to show in LiveArea's more info screen 30 | set(VITA_VERSION "01.00") 31 | 32 | ## Flags and includes for building 33 | # Note that we make sure not to overwrite previous flags 34 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -O3 -fsigned-char -fno-lto") 35 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -O3 -fsigned-char -fno-lto -fno-rtti -fno-exceptions") 36 | # Optional. You can specify more param.sfo flags this way. 37 | set(VITA_MKSFOEX_FLAGS "${VITA_MKSFOEX_FLAGS} -d PARENTAL_LEVEL=1") 38 | 39 | add_definitions(-DRETRO_GAME_SONIC=1) 40 | 41 | # Add any additional include paths here 42 | include_directories( 43 | /home/user/vitasdk/arm-vita-eabi/include 44 | ) 45 | 46 | # Add any additional library paths here 47 | # ${CMAKE_CURRENT_BINARY_DIR} lets you use any library currently being built 48 | link_directories( 49 | ${CMAKE_CURRENT_BINARY_DIR} 50 | ) 51 | 52 | ## Build and link 53 | # Add all the files needed to compile here 54 | add_executable(${PROJECT_NAME} 55 | ../Sonic12Decomp/Animation.cpp 56 | ../Sonic12Decomp/Audio.cpp 57 | ../Sonic12Decomp/Collision.cpp 58 | ../Sonic12Decomp/Debug.cpp 59 | ../Sonic12Decomp/Drawing.cpp 60 | ../Sonic12Decomp/Ini.cpp 61 | ../Sonic12Decomp/Input.cpp 62 | ../Sonic12Decomp/main.cpp 63 | ../Sonic12Decomp/Math.cpp 64 | ../Sonic12Decomp/Network.cpp 65 | ../Sonic12Decomp/Object.cpp 66 | ../Sonic12Decomp/Palette.cpp 67 | ../Sonic12Decomp/PauseMenu.cpp 68 | ../Sonic12Decomp/Reader.cpp 69 | ../Sonic12Decomp/RetroEngine.cpp 70 | ../Sonic12Decomp/RetroGameLoop.cpp 71 | ../Sonic12Decomp/Scene.cpp 72 | ../Sonic12Decomp/Scene3D.cpp 73 | ../Sonic12Decomp/Script.cpp 74 | ../Sonic12Decomp/Sprite.cpp 75 | ../Sonic12Decomp/String.cpp 76 | ../Sonic12Decomp/Text.cpp 77 | ../Sonic12Decomp/Userdata.cpp 78 | ) 79 | 80 | # Library to link to (drop the -l prefix). This will mostly be stubs. 81 | target_link_libraries(${PROJECT_NAME} 82 | SDL2 83 | vita2d 84 | vorbisfile 85 | vorbis 86 | ogg 87 | pthread 88 | SceDisplay_stub 89 | SceAudio_stub 90 | SceCtrl_stub 91 | SceSysmodule_stub 92 | SceGxm_stub 93 | SceCommonDialog_stub 94 | SceAppMgr_stub 95 | SceTouch_stub 96 | SceHid_stub 97 | SceAudio_stub 98 | SceMotion_stub 99 | m 100 | ) 101 | 102 | ## Create Vita files 103 | vita_create_self(${PROJECT_NAME}.self ${PROJECT_NAME}) 104 | # The FILE directive lets you add additional files to the VPK, the syntax is 105 | # FILE src_path dst_path_in_vpk. In this case, we add the LiveArea paths. 106 | vita_create_vpk(${PROJECT_NAME}.vpk ${VITA_TITLEID} ${PROJECT_NAME}.self 107 | VERSION ${VITA_VERSION} 108 | NAME ${VITA_APP_NAME} 109 | # FILE sce_sys/icon0.png sce_sys/icon0.png 110 | # FILE sce_sys/livearea/contents/bg.png sce_sys/livearea/contents/bg.png 111 | # FILE sce_sys/livearea/contents/startup.png sce_sys/livearea/contents/startup.png 112 | FILE sce_sys/livearea/contents/template.xml sce_sys/livearea/contents/template.xml 113 | ) 114 | -------------------------------------------------------------------------------- /Sonic1.Vita/build-internal.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | apk add --no-cache make cmake 3 | cmake . && \ 4 | make -j$(nproc) 5 | rm Sonic1 # this is not ignored by .gitignore 6 | -------------------------------------------------------------------------------- /Sonic1.Vita/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | sudo docker run \ 3 | --rm \ 4 | -v $PWD/..:/work \ 5 | -w /work/Sonic1.Vita \ 6 | vitasdk/vitasdk \ 7 | /bin/sh -C "./build-internal.sh" 8 | -------------------------------------------------------------------------------- /Sonic1.Vita/sce_sys/livearea/contents/template.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | bg.png 6 | 7 | 8 | 9 | startup.png 10 | 11 | 12 | -------------------------------------------------------------------------------- /Sonic12Decomp.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29806.167 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Sonic12Decomp", "Sonic12Decomp\Sonic12Decomp.vcxproj", "{BD4A4266-8ED9-491E-B4CC-3F63F0D39C1B}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {BD4A4266-8ED9-491E-B4CC-3F63F0D39C1B}.Debug|x64.ActiveCfg = Debug|x64 17 | {BD4A4266-8ED9-491E-B4CC-3F63F0D39C1B}.Debug|x64.Build.0 = Debug|x64 18 | {BD4A4266-8ED9-491E-B4CC-3F63F0D39C1B}.Debug|x86.ActiveCfg = Debug|Win32 19 | {BD4A4266-8ED9-491E-B4CC-3F63F0D39C1B}.Debug|x86.Build.0 = Debug|Win32 20 | {BD4A4266-8ED9-491E-B4CC-3F63F0D39C1B}.Release|x64.ActiveCfg = Release|x64 21 | {BD4A4266-8ED9-491E-B4CC-3F63F0D39C1B}.Release|x64.Build.0 = Release|x64 22 | {BD4A4266-8ED9-491E-B4CC-3F63F0D39C1B}.Release|x86.ActiveCfg = Release|Win32 23 | {BD4A4266-8ED9-491E-B4CC-3F63F0D39C1B}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {CEEC6B21-D41E-4C5C-8B97-9FAAD7974615} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /Sonic12Decomp/Animation.cpp: -------------------------------------------------------------------------------- 1 | #include "RetroEngine.hpp" 2 | 3 | AnimationFile animationFileList[ANIFILE_COUNT]; 4 | int animationFileCount = 0; 5 | 6 | SpriteFrame scriptFrames[SPRITEFRAME_COUNT]; 7 | int scriptFrameCount = 0; 8 | 9 | SpriteFrame animFrames[SPRITEFRAME_COUNT]; 10 | int animFrameCount = 0; 11 | SpriteAnimation animationList[ANIMATION_COUNT]; 12 | int animationCount = 0; 13 | Hitbox hitboxList[HITBOX_COUNT]; 14 | int hitboxCount = 0; 15 | 16 | void LoadAnimationFile(char *filePath) 17 | { 18 | FileInfo info; 19 | if (LoadFile(filePath, &info)) { 20 | int fileBuffer = 0; 21 | char strBuf[0x21]; 22 | byte sheetIDs[0x18]; 23 | sheetIDs[0] = 0; 24 | 25 | int sheetCount = 0; 26 | FileRead(&sheetCount, 1); // Sheet Count 27 | 28 | for (int s = 0; s < sheetCount; ++s) { 29 | FileRead(&fileBuffer, 1); 30 | if (fileBuffer) { 31 | int i = 0; 32 | for (; i < fileBuffer; ++i) FileRead(&strBuf[i], 1); 33 | strBuf[i] = 0; 34 | GetFileInfo(&info); 35 | CloseFile(); 36 | sheetIDs[s] = AddGraphicsFile(strBuf); 37 | SetFileInfo(&info); 38 | } 39 | } 40 | 41 | int animCount = 0; 42 | FileRead(&animCount, 1); 43 | AnimationFile *animFile = &animationFileList[animationFileCount]; 44 | animFile->animCount = animCount; 45 | animFile->aniListOffset = animationCount; 46 | 47 | for (int a = 0; a < animCount; ++a) { 48 | SpriteAnimation *anim = &animationList[animationCount++]; 49 | anim->frameListOffset = animFrameCount; 50 | FileRead(&fileBuffer, 1); 51 | FileRead(anim->name, fileBuffer); 52 | anim->name[fileBuffer] = 0; 53 | FileRead(&anim->frameCount, 1); 54 | FileRead(&anim->speed, 1); 55 | FileRead(&anim->loopPoint, 1); 56 | FileRead(&anim->rotationFlag, 1); 57 | 58 | for (int j = 0; j < anim->frameCount; ++j) { 59 | SpriteFrame *frame = &animFrames[animFrameCount++]; 60 | FileRead(&frame->sheetID, 1); 61 | frame->sheetID = sheetIDs[frame->sheetID]; 62 | FileRead(&frame->hitboxID, 1); 63 | FileRead(&frame->sprX, 1); 64 | FileRead(&frame->sprY, 1); 65 | FileRead(&frame->width, 1); 66 | FileRead(&frame->height, 1); 67 | 68 | sbyte buffer = 0; 69 | FileRead(&buffer, 1); 70 | frame->pivotX = buffer; 71 | FileRead(&buffer, 1); 72 | frame->pivotY = buffer; 73 | } 74 | // 90 Degree (Extra rotation Frames) rotation 75 | if (anim->rotationFlag == ROTFLAG_STATICFRAMES) 76 | anim->frameCount >>= 1; 77 | } 78 | 79 | animFile->hitboxListOffset = hitboxCount; 80 | FileRead(&fileBuffer, 1); 81 | for (int i = 0; i < fileBuffer; ++i) { 82 | Hitbox *hitbox = &hitboxList[hitboxCount++]; 83 | for (int d = 0; d < HITBOX_DIR_COUNT; ++d) { 84 | FileRead(&hitbox->left[d], 1); 85 | FileRead(&hitbox->top[d], 1); 86 | FileRead(&hitbox->right[d], 1); 87 | FileRead(&hitbox->bottom[d], 1); 88 | } 89 | } 90 | 91 | CloseFile(); 92 | } 93 | } 94 | void ClearAnimationData() 95 | { 96 | for (int f = 0; f < SPRITEFRAME_COUNT; ++f) MEM_ZERO(scriptFrames[f]); 97 | for (int f = 0; f < SPRITEFRAME_COUNT; ++f) MEM_ZERO(animFrames[f]); 98 | for (int h = 0; h < HITBOX_COUNT; ++h) MEM_ZERO(hitboxList[h]); 99 | for (int a = 0; a < ANIMATION_COUNT; ++a) MEM_ZERO(animationList[a]); 100 | for (int a = 0; a < ANIFILE_COUNT; ++a) MEM_ZERO(animationFileList[a]); 101 | 102 | scriptFrameCount = 0; 103 | animFrameCount = 0; 104 | animationCount = 0; 105 | animationFileCount = 0; 106 | hitboxCount = 0; 107 | 108 | //Used for pause menu 109 | LoadGIFFile("Data/Game/SystemText.gif", SURFACE_MAX - 1); 110 | StrCopy(gfxSurface[SURFACE_MAX - 1].fileName, "Data/Game/SystemText.gif"); 111 | } 112 | 113 | AnimationFile *AddAnimationFile(char *filePath) 114 | { 115 | char path[0x80]; 116 | StrCopy(path, "Data/Animations/"); 117 | StrAdd(path, filePath); 118 | 119 | for (int a = 0; a < 0x100; ++a) { 120 | if (StrLength(animationFileList[a].fileName) <= 0) { 121 | StrCopy(animationFileList[a].fileName, filePath); 122 | LoadAnimationFile(path); 123 | ++animationFileCount; 124 | return &animationFileList[a]; 125 | } 126 | if (StrComp(animationFileList[a].fileName, filePath)) 127 | return &animationFileList[a]; 128 | } 129 | return NULL; 130 | } 131 | 132 | void ProcessObjectAnimation(void *objScr, void *ent) 133 | { 134 | ObjectScript *objectScript = (ObjectScript *)objScr; 135 | Entity *entity = (Entity *)ent; 136 | SpriteAnimation *sprAnim = &animationList[objectScript->animFile->aniListOffset + entity->animation]; 137 | 138 | if (entity->animationSpeed <= 0) { 139 | entity->animationTimer += sprAnim->speed; 140 | } 141 | else { 142 | if (entity->animationSpeed > 0xF0) 143 | entity->animationSpeed = 0xF0; 144 | entity->animationTimer += entity->animationSpeed; 145 | } 146 | if (entity->animation != entity->prevAnimation) { 147 | entity->prevAnimation = entity->animation; 148 | entity->frame = 0; 149 | entity->animationTimer = 0; 150 | entity->animationSpeed = 0; 151 | } 152 | if (entity->animationTimer > 0xEF) { 153 | entity->animationTimer -= 0xF0; 154 | ++entity->frame; 155 | } 156 | 157 | if (entity->frame >= sprAnim->frameCount) 158 | entity->frame = sprAnim->loopPoint; 159 | } -------------------------------------------------------------------------------- /Sonic12Decomp/Animation.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ANIMATION_H 2 | #define ANIMATION_H 3 | 4 | #define ANIFILE_COUNT (0x100) 5 | #define ANIMATION_COUNT (0x400) 6 | #define SPRITEFRAME_COUNT (0x1000) 7 | 8 | #define HITBOX_COUNT (0x20) 9 | #define HITBOX_DIR_COUNT (0x8) 10 | 11 | enum AnimrotationFlags { ROTFLAG_NONE, ROTFLAG_FULL, ROTFLAG_45DEG, ROTFLAG_STATICFRAMES }; 12 | 13 | struct AnimationFile 14 | { 15 | char fileName[0x20]; 16 | int animCount; 17 | int aniListOffset; 18 | int hitboxListOffset; 19 | }; 20 | 21 | struct SpriteAnimation { 22 | char name[16]; 23 | byte frameCount; 24 | byte speed; 25 | byte loopPoint; 26 | byte rotationFlag; 27 | int frameListOffset; 28 | }; 29 | 30 | struct SpriteFrame { 31 | int sprX; 32 | int sprY; 33 | int width; 34 | int height; 35 | int pivotX; 36 | int pivotY; 37 | byte sheetID; 38 | byte hitboxID; 39 | }; 40 | 41 | struct Hitbox { 42 | sbyte left[HITBOX_DIR_COUNT]; 43 | sbyte top[HITBOX_DIR_COUNT]; 44 | sbyte right[HITBOX_DIR_COUNT]; 45 | sbyte bottom[HITBOX_DIR_COUNT]; 46 | }; 47 | 48 | extern AnimationFile animationFileList[ANIFILE_COUNT]; 49 | extern int animationFileCount; 50 | 51 | extern SpriteFrame scriptFrames[SPRITEFRAME_COUNT]; 52 | extern int scriptFrameCount; 53 | 54 | extern SpriteFrame animFrames[SPRITEFRAME_COUNT]; 55 | extern int animFrameCount; 56 | extern SpriteAnimation animationList[ANIMATION_COUNT]; 57 | extern int animationCount; 58 | extern Hitbox hitboxList[HITBOX_COUNT]; 59 | extern int hitboxCount; 60 | 61 | void LoadAnimationFile(char *FilePath); 62 | void ClearAnimationData(); 63 | 64 | AnimationFile *AddAnimationFile(char *FilePath); 65 | 66 | inline AnimationFile *GetDefaultAnimationRef() { return &animationFileList[0]; } 67 | 68 | void ProcessObjectAnimation(void *objScr, void *ent); 69 | 70 | #endif // !ANIMATION_H 71 | -------------------------------------------------------------------------------- /Sonic12Decomp/Audio.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AUDIO_H 2 | #define AUDIO_H 3 | 4 | #define TRACK_COUNT (0x10) 5 | #define SFX_COUNT (0x100) 6 | #define CHANNEL_COUNT (0x8) //4 in the original, 8 for convenience 7 | 8 | #define MAX_VOLUME (100) 9 | 10 | struct TrackInfo { 11 | char fileName[0x40]; 12 | bool trackLoop; 13 | uint loopPoint; 14 | }; 15 | 16 | struct MusicPlaybackInfo { 17 | #if RETRO_USING_SDL 18 | OggVorbis_File vorbisFile; 19 | int vorbBitstream; 20 | SDL_AudioStream *stream; 21 | Sint16 *buffer; 22 | #endif 23 | FileInfo fileInfo; 24 | bool trackLoop; 25 | uint loopPoint; 26 | bool loaded; 27 | }; 28 | 29 | struct SFXInfo { 30 | char name[0x40]; 31 | Sint16 *buffer; 32 | size_t length; 33 | bool loaded; 34 | }; 35 | 36 | struct ChannelInfo { 37 | size_t sampleLength; 38 | Sint16 *samplePtr; 39 | int sfxID; 40 | byte loopSFX; 41 | sbyte pan; 42 | }; 43 | 44 | enum MusicStatuses { 45 | MUSIC_STOPPED = 0, 46 | MUSIC_PLAYING = 1, 47 | MUSIC_PAUSED = 2, 48 | MUSIC_LOADING = 3, 49 | MUSIC_READY = 4, 50 | }; 51 | 52 | extern int globalSFXCount; 53 | extern int stageSFXCount; 54 | 55 | extern int masterVolume; 56 | extern int trackID; 57 | extern int sfxVolume; 58 | extern int bgmVolume; 59 | extern bool audioEnabled; 60 | 61 | extern bool musicEnabled; 62 | extern int musicStatus; 63 | extern int musicStartPos; 64 | extern int musicRatio; 65 | extern TrackInfo musicTracks[TRACK_COUNT]; 66 | 67 | extern SFXInfo sfxList[SFX_COUNT]; 68 | extern char sfxNames[SFX_COUNT][0x40]; 69 | 70 | extern ChannelInfo sfxChannels[CHANNEL_COUNT]; 71 | 72 | extern MusicPlaybackInfo musInfo; 73 | 74 | #if RETRO_USING_SDL 75 | extern SDL_AudioSpec audioDeviceFormat; 76 | #endif 77 | 78 | int InitAudioPlayback(); 79 | 80 | #if RETRO_USING_SDL 81 | void ProcessMusicStream(Sint32 *stream, size_t bytes_wanted); 82 | void ProcessAudioPlayback(void *data, Uint8 *stream, int len); 83 | void ProcessAudioMixing(Sint32 *dst, const Sint16 *src, int len, int volume, sbyte pan); 84 | 85 | 86 | inline void freeMusInfo() 87 | { 88 | if (musInfo.loaded) { 89 | SDL_LockAudio(); 90 | 91 | if (musInfo.buffer) 92 | delete[] musInfo.buffer; 93 | if (musInfo.stream) 94 | SDL_FreeAudioStream(musInfo.stream); 95 | ov_clear(&musInfo.vorbisFile); 96 | musInfo.buffer = nullptr; 97 | musInfo.stream = nullptr; 98 | musInfo.trackLoop = false; 99 | musInfo.loopPoint = 0; 100 | musInfo.loaded = false; 101 | 102 | SDL_UnlockAudio(); 103 | } 104 | } 105 | #else 106 | void ProcessMusicStream() {} 107 | void ProcessAudioPlayback() {} 108 | void ProcessAudioMixing() {} 109 | 110 | inline void freeMusInfo() 111 | { 112 | if (musInfo.loaded) { 113 | if (musInfo.musicFile) 114 | delete[] musInfo.musicFile; 115 | musInfo.musicFile = nullptr; 116 | musInfo.buffer = nullptr; 117 | musInfo.stream = nullptr; 118 | musInfo.pos = 0; 119 | musInfo.len = 0; 120 | musInfo.currentTrack = nullptr; 121 | musInfo.loaded = false; 122 | } 123 | } 124 | #endif 125 | 126 | void SetMusicTrack(const char *filePath, byte trackID, bool loop, uint loopPoint); 127 | void SwapMusicTrack(const char *filePath, byte trackID, uint loopPoint, uint ratio); 128 | bool PlayMusic(int track, int musStartPos); 129 | inline void StopMusic() 130 | { 131 | musicStatus = MUSIC_STOPPED; 132 | SDL_LockAudio(); 133 | freeMusInfo(); 134 | SDL_UnlockAudio(); 135 | } 136 | 137 | void LoadSfx(char *filePath, byte sfxID); 138 | void PlaySfx(int sfx, bool loop); 139 | inline void StopSfx(int sfx) 140 | { 141 | for (int i = 0; i < CHANNEL_COUNT; ++i) { 142 | if (sfxChannels[i].sfxID == sfx) { 143 | MEM_ZERO(sfxChannels[i]); 144 | sfxChannels[i].sfxID = -1; 145 | } 146 | } 147 | } 148 | void SetSfxAttributes(int sfx, int loopCount, sbyte pan); 149 | 150 | void SetSfxName(const char *sfxName, int sfxID); 151 | 152 | //Helper Func 153 | inline bool PlaySFXByName(const char* sfx, sbyte loopCnt) { 154 | for (int s = 0; s < globalSFXCount + stageSFXCount; ++s) { 155 | if (StrComp(sfxNames[s], sfx)) { 156 | PlaySfx(s, loopCnt); 157 | return true; 158 | } 159 | } 160 | return false; 161 | } 162 | 163 | inline void SetMusicVolume(int volume) 164 | { 165 | if (volume < 0) 166 | volume = 0; 167 | if (volume > MAX_VOLUME) 168 | volume = MAX_VOLUME; 169 | masterVolume = volume; 170 | } 171 | 172 | inline void SetGameVolumes(int bgmVolume, int sfxVolume) { 173 | //musicVolumeSetting = bgmVolume; 174 | SetMusicVolume(masterVolume); 175 | //sfxVolumeSetting = ((sfxVolume << 7) / 100); 176 | } 177 | 178 | inline void PauseSound() 179 | { 180 | if (musicStatus == MUSIC_PLAYING) 181 | musicStatus = MUSIC_PAUSED; 182 | } 183 | 184 | inline void ResumeSound() 185 | { 186 | if (musicStatus == MUSIC_PAUSED) 187 | musicStatus = MUSIC_PLAYING; 188 | } 189 | 190 | 191 | inline void StopAllSfx() 192 | { 193 | #if RETRO_USING_SDL 194 | SDL_LockAudio(); 195 | #endif 196 | for (int i = 0; i < CHANNEL_COUNT; ++i) sfxChannels[i].sfxID = -1; 197 | #if RETRO_USING_SDL 198 | SDL_UnlockAudio(); 199 | #endif 200 | } 201 | inline void ReleaseGlobalSfx() 202 | { 203 | for (int i = globalSFXCount; i >= 0; --i) { 204 | if (sfxList[i].loaded) { 205 | StrCopy(sfxList[i].name, ""); 206 | StrCopy(sfxNames[i], ""); 207 | free(sfxList[i].buffer); 208 | sfxList[i].length = 0; 209 | sfxList[i].loaded = false; 210 | } 211 | } 212 | globalSFXCount = 0; 213 | } 214 | inline void ReleaseStageSfx() 215 | { 216 | for (int i = stageSFXCount + globalSFXCount; i >= globalSFXCount; --i) { 217 | if (sfxList[i].loaded) { 218 | StrCopy(sfxList[i].name, ""); 219 | StrCopy(sfxNames[i], ""); 220 | free(sfxList[i].buffer); 221 | sfxList[i].length = 0; 222 | sfxList[i].loaded = false; 223 | } 224 | } 225 | stageSFXCount = 0; 226 | } 227 | 228 | inline void ReleaseAudioDevice() 229 | { 230 | StopMusic(); 231 | StopAllSfx(); 232 | ReleaseStageSfx(); 233 | ReleaseGlobalSfx(); 234 | } 235 | 236 | #endif // !AUDIO_H 237 | -------------------------------------------------------------------------------- /Sonic12Decomp/Collision.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COLLISION_H 2 | #define COLLISION_H 3 | 4 | enum CollisionSidess { 5 | CSIDE_FLOOR = 0, 6 | CSIDE_LWALL = 1, 7 | CSIDE_RWALL = 2, 8 | CSIDE_ROOF = 3, 9 | }; 10 | 11 | enum CollisionModes { 12 | CMODE_FLOOR = 0, 13 | CMODE_LWALL = 1, 14 | CMODE_ROOF = 2, 15 | CMODE_RWALL = 3, 16 | }; 17 | 18 | enum CollisionSolidity { 19 | SOLID_ALL = 0, 20 | SOLID_TOP = 1, 21 | SOLID_LRB = 2, 22 | SOLID_NONE = 3, 23 | SOLID_TOP_NOGRIP = 4, 24 | }; 25 | 26 | enum ObjectCollisionTypes { 27 | C_TOUCH = 0, 28 | C_BOX = 1, 29 | C_BOX2 = 2, 30 | C_PLATFORM = 3, 31 | }; 32 | 33 | struct CollisionSensor 34 | { 35 | int XPos; 36 | int YPos; 37 | int angle; 38 | bool collided; 39 | }; 40 | 41 | extern int collisionLeft; 42 | extern int collisionTop; 43 | extern int collisionRight; 44 | extern int collisionBottom; 45 | 46 | extern int collisionTolerance; 47 | 48 | extern CollisionSensor sensors[7]; 49 | 50 | void FindFloorPosition(Entity *player, CollisionSensor *sensor, int startYPos); 51 | void FindLWallPosition(Entity *player, CollisionSensor *sensor, int startXPos); 52 | void FindRoofPosition(Entity *player, CollisionSensor *sensor, int startYPos); 53 | void FindRWallPosition(Entity *player, CollisionSensor *sensor, int startXPos); 54 | void FloorCollision(Entity *player, CollisionSensor *sensor); 55 | void LWallCollision(Entity *player, CollisionSensor *sensor); 56 | void RoofCollision(Entity *player, CollisionSensor *sensor); 57 | void RWallCollision(Entity *player, CollisionSensor *sensor); 58 | void SetPathGripSensors(Entity *player); 59 | 60 | void ProcessPathGrip(Entity *player); 61 | void ProcessAirCollision(Entity *player); 62 | 63 | void ProcessPlayerTileCollisions(Entity *player); 64 | 65 | void TouchCollision(Entity *thisEntity, int thisLeft, int thisTop, int thisRight, int thisBottom, Entity *otherEntity, int otherLeft, int otherTop, 66 | int otherRight, int otherBottom); 67 | void BoxCollision(Entity *thisEntity, int thisLeft, int thisTop, int thisRight, int thisBottom, Entity *otherEntity, int otherLeft, int otherTop, 68 | int otherRight, int otherBottom); // Standard 69 | void BoxCollision2(Entity *thisEntity, int thisLeft, int thisTop, int thisRight, int thisBottom, Entity *otherEntity, int otherLeft, int otherTop, 70 | int otherRight, int otherBottom); // Updated (?) 71 | void PlatformCollision(Entity *thisEntity, int thisLeft, int thisTop, int thisRight, int thisBottom, Entity *otherEntity, int otherLeft, int otherTop, 72 | int otherRight, int otherBottom); 73 | 74 | void ObjectFloorCollision(int xOffset, int yOffset, int cPath); 75 | void ObjectLWallCollision(int xOffset, int yOffset, int cPath); 76 | void ObjectRoofCollision(int xOffset, int yOffset, int cPath); 77 | void ObjectRWallCollision(int xOffset, int yOffset, int cPath); 78 | 79 | void ObjectFloorGrip(int xOffset, int yOffset, int cPath); 80 | void ObjectLWallGrip(int xOffset, int yOffset, int cPath); 81 | void ObjectRoofGrip(int xOffset, int yOffset, int cPath); 82 | void ObjectRWallGrip(int xOffset, int yOffset, int cPath); 83 | 84 | #endif // !COLLISION_H 85 | -------------------------------------------------------------------------------- /Sonic12Decomp/Debug.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DEBUG_H 2 | #define DEBUG_H 3 | 4 | extern bool endLine; 5 | inline void printLog(const char *msg, ...) 6 | { 7 | #ifndef RETRO_DISABLE_LOG 8 | if (engineDebugMode) { 9 | char buffer[0x100]; 10 | 11 | // make the full string 12 | va_list args; 13 | va_start(args, msg); 14 | vsprintf(buffer, msg, args); 15 | if (endLine) { 16 | printf("%s\n", buffer); 17 | sprintf(buffer, "%s\n", buffer); 18 | } 19 | else { 20 | printf("%s", buffer); 21 | sprintf(buffer, "%s", buffer); 22 | } 23 | 24 | char pathBuffer[0x100]; 25 | #if RETRO_PLATFORM == RETRO_OSX 26 | if (!usingCWD) 27 | sprintf(pathBuffer, "%s/log.txt", getResourcesPath()); 28 | else 29 | sprintf(pathBuffer, "log.txt"); 30 | #else 31 | sprintf(pathBuffer, BASE_PATH "log.txt"); 32 | #endif 33 | FileIO *file = fOpen(pathBuffer, "a"); 34 | if (file) { 35 | fWrite(&buffer, 1, StrLength(buffer), file); 36 | fClose(file); 37 | } 38 | } 39 | #endif 40 | } 41 | 42 | inline void printLog(const ushort *msg) 43 | { 44 | #ifndef RETRO_DISABLE_LOG 45 | if (engineDebugMode) { 46 | int mPos = 0; 47 | while (msg[mPos]) { 48 | printf("%lc", (wint_t)msg[mPos]); 49 | mPos++; 50 | } 51 | if (endLine) 52 | printf("\n"); 53 | 54 | char pathBuffer[0x100]; 55 | #if RETRO_PLATFORM == RETRO_OSX 56 | if (!usingCWD) 57 | sprintf(pathBuffer, "%s/log.txt", getResourcesPath()); 58 | else 59 | sprintf(pathBuffer, "log.txt"); 60 | #else 61 | sprintf(pathBuffer, BASE_PATH "log.txt"); 62 | #endif 63 | mPos = 0; 64 | FileIO *file = fOpen(pathBuffer, "a"); 65 | if (file) { 66 | while (msg[mPos]) { 67 | fWrite(&msg[mPos], 2, 1, file); 68 | mPos++; 69 | } 70 | 71 | ushort el = '\n'; 72 | if (endLine) 73 | fWrite(&el, 2, 1, file); 74 | fClose(file); 75 | } 76 | } 77 | #endif 78 | } 79 | 80 | enum DevMenuMenus { 81 | DEVMENU_MAIN, 82 | DEVMENU_PLAYERSEL, 83 | DEVMENU_STAGELISTSEL, 84 | DEVMENU_STAGESEL, 85 | DEVMENU_SCRIPTERROR, 86 | }; 87 | 88 | enum StartMenuMenus { 89 | STARTMENU_MAIN = 5, 90 | STARTMENU_SAVESEL, 91 | STARTMENU_PLAYERSEL, 92 | STARTMENU_GAMEOPTS, 93 | STARTMENU_TASTAGESEL, 94 | STARTMENU_TACONFIRMSEL, 95 | STARTMENU_ACHIEVEMENTS, 96 | STARTMENU_LEADERBOARDS, 97 | }; 98 | 99 | void initDevMenu(); 100 | void initErrorMessage(); 101 | void processStageSelect(); 102 | 103 | // added due to lack of normal main menu 104 | void initStartMenu(int mode); 105 | void processStartMenu(); 106 | void setTextMenu(int mode); 107 | 108 | #endif //! DEBUG_H 109 | -------------------------------------------------------------------------------- /Sonic12Decomp/Drawing.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DRAWING_H 2 | #define DRAWING_H 3 | 4 | #define SPRITESHEETS_MAX (16) 5 | #define SURFACE_MAX (24) 6 | #define GFXDATA_MAX (0x200000) 7 | 8 | #define BLENDTABLE_YSIZE (0x100) 9 | #define BLENDTABLE_XSIZE (0x20) 10 | #define BLENDTABLE_SIZE (BLENDTABLE_XSIZE * BLENDTABLE_YSIZE) 11 | #define TINTTABLE_SIZE (0x1000) 12 | 13 | #define DRAWLAYER_COUNT (0x7) 14 | 15 | enum FlipFlags { FLIP_NONE, FLIP_X, FLIP_Y, FLIP_XY }; 16 | enum InkFlags { INK_NONE, INK_BLEND, INK_ALPHA, INK_ADD, INK_SUB }; 17 | enum DrawFXFlags { FX_SCALE, FX_ROTATE, FX_ROTOZOOM, FX_INK, FX_TINT, FX_FLIP }; 18 | 19 | struct DrawListEntry 20 | { 21 | int entityRefs[ENTITY_COUNT]; 22 | int listSize; 23 | }; 24 | 25 | struct GFXSurface 26 | { 27 | char fileName[0x40]; 28 | int height; 29 | int width; 30 | int widthShift; 31 | int depth; 32 | int dataPosition; 33 | }; 34 | 35 | extern short blendLookupTable[BLENDTABLE_SIZE]; 36 | extern short subtractLookupTable[BLENDTABLE_SIZE]; 37 | extern short tintLookupTable[TINTTABLE_SIZE]; 38 | 39 | extern int SCREEN_XSIZE; 40 | extern int SCREEN_CENTERX; 41 | 42 | extern DrawListEntry drawListEntries[DRAWLAYER_COUNT]; 43 | 44 | extern int gfxDataPosition; 45 | extern GFXSurface gfxSurface[SURFACE_MAX]; 46 | extern byte graphicData[GFXDATA_MAX]; 47 | 48 | int InitRenderDevice(); 49 | void RenderRenderDevice(); 50 | void ReleaseRenderDevice(); 51 | 52 | void GenerateBlendLookupTable(); 53 | 54 | inline void ClearGraphicsData() 55 | { 56 | for (int i = 0; i < SURFACE_MAX; ++i) StrCopy(gfxSurface[i].fileName, ""); 57 | gfxDataPosition = 0; 58 | } 59 | void ClearScreen(byte index); 60 | 61 | void SetScreenSize(int width, int height); 62 | 63 | // Layer Drawing 64 | void DrawObjectList(int layer); 65 | void DrawStageGFX(); 66 | 67 | // TileLayer Drawing 68 | void DrawHLineScrollLayer(int layerID); 69 | void DrawVLineScrollLayer(int layerID); 70 | void Draw3DFloorLayer(int layerID); 71 | void Draw3DSkyLayer(int layerID); 72 | 73 | // Shape Drawing 74 | void DrawRectangle(int XPos, int YPos, int width, int height, int R, int G, int B, int A); 75 | void SetFadeHQ(int R, int G, int B, int A); 76 | void DrawTintRectangle(int XPos, int YPos, int width, int height); 77 | void DrawScaledTintMask(int direction, int XPos, int YPos, int pivotX, int pivotY, int scaleX, 78 | int scaleY, int width, int height, int sprX, int sprY, int sheetID); 79 | 80 | // Sprite Drawing 81 | void DrawSprite(int XPos, int YPos, int width, int height, int sprX, int sprY, int sheetID); 82 | void DrawSpriteFlipped(int XPos, int YPos, int width, int height, int sprX, int sprY, int direction, 83 | int sheetID); 84 | void DrawSpriteScaled(int direction, int XPos, int YPos, int pivotX, int pivotY, int scaleX, int scaleY, 85 | int width, int height, int sprX, int sprY, int sheetID); 86 | void DrawSpriteRotated(int direction, int XPos, int YPos, int pivotX, int pivotY, int sprX, int sprY, 87 | int width, int height, int rotation, int sheetID); 88 | void DrawSpriteRotozoom(int direction, int XPos, int YPos, int pivotX, int pivotY, int sprX, int sprY, 89 | int width, int height, int rotation, int scale, int sheetID); 90 | 91 | void DrawBlendedSprite(int XPos, int YPos, int width, int height, int sprX, int sprY, int sheetID); 92 | void DrawAlphaBlendedSprite(int XPos, int YPos, int width, int height, int sprX, int sprY, int alpha, 93 | int sheetID); 94 | void DrawAdditiveBlendedSprite(int XPos, int YPos, int width, int height, int sprX, int sprY, int alpha, 95 | int sheetID); 96 | void DrawSubtractiveBlendedSprite(int XPos, int YPos, int width, int height, int sprX, int sprY, 97 | int alpha, int sheetID); 98 | 99 | void DrawObjectAnimation(void *objScr, void *ent, int XPos, int YPos); 100 | 101 | void DrawFace(void *v, uint colour); 102 | void DrawFadedFace(void *v, uint colour, uint fogColour, int alpha); 103 | void DrawTexturedFace(void *v, byte sheetID); 104 | void DrawTexturedFace2(void *v, byte sheetID); 105 | 106 | void DrawBitmapText(void *menu, int XPos, int YPos, int scale, int spacing, int rowStart, int rowCount); 107 | 108 | void DrawTextMenu(void *menu, int XPos, int YPos); 109 | void DrawTextMenuEntry(void *menu, int rowID, int XPos, int YPos, int textHighlight); 110 | void DrawStageTextEntry(void *menu, int rowID, int XPos, int YPos, int textHighlight); 111 | void DrawBlendedTextMenuEntry(void *menu, int rowID, int XPos, int YPos, int textHighlight); 112 | 113 | #endif // !DRAWING_H 114 | -------------------------------------------------------------------------------- /Sonic12Decomp/Ini.cpp: -------------------------------------------------------------------------------- 1 | #include "RetroEngine.hpp" 2 | #include 3 | #include 4 | #include 5 | 6 | IniParser::IniParser(const char *filename) 7 | { 8 | memset(items, 0, 0x80 * sizeof(ConfigItem)); 9 | char buf[0x100]; 10 | char section[0x40]; 11 | bool hasSection = false; 12 | char key[0x40]; 13 | char value[0x100]; 14 | 15 | count = 0; 16 | 17 | char pathBuffer[0x80]; 18 | 19 | #if RETRO_PLATFORM == RETRO_OSX 20 | if (!usingCWD) 21 | sprintf(pathBuffer, "%s/%s", getResourcesPath(), filename); 22 | else 23 | sprintf(pathBuffer, "%s", filename); 24 | #else 25 | sprintf(pathBuffer, "%s", filename); 26 | #endif 27 | 28 | FileIO *f; 29 | if ((f = fOpen(pathBuffer, "r")) == NULL) { 30 | printLog("ERROR: Couldn't open file '%s'!", filename); 31 | return; 32 | } 33 | 34 | while (true) { 35 | bool flag = false; 36 | int ret = 0; 37 | int strLen = 0; 38 | while (true) { 39 | ret = (int)fRead(&buf[strLen++], sizeof(byte), 1, f); 40 | flag = ret == 0; 41 | if (ret == 0) 42 | break; 43 | if (buf[strLen - 1] == '\n') 44 | break; 45 | } 46 | buf[strLen] = 0; 47 | if (buf[0] == '#') 48 | continue; 49 | 50 | if (sscanf(buf, "[%[^][]]", section) == 1) { 51 | hasSection = true; 52 | } 53 | else if (sscanf(buf, "%[^ =]= %s", key, value) == 2 || sscanf(buf, "%[^ =]=%s", key, value) == 2 54 | || sscanf(buf, "%[^ =] = %s", key, value) == 2 || sscanf(buf, "%[^ =] =%s", key, value) == 2) { 55 | if (hasSection) 56 | sprintf(items[count].section, "%s", section); 57 | 58 | sprintf(items[count].key, "%s", key); 59 | sprintf(items[count].value, "%s", value); 60 | items[count].hasSection = hasSection; 61 | count++; 62 | } 63 | if (flag) 64 | break; 65 | } 66 | 67 | fClose(f); 68 | } 69 | 70 | int IniParser::GetString(const char *section, const char *key, char *dest) 71 | { 72 | if (count == 0) 73 | return 0; 74 | 75 | for (int x = 0; x < count; x++) { 76 | if (!strcmp(section, items[x].section)) { 77 | if (!strcmp(key, items[x].key)) { 78 | strcpy(dest, items[x].value); 79 | return 1; 80 | } 81 | } 82 | } 83 | 84 | return 0; 85 | } 86 | int IniParser::GetInteger(const char *section, const char *key, int *dest) 87 | { 88 | if (count == 0) 89 | return 0; 90 | 91 | for (int x = 0; x < count; x++) { 92 | if (!strcmp(section, items[x].section)) { 93 | if (!strcmp(key, items[x].key)) { 94 | *dest = atoi(items[x].value); 95 | return 1; 96 | } 97 | } 98 | } 99 | 100 | return 0; 101 | } 102 | int IniParser::GetFloat(const char *section, const char *key, float *dest) 103 | { 104 | if (count == 0) 105 | return 0; 106 | 107 | for (int x = 0; x < count; x++) { 108 | if (!strcmp(section, items[x].section)) { 109 | if (!strcmp(key, items[x].key)) { 110 | *dest = atof(items[x].value); 111 | return 1; 112 | } 113 | } 114 | } 115 | 116 | return 0.0f; 117 | } 118 | int IniParser::GetBool(const char *section, const char *key, bool *dest) 119 | { 120 | if (count == 0) 121 | return 0; 122 | 123 | for (int x = 0; x < count; x++) { 124 | if (!strcmp(section, items[x].section)) { 125 | if (!strcmp(key, items[x].key)) { 126 | *dest = !strcmp(items[x].value, "true") || !strcmp(items[x].value, "1"); 127 | return 1; 128 | } 129 | } 130 | } 131 | 132 | return 0; 133 | } 134 | 135 | int IniParser::SetString(const char *section, const char *key, char *value) 136 | { 137 | int where = -1; 138 | for (int x = 0; x < count; x++) { 139 | if (strcmp(section, items[x].section) == 0) { 140 | if (strcmp(key, items[x].key) == 0) { 141 | where = x; 142 | break; 143 | } 144 | } 145 | } 146 | if (where < 0) 147 | where = count++; 148 | 149 | strcpy(items[where].section, section); 150 | strcpy(items[where].key, key); 151 | strcpy(items[where].value, value); 152 | items[where].type = INI_ITEM_STRING; 153 | return 1; 154 | } 155 | int IniParser::SetInteger(const char *section, const char *key, int value) 156 | { 157 | int where = -1; 158 | for (int x = 0; x < count; x++) { 159 | if (strcmp(section, items[x].section) == 0) { 160 | if (strcmp(key, items[x].key) == 0) { 161 | where = x; 162 | break; 163 | } 164 | } 165 | } 166 | if (where < 0) 167 | where = count++; 168 | 169 | strcpy(items[where].section, section); 170 | strcpy(items[where].key, key); 171 | sprintf(items[where].value, "%d", value); 172 | items[where].type = INI_ITEM_INT; 173 | return 1; 174 | } 175 | int IniParser::SetFloat(const char *section, const char *key, float value) 176 | { 177 | int where = -1; 178 | for (int x = 0; x < count; x++) { 179 | if (strcmp(section, items[x].section) == 0) { 180 | if (strcmp(key, items[x].key) == 0) { 181 | where = x; 182 | break; 183 | } 184 | } 185 | } 186 | if (where < 0) 187 | where = count++; 188 | 189 | strcpy(items[where].section, section); 190 | strcpy(items[where].key, key); 191 | sprintf(items[where].value, "%f", value); 192 | items[where].type = INI_ITEM_FLOAT; 193 | return 1; 194 | } 195 | int IniParser::SetBool(const char *section, const char *key, bool value) 196 | { 197 | int where = -1; 198 | for (int x = 0; x < count; x++) { 199 | if (strcmp(section, items[x].section) == 0) { 200 | if (strcmp(key, items[x].key) == 0) { 201 | where = x; 202 | break; 203 | } 204 | } 205 | } 206 | if (where < 0) 207 | where = count++; 208 | 209 | strcpy(items[where].section, section); 210 | strcpy(items[where].key, key); 211 | sprintf(items[where].value, "%s", value ? "true" : "false"); 212 | items[where].type = INI_ITEM_BOOL; 213 | return 1; 214 | } 215 | int IniParser::SetComment(const char *section, const char *key, const char *comment) 216 | { 217 | int where = -1; 218 | for (int x = 0; x < count; x++) { 219 | if (strcmp(section, items[x].section) == 0) { 220 | if (strcmp(key, items[x].key) == 0) { 221 | where = x; 222 | break; 223 | } 224 | } 225 | } 226 | if (where < 0) 227 | where = count++; 228 | 229 | strcpy(items[where].section, section); 230 | strcpy(items[where].key, key); 231 | sprintf(items[where].value, "%s", comment); 232 | items[where].type = INI_ITEM_COMMENT; 233 | return 1; 234 | } 235 | 236 | void IniParser::Write(const char *filename) 237 | { 238 | char pathBuffer[0x80]; 239 | 240 | #if RETRO_PLATFORM == RETRO_OSX 241 | if (!usingCWD) 242 | sprintf(pathBuffer, "%s/%s", getResourcesPath(), filename); 243 | else 244 | sprintf(pathBuffer, "%s", filename); 245 | #else 246 | sprintf(pathBuffer, "%s", filename); 247 | #endif 248 | 249 | FileIO *f; 250 | if ((f = fOpen(pathBuffer, "w")) == NULL) { 251 | printLog("ERROR: Couldn't open file '%s' for writing!", filename); 252 | return; 253 | } 254 | 255 | char sections[10][60]; 256 | char past[60]; 257 | int c = 0; 258 | sprintf(past, ""); 259 | for (int i = 0; i < count; ++i) { 260 | if (std::find(std::begin(sections), std::end(sections), items[i].section) == std::end(sections) && strcmp(past, items[i].section) != 0) { 261 | sprintf(past, "%s", items[i].section); 262 | sprintf(sections[c], "%s", items[i].section); 263 | c++; 264 | } 265 | } 266 | 267 | if (c >= 1) { 268 | if (strcmp(sections[0], sections[c - 1]) == 0) 269 | c--; 270 | } 271 | 272 | char buffer[0x100]; 273 | for (int s = 0; s < c; ++s) { 274 | sprintf(buffer, "[%s]\n", sections[s]); 275 | fWrite(&buffer, 1, StrLength(buffer), f); 276 | for (int i = 0; i < count; ++i) { 277 | if (strcmp(sections[s], items[i].section) == 0) { 278 | switch (items[i].type) { 279 | default: 280 | case INI_ITEM_STRING: 281 | case INI_ITEM_INT: 282 | case INI_ITEM_FLOAT: 283 | case INI_ITEM_BOOL: 284 | sprintf(buffer, "%s=%s\n", items[i].key, items[i].value); 285 | fWrite(&buffer, 1, StrLength(buffer), f); 286 | break; 287 | case INI_ITEM_COMMENT: 288 | sprintf(buffer, "; %s\n", items[i].value); 289 | fWrite(&buffer, 1, StrLength(buffer), f); 290 | break; 291 | } 292 | } 293 | } 294 | 295 | if (s + 1 < c) { 296 | sprintf(buffer, "\n"); 297 | fWrite(&buffer, StrLength(buffer), 1, f); 298 | } 299 | } 300 | 301 | fClose(f); 302 | } 303 | -------------------------------------------------------------------------------- /Sonic12Decomp/Ini.hpp: -------------------------------------------------------------------------------- 1 | #ifndef INI_H 2 | #define INI_H 3 | 4 | class IniParser 5 | { 6 | public: 7 | enum ItemType { 8 | INI_ITEM_STRING, 9 | INI_ITEM_INT, 10 | INI_ITEM_FLOAT, 11 | INI_ITEM_BOOL, 12 | INI_ITEM_COMMENT, 13 | }; 14 | 15 | struct ConfigItem { 16 | ConfigItem() 17 | { 18 | sprintf(section, ""); 19 | sprintf(key, ""); 20 | sprintf(value, ""); 21 | hasSection = false; 22 | type = INI_ITEM_STRING; 23 | } 24 | char section[0x20]; 25 | bool hasSection = false; 26 | char key[0x40]; 27 | char value[0x100]; 28 | byte type = INI_ITEM_STRING; 29 | }; 30 | 31 | IniParser() { memset(items, 0, 0x80 * sizeof(ConfigItem)); } 32 | IniParser(const char *filename); 33 | 34 | int GetString(const char *section, const char *key, char *dest); 35 | int GetInteger(const char *section, const char *key, int *dest); 36 | int GetFloat(const char *section, const char *key, float *dest); 37 | int GetBool(const char *section, const char *key, bool *dest); 38 | int SetString(const char *section, const char *key, char *value); 39 | int SetInteger(const char *section, const char *key, int value); 40 | int SetFloat(const char *section, const char *key, float value); 41 | int SetBool(const char *section, const char *key, bool value); 42 | int SetComment(const char *section, const char *key, const char *comment); 43 | void Write(const char *filename); 44 | 45 | ConfigItem items[0x80]; 46 | 47 | int count = 0; 48 | }; 49 | #endif // !INI_H 50 | -------------------------------------------------------------------------------- /Sonic12Decomp/Input.cpp: -------------------------------------------------------------------------------- 1 | #include "RetroEngine.hpp" 2 | 3 | InputData keyPress = InputData(); 4 | InputData keyDown = InputData(); 5 | 6 | bool anyPress = false; 7 | 8 | int touchDown[8]; 9 | int touchX[8]; 10 | int touchY[8]; 11 | int touchID[8]; 12 | int touches = 0; 13 | 14 | InputButton inputDevice[9]; 15 | int inputType = 0; 16 | 17 | int LSTICK_DEADZONE = 20000; 18 | int RSTICK_DEADZONE = 20000; 19 | int LTRIGGER_DEADZONE = 20000; 20 | int RTRIGGER_DEADZONE = 20000; 21 | 22 | #if RETRO_USING_SDL 23 | SDL_GameController *controller; 24 | #endif 25 | 26 | // Easier this way 27 | enum ExtraSDLButtons { 28 | SDL_CONTROLLER_BUTTON_ZL = SDL_CONTROLLER_BUTTON_MAX + 1, 29 | SDL_CONTROLLER_BUTTON_ZR, 30 | SDL_CONTROLLER_BUTTON_LSTICK_UP, 31 | SDL_CONTROLLER_BUTTON_LSTICK_DOWN, 32 | SDL_CONTROLLER_BUTTON_LSTICK_LEFT, 33 | SDL_CONTROLLER_BUTTON_LSTICK_RIGHT, 34 | SDL_CONTROLLER_BUTTON_RSTICK_UP, 35 | SDL_CONTROLLER_BUTTON_RSTICK_DOWN, 36 | SDL_CONTROLLER_BUTTON_RSTICK_LEFT, 37 | SDL_CONTROLLER_BUTTON_RSTICK_RIGHT, 38 | SDL_CONTROLLER_BUTTON_MAX_EXTRA, 39 | }; 40 | 41 | #if RETRO_USING_SDL 42 | bool getControllerButton(byte buttonID) 43 | { 44 | if (SDL_GameControllerGetButton(controller, (SDL_GameControllerButton)buttonID)) { 45 | return true; 46 | } 47 | else { 48 | switch (buttonID) { 49 | default: break; 50 | case SDL_CONTROLLER_BUTTON_DPAD_UP: return SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTY) < -LSTICK_DEADZONE; 51 | case SDL_CONTROLLER_BUTTON_DPAD_DOWN: return SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTY) > LSTICK_DEADZONE; 52 | case SDL_CONTROLLER_BUTTON_DPAD_LEFT: return SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTX) < -LSTICK_DEADZONE; 53 | case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: return SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTX) > LSTICK_DEADZONE; 54 | } 55 | } 56 | 57 | switch (buttonID) { 58 | default: break; 59 | case SDL_CONTROLLER_BUTTON_ZL: return SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_TRIGGERLEFT) > LTRIGGER_DEADZONE; 60 | case SDL_CONTROLLER_BUTTON_ZR: return SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT) > RTRIGGER_DEADZONE; 61 | case SDL_CONTROLLER_BUTTON_LSTICK_UP: return SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTY) < -LSTICK_DEADZONE; 62 | case SDL_CONTROLLER_BUTTON_LSTICK_DOWN: return SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTY) > LSTICK_DEADZONE; 63 | case SDL_CONTROLLER_BUTTON_LSTICK_LEFT: return SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTX) < -LSTICK_DEADZONE; 64 | case SDL_CONTROLLER_BUTTON_LSTICK_RIGHT: return SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTX) > LSTICK_DEADZONE; 65 | case SDL_CONTROLLER_BUTTON_RSTICK_UP: return SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY) < -RSTICK_DEADZONE; 66 | case SDL_CONTROLLER_BUTTON_RSTICK_DOWN: return SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY) > RSTICK_DEADZONE; 67 | case SDL_CONTROLLER_BUTTON_RSTICK_LEFT: return SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX) < -RSTICK_DEADZONE; 68 | case SDL_CONTROLLER_BUTTON_RSTICK_RIGHT: return SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX) > RSTICK_DEADZONE; 69 | } 70 | 71 | return false; 72 | } 73 | #endif 74 | 75 | void ProcessInput() 76 | { 77 | #if RETRO_USING_SDL 78 | int length = 0; 79 | const byte *keyState = SDL_GetKeyboardState(&length); 80 | 81 | if (inputType == 0) { 82 | for (int i = 0; i < 8; i++) { 83 | if (keyState[inputDevice[i].keyMappings]) { 84 | inputDevice[i].setHeld(); 85 | inputDevice[8].setHeld(); 86 | continue; 87 | } 88 | else if (inputDevice[i].hold) 89 | inputDevice[i].setReleased(); 90 | } 91 | } 92 | else if (inputType == 1) { 93 | for (int i = 0; i < 8; i++) { 94 | if (getControllerButton(inputDevice[i].contMappings)) { 95 | inputDevice[i].setHeld(); 96 | inputDevice[8].setHeld(); 97 | continue; 98 | } 99 | else if (inputDevice[i].hold) 100 | inputDevice[i].setReleased(); 101 | } 102 | } 103 | 104 | if (keyState[inputDevice[0].keyMappings] || keyState[inputDevice[1].keyMappings] || keyState[inputDevice[2].keyMappings] 105 | || keyState[inputDevice[3].keyMappings] || keyState[inputDevice[4].keyMappings] || keyState[inputDevice[5].keyMappings] 106 | || keyState[inputDevice[6].keyMappings] || keyState[inputDevice[7].keyMappings]) { 107 | inputType = 0; 108 | } 109 | else if (inputType == 0) 110 | inputDevice[8].setReleased(); 111 | 112 | if (getControllerButton(SDL_CONTROLLER_BUTTON_A) || getControllerButton(SDL_CONTROLLER_BUTTON_B) || getControllerButton(SDL_CONTROLLER_BUTTON_X) 113 | || getControllerButton(SDL_CONTROLLER_BUTTON_Y) || getControllerButton(SDL_CONTROLLER_BUTTON_LEFTSHOULDER) 114 | || getControllerButton(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) || getControllerButton(SDL_CONTROLLER_BUTTON_ZL) 115 | || getControllerButton(SDL_CONTROLLER_BUTTON_ZR) || getControllerButton(SDL_CONTROLLER_BUTTON_DPAD_UP) 116 | || getControllerButton(SDL_CONTROLLER_BUTTON_DPAD_DOWN) || getControllerButton(SDL_CONTROLLER_BUTTON_DPAD_LEFT) 117 | || getControllerButton(SDL_CONTROLLER_BUTTON_DPAD_RIGHT) || getControllerButton(SDL_CONTROLLER_BUTTON_LSTICK_UP) 118 | || getControllerButton(SDL_CONTROLLER_BUTTON_LSTICK_DOWN) || getControllerButton(SDL_CONTROLLER_BUTTON_LSTICK_LEFT) 119 | || getControllerButton(SDL_CONTROLLER_BUTTON_LSTICK_RIGHT) || getControllerButton(SDL_CONTROLLER_BUTTON_RSTICK_UP) 120 | || getControllerButton(SDL_CONTROLLER_BUTTON_RSTICK_DOWN) || getControllerButton(SDL_CONTROLLER_BUTTON_RSTICK_LEFT) 121 | || getControllerButton(SDL_CONTROLLER_BUTTON_RSTICK_RIGHT) || getControllerButton(SDL_CONTROLLER_BUTTON_START)) { 122 | inputType = 1; 123 | } 124 | else if (inputType == 1) 125 | inputDevice[8].setReleased(); 126 | #endif 127 | } 128 | 129 | void CheckKeyPress(InputData *input, byte flags) 130 | { 131 | if (flags & 0x1) 132 | input->up = inputDevice[0].press; 133 | if (flags & 0x2) 134 | input->down = inputDevice[1].press; 135 | if (flags & 0x4) 136 | input->left = inputDevice[2].press; 137 | if (flags & 0x8) 138 | input->right = inputDevice[3].press; 139 | if (flags & 0x10) 140 | input->A = inputDevice[4].press; 141 | if (flags & 0x20) 142 | input->B = inputDevice[5].press; 143 | if (flags & 0x40) 144 | input->C = inputDevice[6].press; 145 | if (flags & 0x80) 146 | input->start = inputDevice[7].press; 147 | if (flags & 0x80) 148 | anyPress = inputDevice[8].press; 149 | } 150 | 151 | void CheckKeyDown(InputData *input, byte flags) 152 | { 153 | if (flags & 0x1) 154 | input->up = inputDevice[0].hold; 155 | if (flags & 0x2) 156 | input->down = inputDevice[1].hold; 157 | if (flags & 0x4) 158 | input->left = inputDevice[2].hold; 159 | if (flags & 0x8) 160 | input->right = inputDevice[3].hold; 161 | if (flags & 0x10) 162 | input->A = inputDevice[4].hold; 163 | if (flags & 0x20) 164 | input->B = inputDevice[5].hold; 165 | if (flags & 0x40) 166 | input->C = inputDevice[6].hold; 167 | if (flags & 0x80) 168 | input->start = inputDevice[7].hold; 169 | // if (flags & 0x80) 170 | // anyHold = inputDevice[8].hold; 171 | } -------------------------------------------------------------------------------- /Sonic12Decomp/Input.hpp: -------------------------------------------------------------------------------- 1 | #ifndef INPUT_H 2 | #define INPUT_H 3 | 4 | struct InputData { 5 | bool up; 6 | bool down; 7 | bool left; 8 | bool right; 9 | bool A; 10 | bool B; 11 | bool C; 12 | bool start; 13 | }; 14 | 15 | struct InputButton { 16 | bool press, hold; 17 | int keyMappings, contMappings; 18 | 19 | inline void setHeld() 20 | { 21 | press = !hold; 22 | hold = true; 23 | } 24 | inline void setReleased() 25 | { 26 | press = false; 27 | hold = false; 28 | } 29 | 30 | inline bool down() { return (press || hold); } 31 | }; 32 | 33 | extern InputData keyPress; 34 | extern InputData keyDown; 35 | 36 | extern bool anyPress; 37 | 38 | extern int touchDown[8]; 39 | extern int touchX[8]; 40 | extern int touchY[8]; 41 | extern int touchID[8]; 42 | extern int touches; 43 | 44 | extern InputButton inputDevice[9]; 45 | extern int inputType; 46 | 47 | extern int LSTICK_DEADZONE; 48 | extern int RSTICK_DEADZONE; 49 | extern int LTRIGGER_DEADZONE; 50 | extern int RTRIGGER_DEADZONE; 51 | 52 | #if RETRO_USING_SDL 53 | extern SDL_GameController *controller; 54 | 55 | inline void controllerInit(byte controllerID) 56 | { 57 | inputType = 1; 58 | controller = SDL_GameControllerOpen(controllerID); 59 | }; 60 | 61 | inline void controllerClose(byte controllerID) 62 | { 63 | if (controllerID >= 2) 64 | return; 65 | inputType = 0; 66 | } 67 | #endif 68 | 69 | void ProcessInput(); 70 | 71 | void CheckKeyPress(InputData *input, byte Flags); 72 | void CheckKeyDown(InputData *input, byte Flags); 73 | 74 | #endif // !INPUT_H 75 | -------------------------------------------------------------------------------- /Sonic12Decomp/Math.cpp: -------------------------------------------------------------------------------- 1 | #include "RetroEngine.hpp" 2 | #include 3 | 4 | #ifndef M_PI 5 | #define M_PI 3.14159265358979323846264338327950288 6 | #endif 7 | 8 | int sinValM7[512]; 9 | int cosValM7[512]; 10 | 11 | int sinVal512[512]; 12 | int cosVal512[512]; 13 | 14 | int sinVal256[256]; 15 | int cosVal256[256]; 16 | 17 | byte atanVal256[0x100 * 0x100]; 18 | 19 | void CalculateTrigAngles() 20 | { 21 | for (int i = 0; i < 0x200; ++i) { 22 | float val = sin(((float)i / 256.0) * M_PI); 23 | sinValM7[i] = (val * 4096.0); 24 | val = cos(((float)i / 256.0) * M_PI); 25 | cosValM7[i] = (val * 4096.0); 26 | } 27 | 28 | cosValM7[0] = 4096; 29 | cosValM7[128] = 0; 30 | cosValM7[256] = -4096; 31 | cosValM7[384] = 0; 32 | sinValM7[0] = 0; 33 | sinValM7[128] = 4096; 34 | sinValM7[256] = 0; 35 | sinValM7[384] = -4096; 36 | 37 | for (int i = 0; i < 0x200; ++i) { 38 | float val = sinf(((float)i / 256) * M_PI); 39 | sinVal512[i] = (signed int)(val * 512.0); 40 | val = cosf(((float)i / 256) * M_PI); 41 | cosVal512[i] = (signed int)(val * 512.0); 42 | } 43 | 44 | cosVal512[0] = 0x200; 45 | cosVal512[128] = 0; 46 | cosVal512[256] = -0x200; 47 | cosVal512[384] = 0; 48 | sinVal512[0] = 0; 49 | sinVal512[128] = 0x200; 50 | sinVal512[256] = 0; 51 | sinVal512[384] = -0x200; 52 | 53 | for (int i = 0; i < 0x100; i++) { 54 | sinVal256[i] = (sinVal512[i * 2] >> 1); 55 | cosVal256[i] = (cosVal512[i * 2] >> 1); 56 | } 57 | 58 | for (int Y = 0; Y < 0x100; ++Y) { 59 | byte *ATan = (byte *)&atanVal256[Y]; 60 | for (int X = 0; X < 0x100; ++X) { 61 | float angle = atan2f(Y, X); 62 | *ATan = (signed int)(angle * 40.743664f); 63 | ATan += 0x100; 64 | } 65 | } 66 | } 67 | 68 | byte ArcTanLookup(int X, int Y) 69 | { 70 | int x = 0; 71 | int y = 0; 72 | 73 | x = abs(X); 74 | y = abs(Y); 75 | 76 | if (x <= y) { 77 | while (y > 0xFF) { 78 | x >>= 4; 79 | y >>= 4; 80 | } 81 | } 82 | else { 83 | while (x > 0xFF) { 84 | x >>= 4; 85 | y >>= 4; 86 | } 87 | } 88 | if (X <= 0) { 89 | if (Y <= 0) 90 | return atanVal256[(x << 8) + y] + -0x80; 91 | else 92 | return -0x80 - atanVal256[(x << 8) + y]; 93 | } 94 | else if (Y <= 0) 95 | return -atanVal256[(x << 8) + y]; 96 | else 97 | return atanVal256[(x << 8) + y]; 98 | } 99 | 100 | int64_t pow(int base, int exponent) { 101 | if (!exponent) return 1; 102 | int64_t result = base; 103 | for (int i = 1; i < exponent; i++) result *= base; 104 | return result; 105 | } 106 | -------------------------------------------------------------------------------- /Sonic12Decomp/Math.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MATH_H 2 | #define MATH_H 3 | 4 | //#define M_PI (3.1415927) 5 | 6 | #define MEM_ZERO(x) memset(&(x), 0, sizeof((x))) 7 | #define MEM_ZEROP(x) memset((x), 0, sizeof(*(x))) 8 | 9 | extern int sinValM7[0x200]; 10 | extern int cosValM7[0x200]; 11 | 12 | extern int sinVal512[0x200]; 13 | extern int cosVal512[0x200]; 14 | 15 | extern int sinVal256[0x100]; 16 | extern int cosVal256[0x100]; 17 | 18 | extern byte atanVal256[0x100 * 0x100]; 19 | 20 | // Setup Angles 21 | void CalculateTrigAngles(); 22 | 23 | inline int sin512(int angle) { 24 | if (angle < 0) 25 | angle = 0x200 - angle; 26 | angle &= 0x1FF; 27 | return sinVal512[angle]; 28 | } 29 | 30 | inline int cos512(int angle) 31 | { 32 | if (angle < 0) 33 | angle = 0x200 - angle; 34 | angle &= 0x1FF; 35 | return cosVal512[angle]; 36 | } 37 | 38 | inline int sin256(int angle) 39 | { 40 | if (angle < 0) 41 | angle = 0x100 - angle; 42 | angle &= 0xFF; 43 | return sinVal256[angle]; 44 | } 45 | 46 | inline int cos256(int angle) 47 | { 48 | if (angle < 0) 49 | angle = 0x100 - angle; 50 | angle &= 0xFF; 51 | return cosVal256[angle]; 52 | } 53 | 54 | // Get Arc Tan value 55 | byte ArcTanLookup(int X, int Y); 56 | 57 | int64_t pow(int base, int exponent); 58 | 59 | #endif // !MATH_H 60 | -------------------------------------------------------------------------------- /Sonic12Decomp/Network.cpp: -------------------------------------------------------------------------------- 1 | //here lies Network.cpp 2 | //check RetroEngine.cpp:215ish for more info (RetroEngine::Init) 3 | 4 | #if RETRO_USE_NETWORKING 5 | #include "RetroEngine.hpp" 6 | 7 | #if RETRO_PLATFORM == RETRO_WIN && _MSC_VER 8 | #include //gives it a macro it wants, will make a note otherwise 9 | #endif 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | inline uint64_t GETBIT(uint64_t i, int n) { return (i & (1LL << n)) >> n; }; 17 | 18 | using namespace asio; 19 | using ip::tcp; 20 | using ip::udp; 21 | using std::string; 22 | using std::cout; 23 | using std::endl; 24 | 25 | const char *pattern = "AABAABC"; 26 | #define PLEN (7) 27 | 28 | char index[36]; 29 | void buildNetworkIndex() { 30 | for (byte i = 0; i < 26; ++i) index[i] = i + 'A'; 31 | for (byte i = 0; i < 10; ++i) index[i + 26] = i + '0'; 32 | } 33 | 34 | udp::socket* udpSocket = nullptr; 35 | udp::endpoint otherEndpoint; 36 | CodeData datas[2]; //0 is my end 37 | std::thread runner; 38 | 39 | string connectedIP; 40 | 41 | string generateCode(ushort port, int gameLength, int itemMode) 42 | { 43 | uint64_t code = 0; 44 | uint ip; 45 | if (getIP(&ip)) 46 | return string(); 47 | datas[0].ip = ip; 48 | datas[0].port = port; 49 | datas[0].gameLength = gameLength; 50 | datas[0].itemMode = itemMode; 51 | datas[0].player = playerListPos; 52 | short gamecode = gameLength | (itemMode << 4) | (playerListPos << 6); 53 | int ipc = 0, pc = 0, gc = 0; 54 | for (uint64_t i = 0; i < PLEN * 8; i++) { 55 | std::cout << code << ' ' << i << std::endl; 56 | char c = pattern[i % PLEN]; 57 | switch (c) { 58 | case 'A': code |= GETBIT(ip, ipc++) << i; break; 59 | case 'B': code |= GETBIT(port, pc++) << i; break; 60 | case 'C': code |= GETBIT(gamecode, gc++) << i; break; 61 | } 62 | } 63 | int i = 0; 64 | while (code >= (uint64_t)pow(36, i + 1)) i++; 65 | std::ostringstream out; 66 | while (code) { 67 | out << index[code / (uint64_t)pow(36, i)]; 68 | code %= pow(36, i--); 69 | } 70 | datas[0].codeString = out.str().c_str(); 71 | return out.str(); 72 | } 73 | 74 | CodeData parseCode(const string code) 75 | { 76 | uint64_t out = 0; 77 | int i = 0; 78 | for (auto rit = code.rbegin(); rit != code.rend(); ++rit) { 79 | char c = *rit, mult; 80 | if (c <= '9') 81 | mult = c - '0' + 26; 82 | else mult = c - 'A'; 83 | out += mult * (uint64_t)pow(36, i++); 84 | } 85 | int bitlen = floor(log2(out) + 1); 86 | CodeData ret; 87 | MEM_ZERO(ret); 88 | short gamecode = 0; 89 | int ipc = 0, pc = 0, gc = 0; 90 | for (int i = 0; i < bitlen; ++i) { 91 | char c = pattern[i % PLEN]; 92 | auto bit = GETBIT(out, i); 93 | switch (c) { 94 | case 'A': ret.ip |= bit << ipc++; break; 95 | case 'B': ret.port |= bit << pc++; break; 96 | case 'C': gamecode |= bit << gc++; break; 97 | } 98 | } 99 | ret.gameLength = gamecode & 0b1111; 100 | ret.itemMode = (gamecode >> 4) & 0b11; 101 | ret.player = (gamecode >> 6) & 0b11; 102 | //ret.ip = 127 | 1 << 24; 103 | std::ostringstream ip; 104 | ip << (ret.ip & 0xFF) << '.' 105 | << ((ret.ip >> 8) & 0xFF) << '.' 106 | << ((ret.ip >> 16) & 0xFF) << '.' 107 | << ((ret.ip >> 24) & 0xFF); 108 | ret.ipString = ip.str(); 109 | ret.codeString = code.c_str(); //make a copy i guess 110 | return ret; 111 | } 112 | 113 | int getIP(uint *ip) 114 | { 115 | string ips; 116 | try { 117 | io_service io_service; 118 | tcp::resolver resolver(io_service); 119 | tcp::resolver::query query("api.ipify.org", "http"); 120 | tcp::resolver::iterator endpoint_iterator = resolver.resolve(query), end; 121 | tcp::socket socket(io_service); 122 | 123 | error_code ec = error::not_connected; 124 | while (ec && endpoint_iterator != end) { 125 | socket.close(); 126 | socket.connect(*endpoint_iterator++, ec); 127 | } 128 | if (ec) 129 | return ec.value(); 130 | // form the HTTP request 131 | streambuf request; 132 | std::ostream request_stream(&request); 133 | request_stream << "GET / HTTP/1.0\r\n"; 134 | request_stream << "Host: api.ipify.org\r\n\r\n"; 135 | // send it 136 | write(socket, request, ec); 137 | if (ec) 138 | return ec.value(); 139 | // read the status code and HTTP code 140 | streambuf response; 141 | read_until(socket, response, "\r\n", ec); 142 | if (ec) 143 | return ec.value(); 144 | // is our response valid? 145 | std::istream response_stream(&response); 146 | string http_version; 147 | response_stream >> http_version; 148 | unsigned int status_code; 149 | response_stream >> status_code; 150 | string status_message; 151 | std::getline(response_stream, status_message); 152 | if (!response_stream || http_version.substr(0, 5) != "HTTP/") 153 | return 1; // invalid response 154 | if (status_code != 200) 155 | return status_code; // not OK 156 | // read the header 157 | std::string header; 158 | while (std::getline(response_stream, header) && header != "\r") { 159 | } 160 | // finally, read the IP address 161 | std::ostringstream output; 162 | if (response.size() > 0) 163 | output << &response; 164 | while (read(socket, response, transfer_at_least(1), ec)) output << &response; 165 | // output is now our ip address. 166 | // we can now exit the clause, but make sure we know our IP! 167 | ips = output.str(); 168 | } catch (std::exception &e) { 169 | return 1; // any exceptions we just retun 1 170 | } 171 | std::istringstream ipstream(ips); 172 | char buf; 173 | *ip = 0; 174 | // fill the bytes in properly. 175 | for (int i = 0; i < 4; i++) { 176 | int read; 177 | ipstream >> read >> buf; 178 | *ip |= (read << (i * 8)); 179 | } 180 | return 0; 181 | } 182 | 183 | void runServer(ushort); 184 | void runClient(CodeData); 185 | 186 | void initServer(ushort port) { 187 | runner = std::thread(runServer, port); 188 | } 189 | void initClient(CodeData data) { 190 | runner = std::thread(runClient, data); 191 | } 192 | 193 | void stopNetworking() { 194 | error_code ignored; 195 | udpSocket->close(ignored); //this should error the running loop 196 | runner.detach(); 197 | } 198 | 199 | void runServer(ushort port) { 200 | io_context io_context; 201 | MEM_ZERO(datas[1]); 202 | udpSocket = new udp::socket(io_context, udp::endpoint(udp::v4(), port)); 203 | while (udpSocket->is_open()) { 204 | udp::endpoint remote_endpoint; 205 | error_code ignored; 206 | char recv_buf[0x1000]; 207 | MEM_ZEROP(recv_buf); 208 | cout << "waiting for bytes" << endl; 209 | try { 210 | udpSocket->receive_from(buffer(recv_buf), remote_endpoint, 0); 211 | } 212 | catch (std::exception &e) { 213 | recv_buf[0] = 0x82; //error on the socket end, terminate immediately 214 | } 215 | cout << "recieved " << +recv_buf[0] << " from " << remote_endpoint.address().to_v4().to_string() << ":" << remote_endpoint.port() << endl; 216 | if (datas[1].ip && datas[1].ipString.compare(remote_endpoint.address().to_v4().to_string())) { 217 | sendData(0x80, 0, nullptr, &remote_endpoint); //you're not who we're looking for 218 | continue; 219 | } 220 | sbyte flag = recv_buf[0] & 0x7F, error = recv_buf[0] & 0x80; 221 | ushort datalen = *(short*)(recv_buf + 1); 222 | if (error) { 223 | udpSocket->close(ignored); 224 | receive2PVSMatchCode(1); 225 | break; //if, at ANY point, it no longer succeeds, break the whole loop 226 | } 227 | switch (flag) { 228 | default: continue; 229 | case 0x00: { //game code, this aint our business at this point 230 | receive2PVSData((MultiplayerData*)(recv_buf + 3)); 231 | break; 232 | } 233 | case 0x01: { //first response, "introduce ourselves", make sure the code's correct 234 | string code((char*)(recv_buf + 3), datalen); 235 | if (code.compare(datas[0].codeString)) { 236 | sendData(0x81, 0, nullptr, &remote_endpoint); //incorrect code 237 | continue; 238 | } 239 | otherEndpoint = remote_endpoint; //friend!!!!! 240 | sendData(0x41, 0, nullptr); 241 | break; 242 | } 243 | case 0x02: { //after first client response, this has the code. after this one server starts 244 | datas[1] = parseCode(string((char*)(recv_buf + 3), datalen)); 245 | receive2PVSMatchCode((datas[1].gameLength << 4) + (datas[1].itemMode << 8) + (datas[1].player << 12)); 246 | break; 247 | } 248 | } 249 | } 250 | } 251 | void runClient(CodeData data) { 252 | io_context io_context; 253 | datas[1] = data; 254 | otherEndpoint = udp::endpoint(ip::address_v4(data.ip), data.port); 255 | udpSocket = new udp::socket(io_context, otherEndpoint); 256 | udpSocket->connect(otherEndpoint); 257 | sendData(0x01, datas[1].codeString.length(), (void*)datas[1].codeString.c_str()); 258 | while (udpSocket->is_open()) { 259 | udp::endpoint remote_endpoint; 260 | error_code ignored; 261 | char recv_buf[0x1000]; 262 | MEM_ZEROP(recv_buf); 263 | cout << "waiting for bytes" << endl; 264 | udpSocket->receive_from(buffer(recv_buf), remote_endpoint, 0, ignored); 265 | cout << "recieved " << +recv_buf[0] << " from " << remote_endpoint.address().to_v4().to_string() << ":" << remote_endpoint.port() << endl; 266 | if (datas[1].port != remote_endpoint.port() || datas[1].ipString.compare(remote_endpoint.address().to_v4().to_string())) { 267 | sendData(0x80, 0, nullptr, &remote_endpoint); //you're not who we're looking for 268 | continue; 269 | } 270 | sbyte flag = recv_buf[0] & 0x7F, error = recv_buf[0] & 0x80; 271 | ushort datalen = *(short*)(recv_buf + 1); 272 | if (error) { 273 | udpSocket->close(ignored); 274 | receive2PVSMatchCode(1); 275 | break; //if, at ANY point, it no longer succeeds, break the whole loop 276 | } 277 | switch (flag) { 278 | case 0x00: { //game code, this aint our business at this point 279 | receive2PVSData((MultiplayerData*)(recv_buf + 3)); 280 | break; 281 | } 282 | case 0x41: { //after first response from the server, "send me your code." we start after waiting a little bit 283 | sendData(0x02, datas[0].codeString.length(), (void*)datas[0].codeString.c_str()); 284 | std::this_thread::sleep_for(std::chrono::milliseconds(500)); 285 | receive2PVSMatchCode((datas[1].gameLength << 4) + (datas[1].itemMode << 8) + (datas[1].player << 12)); 286 | break; 287 | } 288 | } 289 | } 290 | } 291 | 292 | int sendData(byte flag, ushort datalen, void* data, void *endpoint) { 293 | if (!udpSocket || !udpSocket->is_open()) return 1; //if socket doesn't exist 294 | if (!endpoint) endpoint = &otherEndpoint; //set the default endpoint 295 | error_code ec; 296 | char sendbuf[0x1000]; 297 | MEM_ZEROP(sendbuf); 298 | *sendbuf = flag; 299 | *(sendbuf + 1) = datalen; 300 | memcpy(sendbuf + 3, data, datalen); //fill our sendbuffer 301 | udpSocket->send_to(buffer(sendbuf), *(udp::endpoint*)endpoint, 0, ec); //send it over 302 | if (ec) return ec.value(); 303 | cout << "sent " << +flag << " to " << (*(udp::endpoint*)endpoint).address().to_v4().to_string() << endl; 304 | return 0; 305 | } 306 | #endif -------------------------------------------------------------------------------- /Sonic12Decomp/Network.hpp: -------------------------------------------------------------------------------- 1 | #ifndef NETWORK_H 2 | #define NETWORK_H 3 | #include 4 | 5 | struct CodeData { 6 | uint ip; 7 | ushort port; 8 | int gameLength; 9 | int itemMode; 10 | int player; 11 | std::string ipString; 12 | std::string codeString; 13 | }; 14 | void buildNetworkIndex(); 15 | 16 | /* generates the code based on IP, port, gameLength, itemMode, and playerListPos. 17 | the other user inputs this code to connect */ 18 | std::string generateCode(ushort port, int gameLength, int itemMode); 19 | 20 | /* parses the code given and returns a CodeData. 21 | we can use this to get the IP and port and set our other variables later on */ 22 | CodeData parseCode(const std::string code); 23 | 24 | /* returns 1 or more on error 25 | this also ensures the user is connected to the internet */ 26 | int getIP(uint* ip); 27 | 28 | void initServer(ushort port); 29 | void initClient(CodeData data); 30 | int sendData(byte flag, ushort datalen, void* data, void* endpoint = nullptr); 31 | 32 | #endif -------------------------------------------------------------------------------- /Sonic12Decomp/Object.hpp: -------------------------------------------------------------------------------- 1 | #ifndef OBJECT_H 2 | #define OBJECT_H 3 | 4 | #define NATIVEENTITY_COUNT (0x100) 5 | 6 | #define ENTITY_COUNT (0x4A0) 7 | #define TEMPENTITY_START (ENTITY_COUNT - 0x80) 8 | #define OBJECT_COUNT (0x100) 9 | #define TYPEGROUP_COUNT (0x103) 10 | 11 | struct TypeGroupList { 12 | int entityRefs[ENTITY_COUNT]; 13 | int listSize; 14 | }; 15 | 16 | struct Entity { 17 | int XPos; 18 | int YPos; 19 | int XVelocity; 20 | int YVelocity; 21 | int speed; 22 | int values[48]; 23 | int state; 24 | int angle; 25 | int scale; 26 | int rotation; 27 | int alpha; 28 | int animationTimer; 29 | int animationSpeed; 30 | int camOffsetX; 31 | int lookPos; 32 | ushort typeGroup; 33 | byte type; 34 | byte propertyValue; 35 | byte priority; 36 | sbyte drawOrder; 37 | byte direction; 38 | byte inkEffect; 39 | byte animation; 40 | byte prevAnimation; 41 | byte frame; 42 | byte collisionMode; 43 | byte collisionPlane; 44 | sbyte controlMode; 45 | byte controlLock; 46 | byte pushing; 47 | byte visible; 48 | byte tileCollisions; 49 | byte objectInteractions; 50 | byte gravity; 51 | byte left; 52 | byte right; 53 | byte up; 54 | byte down; 55 | byte jumpPress; 56 | byte jumpHold; 57 | byte trackScroll; 58 | byte flailing[5]; 59 | }; 60 | 61 | struct NativeEntityBase { 62 | void (*createPtr)(void *objPtr); 63 | void (*mainPtr)(void *objPtr); 64 | int slotID; 65 | int objectID; 66 | }; 67 | 68 | struct NativeEntity { 69 | void (*createPtr)(void *objPtr); 70 | void (*mainPtr)(void *objPtr); 71 | int slotID; 72 | int objectID; 73 | byte extra[0x400]; 74 | }; 75 | 76 | enum ObjectTypes { 77 | OBJ_TYPE_BLANKOBJECT = 0 //0 is always blank obj 78 | }; 79 | 80 | enum ObjectPriority { 81 | PRIORITY_ACTIVE_BOUNDS, 82 | PRIORITY_ACTIVE, 83 | PRIORITY_ACTIVE_PAUSED, 84 | PRIORITY_ACTIVE_XBOUNDS, 85 | PRIORITY_ACTIVE_XBOUNDS_REMOVE, 86 | PRIORITY_INACTIVE, 87 | PRIORITY_ACTIVE_BOUNDS_SMALL, 88 | PRIORITY_ACTIVE2 89 | }; 90 | 91 | //Native Objects 92 | extern int nativeEntityPos; 93 | 94 | extern int activeEntityList[NATIVEENTITY_COUNT]; 95 | extern int objectRemoveFlag[NATIVEENTITY_COUNT]; 96 | extern NativeEntity objectEntityBank[NATIVEENTITY_COUNT]; 97 | extern int nativeEntityCount; 98 | 99 | extern int nativeEntityCountBackup; 100 | extern int backupEntityList[NATIVEENTITY_COUNT]; 101 | extern NativeEntity objectEntityBackup[NATIVEENTITY_COUNT]; 102 | 103 | extern int nativeEntityCountBackupS; 104 | extern int backupEntityListS[NATIVEENTITY_COUNT]; 105 | extern NativeEntity objectEntityBackupS[NATIVEENTITY_COUNT]; 106 | 107 | //Game Objects 108 | extern int objectEntityPos; 109 | extern int curObjectType; 110 | extern Entity objectEntityList[ENTITY_COUNT]; 111 | extern int processObjectFlag[ENTITY_COUNT]; 112 | extern TypeGroupList objectTypeGroupList[TYPEGROUP_COUNT]; 113 | 114 | extern char typeNames[OBJECT_COUNT][0x40]; 115 | 116 | extern int OBJECT_BORDER_X1; 117 | extern int OBJECT_BORDER_X2; 118 | extern int OBJECT_BORDER_X3; 119 | extern int OBJECT_BORDER_X4; 120 | extern const int OBJECT_BORDER_Y1; 121 | extern const int OBJECT_BORDER_Y2; 122 | extern const int OBJECT_BORDER_Y3; 123 | extern const int OBJECT_BORDER_Y4; 124 | 125 | void ProcessStartupObjects(); 126 | void ProcessObjects(); 127 | void ProcessPausedObjects(); 128 | void ProcessFrozenObjects(); 129 | void Process2PObjects(); 130 | 131 | void SetObjectTypeName(const char *objectName, int objectID); 132 | 133 | extern int playerListPos; 134 | 135 | void ProcessPlayerControl(Entity *player); 136 | 137 | void InitNativeObjectSystem(); 138 | NativeEntity *CreateNativeObject(void (*objCreate)(void *objPtr), void (*objMain)(void *objPtr)); 139 | void RemoveNativeObject(NativeEntityBase *NativeEntry); 140 | void ProcessNativeObjects(); 141 | inline void BackupNativeObjects() { 142 | memcpy(backupEntityList, activeEntityList, sizeof(int) * NATIVEENTITY_COUNT); 143 | memcpy(objectEntityBackup, objectEntityBank, sizeof(NativeEntity) * NATIVEENTITY_COUNT); 144 | nativeEntityCountBackup = nativeEntityCount; 145 | } 146 | inline void BackupNativeObjectsSettings() { 147 | memcpy(backupEntityListS, activeEntityList, sizeof(int) * NATIVEENTITY_COUNT); 148 | memcpy(objectEntityBackupS, objectEntityBank, sizeof(NativeEntity) * NATIVEENTITY_COUNT); 149 | nativeEntityCountBackupS = nativeEntityCount; 150 | } 151 | inline void RestoreNativeObjects() 152 | { 153 | memcpy(activeEntityList, backupEntityList, sizeof(int) * NATIVEENTITY_COUNT); 154 | nativeEntityCount = nativeEntityCountBackup; 155 | memcpy(objectEntityBank, objectEntityBackup, sizeof(NativeEntity) * NATIVEENTITY_COUNT); 156 | 157 | //ptr = CreateNativeObject(FadeScreen_Create, FadeScreen_Main); 158 | //ptr + 16 = 0; 159 | } 160 | inline void RestoreNativeObjectsNoFade() 161 | { 162 | memcpy(activeEntityList, backupEntityList, sizeof(int) * NATIVEENTITY_COUNT); 163 | nativeEntityCount = nativeEntityCountBackup; 164 | memcpy(objectEntityBank, objectEntityBackup, sizeof(NativeEntity) * NATIVEENTITY_COUNT); 165 | } 166 | inline void RestoreNativeObjectsSettings() 167 | { 168 | memcpy(activeEntityList, backupEntityListS, sizeof(int) * NATIVEENTITY_COUNT); 169 | nativeEntityCount = nativeEntityCountBackupS; 170 | memcpy(objectEntityBank, objectEntityBackupS, sizeof(NativeEntity) * NATIVEENTITY_COUNT); 171 | } 172 | inline void GetNativeObject(NativeEntity *obj, void (*newCreate)(void *objPtr), void (*newMain)(void *objPtr)) 173 | { 174 | int slotID = obj->slotID; 175 | int objID = obj->objectID; 176 | memset(&objectEntityBank[slotID], 0, sizeof(NativeEntity)); 177 | obj->slotID = slotID; 178 | obj->mainPtr = newMain; 179 | obj->createPtr = newCreate; 180 | obj->objectID = objID; 181 | if (obj->createPtr) 182 | obj->createPtr(obj); 183 | } 184 | inline NativeEntity *GetNativeObject(uint objID) 185 | { 186 | if (objID > 0xFF) 187 | return nullptr; 188 | else 189 | return &objectEntityBank[objID]; 190 | } 191 | inline void ClearNativeObjects() { 192 | nativeEntityCount = 0; 193 | memset(objectEntityBank, 0, sizeof(NativeEntity) * NATIVEENTITY_COUNT); 194 | } 195 | 196 | #endif // !OBJECT_H 197 | -------------------------------------------------------------------------------- /Sonic12Decomp/Palette.cpp: -------------------------------------------------------------------------------- 1 | #include "RetroEngine.hpp" 2 | 3 | // Palettes (as RGB888 Colours) 4 | PaletteEntry fullPalette32[PALETTE_COUNT][PALETTE_SIZE]; 5 | PaletteEntry *activePalette32 = fullPalette32[0]; 6 | 7 | // Palettes (as RGB565 Colours) 8 | ushort fullPalette[PALETTE_COUNT][PALETTE_SIZE]; 9 | ushort *activePalette = fullPalette[0]; // Ptr to the 256 colour set thats active 10 | 11 | byte gfxLineBuffer[SCREEN_YSIZE]; // Pointers to active palette 12 | 13 | int fadeMode = 0; 14 | byte fadeA = 0; 15 | byte fadeR = 0; 16 | byte fadeG = 0; 17 | byte fadeB = 0; 18 | 19 | void LoadPalette(const char *filePath, int paletteID, int startPaletteIndex, int startIndex, int endIndex) 20 | { 21 | FileInfo info; 22 | char fullPath[0x80]; 23 | 24 | StrCopy(fullPath, "Data/Palettes/"); 25 | StrAdd(fullPath, filePath); 26 | 27 | if (LoadFile(fullPath, &info)) { 28 | SetFilePosition(3 * startIndex); 29 | if (paletteID >= PALETTE_COUNT || paletteID < 0) 30 | paletteID = 0; 31 | 32 | byte colour[3]; 33 | if (paletteID) { 34 | for (int i = startIndex; i < endIndex; ++i) { 35 | FileRead(&colour, 3); 36 | SetPaletteEntry(paletteID, startPaletteIndex++, colour[0], colour[1], colour[2]); 37 | } 38 | } 39 | else { 40 | for (int i = startIndex; i < endIndex; ++i) { 41 | FileRead(&colour, 3); 42 | SetPaletteEntry(-1, startPaletteIndex++, colour[0], colour[1], colour[2]); 43 | } 44 | } 45 | CloseFile(); 46 | } 47 | } 48 | 49 | void SetPaletteFade(byte destPaletteID, byte srcPaletteA, byte srcPaletteB, ushort blendAmount, int startIndex, 50 | int endIndex) 51 | { 52 | if (destPaletteID >= PALETTE_COUNT || srcPaletteA >= PALETTE_COUNT || srcPaletteB >= PALETTE_COUNT) 53 | return; 54 | 55 | if (blendAmount > 0xFF) { 56 | blendAmount = 0xFF; 57 | } 58 | else { 59 | blendAmount = 0; 60 | } 61 | if (endIndex > 0x100) 62 | endIndex = 0x100; 63 | 64 | if (startIndex < endIndex) { 65 | uint blendA = 0xFF - blendAmount; 66 | ushort *dst = &fullPalette[destPaletteID][startIndex]; 67 | ushort *srcA = &fullPalette[srcPaletteA][startIndex]; 68 | ushort *srcB = &fullPalette[srcPaletteB][startIndex]; 69 | 70 | for (int l = startIndex; l < endIndex; ++l) { 71 | *dst = RGB888_TO_RGB565((byte)((ushort)(fullPalette32[srcPaletteB][l].r * blendAmount + blendA * fullPalette32[srcPaletteA][l].r) >> 8), 72 | (byte)((ushort)(fullPalette32[srcPaletteB][l].g * blendAmount + blendA * fullPalette32[srcPaletteA][l].g) >> 8), 73 | (byte)((ushort)(fullPalette32[srcPaletteB][l].b * blendAmount + blendA * fullPalette32[srcPaletteA][l].b) >> 8)); 74 | ++srcA; 75 | ++srcB; 76 | ++dst; 77 | } 78 | 79 | 80 | } 81 | } -------------------------------------------------------------------------------- /Sonic12Decomp/Palette.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PALETTE_H 2 | #define PALETTE_H 3 | 4 | #define PALETTE_COUNT (0x8) 5 | #define PALETTE_SIZE (0x100) 6 | 7 | struct PaletteEntry { 8 | byte r; 9 | byte g; 10 | byte b; 11 | }; 12 | 13 | // Palettes (as RGB565 Colours) 14 | extern PaletteEntry fullPalette32[PALETTE_COUNT][PALETTE_SIZE]; 15 | extern ushort fullPalette[PALETTE_COUNT][PALETTE_SIZE]; 16 | extern ushort *activePalette; // Ptr to the 256 colour set thats active 17 | extern PaletteEntry *activePalette32; 18 | 19 | extern byte gfxLineBuffer[SCREEN_YSIZE]; // Pointers to active palette 20 | 21 | extern int fadeMode; 22 | extern byte fadeA; 23 | extern byte fadeR; 24 | extern byte fadeG; 25 | extern byte fadeB; 26 | 27 | extern int paletteMode; 28 | 29 | #define RGB888_TO_RGB5551(r, g, b) (2 * ((b) >> 3) | ((g) >> 3 << 6) | ((r) >> 3 << 11) | 1) // used in mobile vers 30 | #define RGB888_TO_RGB565(r, g, b) ((b) >> 3) | (((g) >> 2) << 5) | (((r) >> 3) << 11) // used in pc vers 31 | 32 | void LoadPalette(const char *filePath, int paletteID, int startPaletteIndex, int startIndex, int endIndex); 33 | 34 | inline void SetActivePalette(byte newActivePal, int startLine, int endLine) 35 | { 36 | if (newActivePal < PALETTE_COUNT) 37 | while (startLine++ < endLine) gfxLineBuffer[startLine % SCREEN_YSIZE] = newActivePal; 38 | 39 | activePalette = fullPalette[gfxLineBuffer[0]]; 40 | activePalette32 = fullPalette32[gfxLineBuffer[0]]; 41 | } 42 | 43 | inline void SetPaletteEntry(byte paletteIndex, byte index, byte r, byte g, byte b) 44 | { 45 | if (paletteIndex != 0xFF) { 46 | fullPalette[paletteIndex][index] = RGB888_TO_RGB565(r, g, b); 47 | fullPalette32[paletteIndex][index].r = r; 48 | fullPalette32[paletteIndex][index].g = g; 49 | fullPalette32[paletteIndex][index].b = b; 50 | } 51 | else { 52 | activePalette[index] = RGB888_TO_RGB565(r, g, b); 53 | activePalette32[index].r = r; 54 | activePalette32[index].g = g; 55 | activePalette32[index].b = b; 56 | } 57 | } 58 | 59 | inline void SetPaletteEntryPacked(byte paletteIndex, byte index, uint colour) 60 | { 61 | fullPalette[paletteIndex][index] = RGB888_TO_RGB565((byte)(colour >> 16), (byte)(colour >> 8), (byte)(colour >> 0)); 62 | fullPalette32[paletteIndex][index].r = (byte)(colour >> 16); 63 | fullPalette32[paletteIndex][index].g = (byte)(colour >> 8); 64 | fullPalette32[paletteIndex][index].b = (byte)(colour >> 0); 65 | } 66 | 67 | inline uint GetPaletteEntryPacked(byte paletteIndex, byte index) 68 | { 69 | PaletteEntry clr = fullPalette32[paletteIndex][index]; 70 | return (clr.r << 16) | (clr.g << 8) | (clr.b); 71 | } 72 | 73 | inline void CopyPalette(byte sourcePalette, byte srcPaletteStart, byte destinationPalette, byte destPaletteStart, byte count) 74 | { 75 | if (sourcePalette < PALETTE_COUNT && destinationPalette < PALETTE_COUNT) { 76 | for (int i = 0; i < count; ++i) { 77 | fullPalette[destinationPalette][srcPaletteStart + i] = fullPalette[sourcePalette][destPaletteStart + i]; 78 | fullPalette32[destinationPalette][srcPaletteStart + i] = fullPalette32[sourcePalette][destPaletteStart + i]; 79 | } 80 | } 81 | } 82 | 83 | inline void RotatePalette(int palID, byte startIndex, byte endIndex, bool right) 84 | { 85 | if (right) { 86 | ushort startClr = fullPalette[palID][endIndex]; 87 | PaletteEntry startClr32 = fullPalette32[palID][startIndex]; 88 | for (int i = endIndex; i > startIndex; --i) { 89 | fullPalette[palID][i] = fullPalette[palID][i - 1]; 90 | fullPalette32[palID][i] = fullPalette32[palID][i - 1]; 91 | } 92 | fullPalette[palID][startIndex] = startClr; 93 | fullPalette32[palID][endIndex] = startClr32; 94 | } 95 | else { 96 | ushort startClr = fullPalette[palID][startIndex]; 97 | PaletteEntry startClr32 = fullPalette32[palID][startIndex]; 98 | for (int i = startIndex; i < endIndex; ++i) { 99 | fullPalette[palID][i] = fullPalette[palID][i + 1]; 100 | fullPalette32[palID][i] = fullPalette32[palID][i + 1]; 101 | } 102 | fullPalette[palID][endIndex] = startClr; 103 | fullPalette32[palID][endIndex] = startClr32; 104 | } 105 | } 106 | 107 | inline void SetFade(byte R, byte G, byte B, ushort A) 108 | { 109 | fadeMode = 1; 110 | fadeR = R; 111 | fadeG = G; 112 | fadeB = B; 113 | fadeA = A > 0xFF ? 0xFF : A; 114 | } 115 | void SetPaletteFade(byte destPaletteID, byte srcPaletteA, byte srcPaletteB, ushort blendAmount, int startIndex, int endIndex); 116 | 117 | #endif // !PALETTE_H 118 | -------------------------------------------------------------------------------- /Sonic12Decomp/PauseMenu.cpp: -------------------------------------------------------------------------------- 1 | #include "RetroEngine.hpp" 2 | 3 | TextMenu pauseTextMenu; 4 | 5 | void PauseMenu_Create(void *objPtr) 6 | { 7 | NativeEntity_PauseMenu *pauseMenu = (NativeEntity_PauseMenu *)objPtr; 8 | pauseMenu->state = 0; 9 | pauseMenu->timer = 0; 10 | pauseMenu->selectedOption = 0; 11 | pauseMenu->barPos = SCREEN_XSIZE + 64; 12 | pauseMenu->menu = &pauseTextMenu; 13 | MEM_ZEROP(pauseMenu->menu); 14 | 15 | AddTextMenuEntry(pauseMenu->menu, "RESUME"); 16 | AddTextMenuEntry(pauseMenu->menu, ""); 17 | AddTextMenuEntry(pauseMenu->menu, ""); 18 | AddTextMenuEntry(pauseMenu->menu, ""); 19 | AddTextMenuEntry(pauseMenu->menu, "RESTART"); 20 | AddTextMenuEntry(pauseMenu->menu, ""); 21 | AddTextMenuEntry(pauseMenu->menu, ""); 22 | AddTextMenuEntry(pauseMenu->menu, ""); 23 | AddTextMenuEntry(pauseMenu->menu, "EXIT"); 24 | if (Engine.devMenu) { 25 | AddTextMenuEntry(pauseMenu->menu, ""); 26 | AddTextMenuEntry(pauseMenu->menu, ""); 27 | AddTextMenuEntry(pauseMenu->menu, ""); 28 | AddTextMenuEntry(pauseMenu->menu, "DEV MENU"); 29 | } 30 | pauseMenu->menu->alignment = MENU_ALIGN_CENTER; 31 | pauseMenu->menu->selectionCount = Engine.devMenu ? 3 : 2; 32 | pauseMenu->menu->selection1 = 0; 33 | pauseMenu->menu->selection2 = 0; 34 | pauseMenu->lastSurfaceNo = textMenuSurfaceNo; 35 | textMenuSurfaceNo = SURFACE_MAX - 1; 36 | 37 | SetPaletteEntryPacked(7, 0x08, GetPaletteEntryPacked(0, 8)); 38 | SetPaletteEntryPacked(7, 0xFF, 0xFFFFFF); 39 | } 40 | void PauseMenu_Main(void *objPtr) 41 | { 42 | CheckKeyDown(&keyDown, 0xFF); 43 | CheckKeyPress(&keyPress, 0xFF); 44 | 45 | NativeEntity_PauseMenu *pauseMenu = (NativeEntity_PauseMenu *)objPtr; 46 | 47 | switch (pauseMenu->state) { 48 | case 0: 49 | // wait 50 | pauseMenu->barPos -= 16; 51 | if (pauseMenu->barPos + 64 < SCREEN_XSIZE) { 52 | pauseMenu->state++; 53 | } 54 | break; 55 | case 1: 56 | if (keyPress.up) { 57 | if (pauseMenu->selectedOption - 1 < 0) { 58 | if (!Engine.devMenu) 59 | pauseMenu->selectedOption = 3; 60 | else 61 | pauseMenu->selectedOption = 4; 62 | } 63 | --pauseMenu->selectedOption; 64 | PlaySFXByName("MenuMove", 0); 65 | } 66 | else if (keyPress.down) { 67 | if (!Engine.devMenu) 68 | pauseMenu->selectedOption = ++pauseMenu->selectedOption % 3; 69 | else 70 | pauseMenu->selectedOption = ++pauseMenu->selectedOption % 4; 71 | PlaySFXByName("MenuMove", 0); 72 | } 73 | 74 | pauseMenu->menu->selection1 = pauseMenu->selectedOption * 4; 75 | 76 | if (keyPress.A || keyPress.start) { 77 | switch (pauseMenu->selectedOption) { 78 | case 0: { 79 | Engine.gameMode = ENGINE_EXITPAUSE; 80 | pauseMenu->state = 2; 81 | break; 82 | } 83 | case 1: { 84 | pauseMenu->state = 3; 85 | break; 86 | } 87 | case 2: { 88 | pauseMenu->state = 4; 89 | break; 90 | } 91 | case 3: { 92 | pauseMenu->state = 5; 93 | break; 94 | } 95 | } 96 | PlaySFXByName("MenuSelect", 0); 97 | } 98 | else if (keyPress.B) { 99 | Engine.gameMode = ENGINE_EXITPAUSE; 100 | PlaySFXByName("MenuBack", 0); 101 | pauseMenu->state = 6; 102 | } 103 | break; 104 | case 2: 105 | case 6: 106 | pauseMenu->barPos += 16; 107 | if (pauseMenu->barPos > SCREEN_XSIZE + 64) { 108 | textMenuSurfaceNo = pauseMenu->lastSurfaceNo; 109 | RemoveNativeObject(pauseMenu); 110 | return; 111 | } 112 | break; 113 | case 3: 114 | case 4: 115 | case 5: 116 | // wait (again) 117 | pauseMenu->barPos -= 16; 118 | if (pauseMenu->barPos + 64 < 0) { 119 | switch (pauseMenu->state) { 120 | default: break; 121 | case 3: 122 | stageMode = STAGEMODE_LOAD; 123 | Engine.gameMode = ENGINE_MAINGAME; 124 | break; 125 | case 4: initStartMenu(0); break; 126 | case 5: 127 | Engine.gameMode = ENGINE_DEVMENU; 128 | initDevMenu(); 129 | break; 130 | } 131 | textMenuSurfaceNo = pauseMenu->lastSurfaceNo; 132 | RemoveNativeObject(pauseMenu); 133 | return; 134 | } 135 | break; 136 | } 137 | 138 | if (pauseMenu->menu) { 139 | SetActivePalette(7, 0, SCREEN_YSIZE); 140 | 141 | DrawRectangle(pauseMenu->barPos, 0, SCREEN_XSIZE - pauseMenu->barPos, SCREEN_YSIZE, 0, 0, 0, 0xFF); 142 | DrawTextMenu(pauseMenu->menu, pauseMenu->barPos + 0x28, SCREEN_CENTERY - 0x30); 143 | 144 | SetActivePalette(0, 0, SCREEN_YSIZE); 145 | } 146 | } -------------------------------------------------------------------------------- /Sonic12Decomp/PauseMenu.hpp: -------------------------------------------------------------------------------- 1 | #ifndef NATIVE_PAUSEMENU_H 2 | #define NATIVE_PAUSEMENU_H 3 | 4 | struct NativeEntity_PauseMenu : NativeEntityBase { 5 | byte state; 6 | int timer; 7 | int barPos; 8 | byte selectedOption; 9 | TextMenu *menu; 10 | 11 | int lastSurfaceNo; 12 | }; 13 | 14 | void PauseMenu_Create(void *objPtr); 15 | void PauseMenu_Main(void *objPtr); 16 | 17 | #endif // NATIVE_PAUSEMENU_H -------------------------------------------------------------------------------- /Sonic12Decomp/Reader.hpp: -------------------------------------------------------------------------------- 1 | #ifndef READER_H 2 | #define READER_H 3 | 4 | #if RETRO_USING_SDL 5 | #define FileIO SDL_RWops 6 | #define fOpen(path, mode) SDL_RWFromFile(path, mode) 7 | #define fRead(buffer, elementSize, elementCount, file) SDL_RWread(file, buffer, elementSize, elementCount) 8 | #define fSeek(file, offset, whence) SDL_RWseek(file, offset, whence) 9 | #define fTell(file) SDL_RWtell(file) 10 | #define fClose(file) SDL_RWclose(file) 11 | #define fWrite(buffer, elementSize, elementCount, file) SDL_RWwrite(file, buffer, elementSize, elementCount) 12 | #else 13 | #define FileIO FILE 14 | #define fOpen(path, mode) fopen(path, mode) 15 | #define fRead(buffer, elementSize, elementCount, file) fread(buffer, elementSize, elementCount, file) 16 | #define fSeek(file, offset, whence) fseek(file, offset, whence) 17 | #define fTell(file) ftell(file) 18 | #define fClose(file) fclose(file) 19 | #define fWrite(buffer, elementSize, elementCount, file) fwrite(buffer, elementSize, elementCount, file) 20 | #endif 21 | 22 | struct FileInfo { 23 | char fileName[0x100]; 24 | int fileSize; 25 | int vfileSize; 26 | int readPos; 27 | int bufferPosition; 28 | int virtualFileOffset; 29 | byte eStringPosA; 30 | byte eStringPosB; 31 | byte eStringNo; 32 | byte eNybbleSwap; 33 | bool useEncryption; 34 | byte encryptionStringA[0x10]; 35 | byte encryptionStringB[0x10]; 36 | FileIO *cFileHandle; 37 | }; 38 | 39 | struct RSDKFileInfo { 40 | byte hash[0x10]; 41 | int offset; 42 | int filesize; 43 | bool encrypted; 44 | int fileID; 45 | }; 46 | 47 | struct RSDKContainer { 48 | RSDKFileInfo files[0x400]; 49 | int fileCount; 50 | }; 51 | 52 | extern RSDKContainer rsdkContainer; 53 | extern char rsdkName[0x400]; 54 | 55 | extern char fileName[0x100]; 56 | extern byte fileBuffer[0x2000]; 57 | extern int fileSize; 58 | extern int vFileSize; 59 | extern int readPos; 60 | extern int readSize; 61 | extern int bufferPosition; 62 | extern int virtualFileOffset; 63 | extern bool useEncryption; 64 | extern byte eStringPosA; 65 | extern byte eStringPosB; 66 | extern byte eStringNo; 67 | extern byte eNybbleSwap; 68 | extern byte encryptionStringA[0x10]; 69 | extern byte encryptionStringB[0x10]; 70 | 71 | extern FileIO *cFileHandle; 72 | 73 | inline void CopyFilePath(char *dest, const char *src) 74 | { 75 | strcpy(dest, src); 76 | for (int i = 0;; ++i) { 77 | if (i >= strlen(dest)) { 78 | break; 79 | } 80 | 81 | if (dest[i] == '/') 82 | dest[i] = '\\'; 83 | } 84 | } 85 | bool CheckRSDKFile(const char *filePath); 86 | 87 | bool LoadFile(const char *filePath, FileInfo *fileInfo); 88 | inline bool CloseFile() 89 | { 90 | int result = 0; 91 | if (cFileHandle) 92 | result = fClose(cFileHandle); 93 | 94 | cFileHandle = NULL; 95 | return result; 96 | } 97 | 98 | void GenerateELoadKeys(uint key1, uint key2); 99 | 100 | void FileRead(void *dest, int size); 101 | 102 | inline size_t FillFileBuffer() 103 | { 104 | if (readPos + 0x2000 <= fileSize) 105 | readSize = 0x2000; 106 | else 107 | readSize = fileSize - readPos; 108 | 109 | size_t result = fRead(fileBuffer, 1u, readSize, cFileHandle); 110 | readPos += readSize; 111 | bufferPosition = 0; 112 | return result; 113 | } 114 | 115 | void GetFileInfo(FileInfo *fileInfo); 116 | void SetFileInfo(FileInfo *fileInfo); 117 | size_t GetFilePosition(); 118 | void SetFilePosition(int newPos); 119 | bool ReachedEndOfFile(); 120 | 121 | 122 | size_t FileRead2(FileInfo *info, void *dest, int size); // For Music Streaming 123 | inline bool CloseFile2(FileInfo *info) 124 | { 125 | int result = 0; 126 | if (info->cFileHandle) 127 | result = fClose(info->cFileHandle); 128 | 129 | info->cFileHandle = NULL; 130 | return result; 131 | } 132 | size_t GetFilePosition2(FileInfo *info); 133 | void SetFilePosition2(FileInfo *info, int newPos); 134 | 135 | #endif // !READER_H 136 | -------------------------------------------------------------------------------- /Sonic12Decomp/RetroEngine.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RETROENGINE_H 2 | #define RETROENGINE_H 3 | 4 | // Disables POSIX use c++ name blah blah stuff 5 | #pragma warning(disable : 4996) 6 | 7 | // ================ 8 | // STANDARD LIBS 9 | // ================ 10 | #include 11 | #include 12 | 13 | // ================ 14 | // STANDARD TYPES 15 | // ================ 16 | typedef unsigned char byte; 17 | typedef signed char sbyte; 18 | typedef unsigned short ushort; 19 | typedef unsigned int uint; 20 | // typedef unsigned long long ulong; 21 | 22 | #define RETRO_USE_NETWORKING (0) 23 | #if !RETRO_USE_NETWORKING 24 | #define NETWORK_H // easy way to fuck over network header LOL 25 | #endif 26 | 27 | // Platforms (RSDKv4 only defines these 7 (I assume), but feel free to add your own custom platform define for easier platform code changes) 28 | #define RETRO_WIN (0) 29 | #define RETRO_OSX (1) 30 | #define RETRO_XBOX_360 (2) 31 | #define RETRO_PS3 (3) 32 | #define RETRO_iOS (4) 33 | #define RETRO_ANDROID (5) 34 | #define RETRO_WP7 (6) 35 | // Custom Platforms start here 36 | #define RETRO_VITA (7) 37 | 38 | // Platform types (Game manages platform-specific code such as HUD position using this rather than the above) 39 | #define RETRO_STANDARD (0) 40 | #define RETRO_MOBILE (1) 41 | 42 | #if defined _WIN32 43 | #define RETRO_PLATFORM (RETRO_WIN) 44 | #define RETRO_PLATTYPE (RETRO_STANDARD) 45 | #elif defined __APPLE__ 46 | #if __IPHONEOS__ 47 | #define RETRO_PLATTYPE (RETRO_iOS) 48 | #define RETRO_PLATTYPE (RETRO_MOBILE) 49 | #else 50 | #define RETRO_PLATFORM (RETRO_OSX) 51 | #define RETRO_PLATTYPE (RETRO_STANDARD) 52 | #endif 53 | #elif defined __vita__ 54 | #define RETRO_PLATFORM (RETRO_VITA) 55 | #define RETRO_PLATTYPE (RETRO_STANDARD) 56 | #else 57 | #define RETRO_PLATFORM (RETRO_WIN) 58 | #define RETRO_PLATTYPE (RETRO_STANDARD) 59 | #endif 60 | 61 | #if RETRO_PLATFORM == RETRO_VITA 62 | #if RETRO_GAME_SONIC == 1 63 | #define BASE_PATH "ux0:data/Sonic1/" 64 | #elif RETRO_GAME_SONIC == 2 65 | #define BASE_PATH "ux0:data/Sonic2/" 66 | #else 67 | #error "RETRO_GAME_SONIC not defined" 68 | #endif 69 | #define DEFAULT_SCREEN_XSIZE 480 70 | #define DEFAULT_FULLSCREEN false 71 | #define SCREEN_YSIZE (272) 72 | #else 73 | #define BASE_PATH "" 74 | #define DEFAULT_SCREEN_XSIZE 424 75 | #define DEFAULT_FULLSCREEN false 76 | #define SCREEN_YSIZE (240) 77 | #define RETRO_USING_MOUSE 78 | #define RETRO_USING_TOUCH 79 | #endif 80 | 81 | #if RETRO_PLATFORM == RETRO_WINDOWS || RETRO_PLATFORM == RETRO_OSX || RETRO_PLATFORM == RETRO_VITA 82 | #define RETRO_USING_SDL (1) 83 | #else // Since its an else & not an elif these platforms probably aren't supported yet 84 | #define RETRO_USING_SDL (0) 85 | #endif 86 | 87 | #define RETRO_GAME_STANDARD (0) 88 | #define RETRO_GAME_MOBILE (1) 89 | 90 | #if RETRO_PLATFORM == RETRO_iOS || RETRO_PLATFORM == RETRO_ANDROID || RETRO_PLATFORM == RETRO_WP7 91 | #define RETRO_GAMEPLATFORM (RETRO_GAME_MOBILE) 92 | #else 93 | #define RETRO_GAMEPLATFORM (RETRO_GAME_STANDARD) 94 | #endif 95 | 96 | #define RETRO_SW_RENDER (0) 97 | #define RETRO_HW_RENDER (1) 98 | #define RETRO_RENDERTYPE (RETRO_SW_RENDER) 99 | 100 | #define RETRO_USE_HAPTICS (1) 101 | 102 | enum RetroLanguages { 103 | RETRO_EN = 0, 104 | RETRO_FR = 1, 105 | RETRO_IT = 2, 106 | RETRO_DE = 3, 107 | RETRO_ES = 4, 108 | RETRO_JP = 5, 109 | RETRO_PT = 6, 110 | RETRO_RU = 7, 111 | RETRO_KO = 8, 112 | RETRO_ZH = 9, 113 | RETRO_ZS = 10, 114 | }; 115 | 116 | enum RetroStates { 117 | ENGINE_DEVMENU = 0, 118 | ENGINE_MAINGAME = 1, 119 | ENGINE_INITDEVMENU = 2, 120 | ENGINE_WAIT = 3, 121 | ENGINE_SCRIPTERROR = 4, 122 | ENGINE_INITPAUSE = 5, 123 | ENGINE_EXITPAUSE = 6, 124 | ENGINE_ENDGAME = 7, 125 | ENGINE_RESETGAME = 8, 126 | 127 | // Custom GameModes (required to make some features work 128 | ENGINE_CONNECT2PVS = 0x80, 129 | }; 130 | 131 | enum RetroGameType { 132 | GAME_UNKNOWN = 0, 133 | GAME_SONIC1 = 1, 134 | GAME_SONIC2 = 2, 135 | }; 136 | 137 | // General Defines 138 | #define SCREEN_CENTERY (SCREEN_YSIZE / 2) 139 | 140 | #if RETRO_PLATFORM == RETRO_WIN 141 | #include 142 | #include 143 | #elif RETRO_PLATFORM == RETRO_OSX 144 | #include 145 | #include 146 | 147 | #include "cocoaHelpers.hpp" 148 | 149 | #elif RETRO_USING_SDL 150 | #include 151 | #include 152 | 153 | #else 154 | 155 | #endif 156 | 157 | extern bool usingCWD; 158 | extern bool engineDebugMode; 159 | 160 | // Utils 161 | #include "Ini.hpp" 162 | #include "Network.hpp" 163 | 164 | #include "Math.hpp" 165 | #include "Reader.hpp" 166 | #include "String.hpp" 167 | #include "Animation.hpp" 168 | #include "Audio.hpp" 169 | #include "Input.hpp" 170 | #include "Object.hpp" 171 | #include "Palette.hpp" 172 | #include "Drawing.hpp" 173 | #include "Scene3D.hpp" 174 | #include "Collision.hpp" 175 | #include "Scene.hpp" 176 | #include "Script.hpp" 177 | #include "Sprite.hpp" 178 | #include "Text.hpp" 179 | #include "Userdata.hpp" 180 | #include "Debug.hpp" 181 | 182 | // Native Entities 183 | #include "PauseMenu.hpp" 184 | #include "RetroGameLoop.hpp" 185 | 186 | class RetroEngine 187 | { 188 | public: 189 | bool usingDataFile = false; 190 | bool usingBytecode = false; 191 | 192 | char *dataFile = new char[0x80]; 193 | 194 | bool initialised = false; 195 | bool running = false; 196 | 197 | int gameMode = 1; 198 | int language = RETRO_EN; 199 | int message = 0; 200 | 201 | bool trialMode = false; 202 | bool onlineActive = true; 203 | bool hapticsEnabled = true; 204 | 205 | int frameSkipSetting = 0; 206 | int frameSkipTimer = 0; 207 | 208 | // Ported from RSDKv5 209 | bool devMenu = false; 210 | int startList = -1; 211 | int startStage = -1; 212 | int startPlayer = -1; 213 | int startSave = -1; 214 | int gameSpeed = 1; 215 | int fastForwardSpeed = 8; 216 | bool masterPaused = false; 217 | bool frameStep = false; 218 | 219 | bool showPaletteOverlay = false; 220 | bool useHQModes = true; 221 | 222 | void Init(); 223 | void Run(); 224 | 225 | bool LoadGameConfig(const char *Filepath); 226 | 227 | int callbackMessage = 0; 228 | int prevMessage = 0; 229 | int waitValue = 0; 230 | void Callback(int callbackID); 231 | 232 | bool finishedStartMenu = false; 233 | 234 | char gameWindowText[0x40]; 235 | char gameDescriptionText[0x100]; 236 | const char *gameVersion = "1.1.0"; 237 | #if RETRO_GAMEPLATFORM == RETRO_GAME_STANDARD 238 | const char *gamePlatform = "STANDARD"; 239 | #elif RETRO_GAMEPLATFORM == RETRO_GAME_MOBILE 240 | const char *gamePlatform = "MOBILE"; 241 | #endif 242 | 243 | #if RETRO_RENDERTYPE == RETRO_SW_RENDER 244 | const char *gameRenderType = "SW_RENDERING"; 245 | #elif RETRO_RENDERTYPE == RETRO_HW_RENDER 246 | const char *gameRenderType = "HW_RENDERING"; 247 | #endif 248 | 249 | #if RETRO_USE_HAPTICS 250 | const char *gameHapticSetting = "USE_F_FEEDBACK"; // None is default, but people with controllers exist 251 | #else 252 | const char *gameHapticSetting = "NO_F_FEEDBACK"; 253 | #endif 254 | 255 | byte gameType = GAME_UNKNOWN; 256 | 257 | ushort *frameBuffer = nullptr; 258 | ushort *frameBuffer2x = nullptr; 259 | 260 | bool isFullScreen = false; 261 | 262 | bool startFullScreen = false; // if should start as fullscreen 263 | bool borderless = false; 264 | bool vsync = false; 265 | int windowScale = 2; 266 | int refreshRate = 60; // user-picked screen update rate 267 | int screenRefreshRate = 60; // hardware screen update rate 268 | int targetRefreshRate = 60; // game logic update rate 269 | 270 | uint frameCount = 0; // frames since scene load 271 | int renderFrameIndex = 0; 272 | int skipFrameIndex = 0; 273 | 274 | #if RETRO_USING_SDL 275 | SDL_Window *window = nullptr; 276 | SDL_Renderer *renderer = nullptr; 277 | SDL_Texture *screenBuffer = nullptr; 278 | SDL_Texture *screenBuffer2x = nullptr; 279 | 280 | SDL_Event sdlEvents; 281 | #endif 282 | }; 283 | 284 | extern RetroEngine Engine; 285 | #endif // !RETROENGINE_H 286 | -------------------------------------------------------------------------------- /Sonic12Decomp/RetroGameLoop.cpp: -------------------------------------------------------------------------------- 1 | #include "RetroEngine.hpp" 2 | 3 | void RetroGameLoop_Create(void *objPtr) { 4 | NativeEntity_RetroGameLoop *entity = (NativeEntity_RetroGameLoop *)objPtr; 5 | entity->pauseMenu = nullptr; 6 | } 7 | void RetroGameLoop_Main(void *objPtr) 8 | { 9 | NativeEntity_RetroGameLoop *entity = (NativeEntity_RetroGameLoop *)objPtr; 10 | 11 | switch (Engine.gameMode) { 12 | case ENGINE_DEVMENU: 13 | if (entity->pauseMenu && nativeEntityCount > 1) // dumb fix but yknow how it is 14 | RemoveNativeObject(entity->pauseMenu); 15 | entity->pauseMenu = nullptr; 16 | 17 | processStageSelect(); break; 18 | case ENGINE_MAINGAME: 19 | if (Engine.finishedStartMenu) 20 | ProcessStage(); 21 | else 22 | processStartMenu(); 23 | break; 24 | case ENGINE_INITDEVMENU: 25 | Engine.LoadGameConfig("Data/Game/GameConfig.bin"); 26 | initDevMenu(); 27 | ResetCurrentStageFolder(); 28 | break; 29 | case ENGINE_WAIT: break; 30 | case ENGINE_SCRIPTERROR: 31 | Engine.LoadGameConfig("Data/Game/GameConfig.bin"); 32 | initErrorMessage(); 33 | ResetCurrentStageFolder(); 34 | break; 35 | case ENGINE_INITPAUSE: 36 | PauseSound(); 37 | //ClearNativeObjects(); 38 | Engine.gameMode = ENGINE_WAIT; //temp (maybe?) so pause menu renders on top 39 | // CreateNativeObject(MenuBG_Create, MenuBG_Main); // temp until/if nativeObjs are fully complete 40 | if (entity->pauseMenu && nativeEntityCount > 1) 41 | RemoveNativeObject(entity->pauseMenu); 42 | entity->pauseMenu = (NativeEntity_PauseMenu*)CreateNativeObject(PauseMenu_Create, PauseMenu_Main); 43 | break; 44 | case ENGINE_EXITPAUSE: 45 | Engine.gameMode = ENGINE_MAINGAME; 46 | ResumeSound(); 47 | if (entity->pauseMenu) 48 | RemoveNativeObject(entity->pauseMenu); 49 | entity->pauseMenu = nullptr; 50 | break; 51 | case ENGINE_ENDGAME: 52 | ClearScreen(1); 53 | //RestoreNativeObjects(); 54 | Engine.LoadGameConfig("Data/Game/GameConfig.bin"); 55 | activeStageList = 0; 56 | stageListPosition = 0; 57 | initStartMenu(0); 58 | break; 59 | case ENGINE_RESETGAME: //Also called when 2P VS disconnects 60 | ClearScreen(1); 61 | //RestoreNativeObjects(); 62 | initStartMenu(1); 63 | break; 64 | case ENGINE_CONNECT2PVS: 65 | //connect screen goes here 66 | break; 67 | default: 68 | printLog("GameMode '%d' Called", Engine.gameMode); 69 | activeStageList = 0; 70 | stageListPosition = 0; 71 | stageMode = STAGEMODE_LOAD; 72 | Engine.gameMode = ENGINE_MAINGAME; 73 | break; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Sonic12Decomp/RetroGameLoop.hpp: -------------------------------------------------------------------------------- 1 | #ifndef NATIVE_RETROGAMELOOP_H 2 | #define NATIVE_RETROGAMELOOP_H 3 | 4 | struct NativeEntity_RetroGameLoop : NativeEntityBase { 5 | //Nothin lol 6 | NativeEntity_PauseMenu *pauseMenu; 7 | }; 8 | 9 | void RetroGameLoop_Create(void *objPtr); 10 | void RetroGameLoop_Main(void *objPtr); 11 | 12 | #endif // !NATIVE_RETROGAMELOOP_H 13 | -------------------------------------------------------------------------------- /Sonic12Decomp/Scene.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SCENE_H 2 | #define SCENE_H 3 | 4 | #define LAYER_COUNT (9) 5 | #define DEFORM_STORE (0x100) 6 | #define DEFORM_SIZE (320) 7 | #define DEFORM_COUNT (DEFORM_STORE + DEFORM_SIZE) 8 | #define PARALLAX_COUNT (0x100) 9 | 10 | #define TILE_COUNT (0x400) 11 | #define TILE_SIZE (0x10) 12 | #define CHUNK_SIZE (0x80) 13 | #define TILE_DATASIZE (TILE_SIZE * TILE_SIZE) 14 | #define TILESET_SIZE (TILE_COUNT * TILE_DATASIZE) 15 | 16 | #define TILELAYER_CHUNK_W (0x100) 17 | #define TILELAYER_CHUNK_H (0x100) 18 | #define TILELAYER_CHUNK_MAX (TILELAYER_CHUNK_W * TILELAYER_CHUNK_H) 19 | #define TILELAYER_SCROLL_MAX (TILELAYER_CHUNK_H * CHUNK_SIZE) 20 | 21 | #define CHUNKTILE_COUNT (0x200 * (8 * 8)) 22 | 23 | #define CPATH_COUNT (2) 24 | 25 | enum StageListNames { 26 | STAGELIST_PRESENTATION = 0, 27 | STAGELIST_REGULAR = 1, 28 | STAGELIST_BONUS = 2, 29 | STAGELIST_SPECIAL = 3, 30 | STAGELIST_MAX, //StageList size 31 | }; 32 | 33 | enum TileLayerTypes { 34 | LAYER_NOSCROLL = 0, 35 | LAYER_HSCROLL = 1, 36 | LAYER_VSCROLL = 2, 37 | LAYER_3DFLOOR = 3, 38 | LAYER_3DSKY = 4, 39 | }; 40 | 41 | enum StageModes { 42 | STAGEMODE_LOAD = 0, 43 | STAGEMODE_NORMAL = 1, 44 | STAGEMODE_PAUSED = 2, 45 | STAGEMODE_FROZEN = 3, 46 | STAGEMODE_2P = 4, 47 | STAGEMODE_STEPOVER = 5, 48 | STAGEMODE_PAUSED_STEPOVER = 6, 49 | STAGEMODE_FROZEN_PAUSED = 7, 50 | STAGEMODE_2P_PAUSED = 8 51 | }; 52 | 53 | enum TileInfo { 54 | TILEINFO_INDEX = 0, 55 | TILEINFO_DIRECTION = 1, 56 | TILEINFO_VISUALPLANE = 2, 57 | TILEINFO_SOLIDITYA = 3, 58 | TILEINFO_SOLIDITYB = 4, 59 | TILEINFO_FLAGSA = 5, 60 | TILEINFO_ANGLEA = 6, 61 | TILEINFO_FLAGSB = 7, 62 | TILEINFO_ANGLEB = 8, 63 | }; 64 | 65 | enum DeformationModes { 66 | DEFORM_FG = 0, 67 | DEFORM_FG_WATER = 1, 68 | DEFORM_BG = 2, 69 | DEFORM_BG_WATER = 3, 70 | }; 71 | 72 | struct SceneInfo { 73 | char name[0x40]; 74 | char folder[0x40]; 75 | char id[0x40]; 76 | bool highlighted; 77 | }; 78 | 79 | struct CollisionMasks { 80 | sbyte floorMasks[TILE_COUNT * TILE_SIZE]; 81 | sbyte lWallMasks[TILE_COUNT * TILE_SIZE]; 82 | sbyte rWallMasks[TILE_COUNT * TILE_SIZE]; 83 | sbyte roofMasks[TILE_COUNT * TILE_SIZE]; 84 | uint angles[TILE_COUNT]; 85 | byte flags[TILE_COUNT]; 86 | }; 87 | 88 | struct TileLayer { 89 | ushort tiles[TILELAYER_CHUNK_MAX]; 90 | byte lineScroll[TILELAYER_SCROLL_MAX]; 91 | int parallaxFactor; 92 | int scrollSpeed; 93 | int scrollPos; 94 | int angle; 95 | int XPos; 96 | int YPos; 97 | int ZPos; 98 | int deformationOffset; 99 | int deformationOffsetW; 100 | byte type; 101 | byte width; 102 | byte height; 103 | }; 104 | 105 | struct LineScroll { 106 | int parallaxFactor[PARALLAX_COUNT]; 107 | int scrollSpeed[PARALLAX_COUNT]; 108 | int scrollPos[PARALLAX_COUNT]; 109 | int tilePos[PARALLAX_COUNT]; 110 | int deform[PARALLAX_COUNT]; 111 | byte entryCount; 112 | }; 113 | 114 | struct Tiles128x128 { 115 | int gfxDataPos[CHUNKTILE_COUNT]; 116 | ushort tileIndex[CHUNKTILE_COUNT]; 117 | byte direction[CHUNKTILE_COUNT]; 118 | byte visualPlane[CHUNKTILE_COUNT]; 119 | byte collisionFlags[CPATH_COUNT][CHUNKTILE_COUNT]; 120 | }; 121 | 122 | extern int stageListCount[STAGELIST_MAX]; 123 | extern char stageListNames[STAGELIST_MAX][0x20]; 124 | extern SceneInfo stageList[STAGELIST_MAX][0x100]; 125 | 126 | extern int stageMode; 127 | 128 | extern int cameraTarget; 129 | extern int cameraStyle; 130 | extern int cameraEnabled; 131 | extern int cameraAdjustY; 132 | extern int xScrollOffset; 133 | extern int yScrollOffset; 134 | extern int cameraXPos; 135 | extern int cameraYPos; 136 | extern int cameraShift; 137 | extern int cameraLockedY; 138 | extern int cameraShakeX; 139 | extern int cameraShakeY; 140 | extern int cameraLag; 141 | extern int cameraLagStyle; 142 | 143 | extern int curXBoundary1; 144 | extern int newXBoundary1; 145 | extern int curYBoundary1; 146 | extern int newYBoundary1; 147 | extern int curXBoundary2; 148 | extern int curYBoundary2; 149 | extern int waterLevel; 150 | extern int waterDrawPos; 151 | extern int newXBoundary2; 152 | extern int newYBoundary2; 153 | 154 | extern int SCREEN_SCROLL_LEFT; 155 | extern int SCREEN_SCROLL_RIGHT; 156 | #define SCREEN_SCROLL_UP ((SCREEN_YSIZE / 2) - 16) 157 | #define SCREEN_SCROLL_DOWN ((SCREEN_YSIZE / 2) + 16) 158 | 159 | extern int lastXSize; 160 | extern int lastYSize; 161 | 162 | extern bool pauseEnabled; 163 | extern bool timeEnabled; 164 | extern bool debugMode; 165 | extern int frameCounter; 166 | extern int stageMilliseconds; 167 | extern int stageSeconds; 168 | extern int stageMinutes; 169 | 170 | // Category and Scene IDs 171 | extern int activeStageList; 172 | extern int stageListPosition; 173 | extern char currentStageFolder[0x100]; 174 | extern int actID; 175 | 176 | extern char titleCardText[0x100]; 177 | extern byte titleCardWord2; 178 | 179 | extern byte activeTileLayers[4]; 180 | extern byte tLayerMidPoint; 181 | extern TileLayer stageLayouts[LAYER_COUNT]; 182 | 183 | extern int bgDeformationData0[DEFORM_COUNT]; 184 | extern int bgDeformationData1[DEFORM_COUNT]; 185 | extern int bgDeformationData2[DEFORM_COUNT]; 186 | extern int bgDeformationData3[DEFORM_COUNT]; 187 | 188 | extern LineScroll hParallax; 189 | extern LineScroll vParallax; 190 | 191 | extern Tiles128x128 tiles128x128; 192 | extern CollisionMasks collisionMasks[2]; 193 | 194 | extern byte tilesetGFXData[TILESET_SIZE]; 195 | 196 | extern ushort tile3DFloorBuffer[0x13334]; 197 | extern bool drawStageGFXHQ; 198 | 199 | void InitFirstStage(); 200 | void InitStartingStage(int list, int stage, int player); 201 | void ProcessStage(); 202 | 203 | void ProcessParallaxAutoScroll(); 204 | 205 | void ResetBackgroundSettings(); 206 | inline void ResetCurrentStageFolder() { strcpy(currentStageFolder, ""); } 207 | inline bool CheckCurrentStageFolder(int stage) 208 | { 209 | if (strcmp(currentStageFolder, stageList[activeStageList][stage].folder) == 0) { 210 | return true; 211 | } 212 | else { 213 | strcpy(currentStageFolder, stageList[activeStageList][stage].folder); 214 | return false; 215 | } 216 | } 217 | 218 | void LoadStageFiles(); 219 | int LoadActFile(const char *ext, int stageID, FileInfo *info); 220 | int LoadStageFile(const char *filePath, int stageID, FileInfo *info); 221 | 222 | void LoadActLayout(); 223 | void LoadStageBackground(); 224 | void LoadStageChunks(); 225 | void LoadStageCollisions(); 226 | void LoadStageGIFFile(int stageID); 227 | 228 | inline void Init3DFloorBuffer(int layerID) 229 | { 230 | for (int y = 0; y < TILELAYER_CHUNK_H; ++y) { 231 | for (int x = 0; x < TILELAYER_CHUNK_W; ++x) { 232 | int c = stageLayouts[layerID].tiles[(x >> 3) + (y >> 3 << 8)] << 6; 233 | int tx = x & 7; 234 | tile3DFloorBuffer[x + (y << 8)] = c + tx + ((y & 7) << 3); 235 | } 236 | } 237 | } 238 | 239 | inline void Copy16x16Tile(ushort dest, ushort src) 240 | { 241 | byte *destPtr = &tilesetGFXData[TILELAYER_CHUNK_W * dest]; 242 | byte *srcPtr = &tilesetGFXData[TILELAYER_CHUNK_W * src]; 243 | int cnt = TILE_DATASIZE; 244 | while (cnt--) *destPtr++ = *srcPtr++; 245 | } 246 | 247 | void SetLayerDeformation(int deformID, int deformationA, int deformationB, int deformType, int deformOffset, int deformCount); 248 | 249 | void SetPlayerScreenPosition(Entity *target); 250 | void SetPlayerScreenPositionCDStyle(Entity *target); 251 | void SetPlayerHLockedScreenPosition(Entity *target); 252 | void SetPlayerLockedScreenPosition(Entity *target); 253 | void SetPlayerScreenPositionFixed(Entity *target); 254 | 255 | #endif // !SCENE_H 256 | -------------------------------------------------------------------------------- /Sonic12Decomp/Scene3D.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DRAWING3D_H 2 | #define DRAWING3D_H 3 | 4 | #define VERTEXBUFFER_SIZE (0x1000) 5 | #define FACEBUFFER_SIZE (0x400) 6 | 7 | enum FaceFlags { 8 | FACE_FLAG_TEXTURED_3D = 0, 9 | FACE_FLAG_TEXTURED_2D = 1, 10 | FACE_FLAG_COLOURED_3D = 2, 11 | FACE_FLAG_COLOURED_2D = 3, 12 | FACE_FLAG_FADED = 4, 13 | FACE_FLAG_TEXTURED_C = 5, 14 | FACE_FLAG_TEXTURED_D = 6, 15 | FACE_FLAG_3DSPRITE = 7 16 | }; 17 | 18 | enum MatrixTypes { 19 | MAT_WORLD = 0, 20 | MAT_VIEW = 1, 21 | MAT_TEMP = 2, 22 | }; 23 | 24 | struct Matrix { 25 | int values[4][4]; 26 | }; 27 | 28 | struct Vertex { 29 | int x; 30 | int y; 31 | int z; 32 | int u; 33 | int v; 34 | }; 35 | 36 | struct Face { 37 | int a; 38 | int b; 39 | int c; 40 | int d; 41 | uint colour; 42 | int flag; 43 | }; 44 | 45 | struct DrawListEntry3D { 46 | int faceID; 47 | int depth; 48 | }; 49 | 50 | extern int vertexCount; 51 | extern int faceCount; 52 | 53 | extern Matrix matFinal; 54 | extern Matrix matWorld; 55 | extern Matrix matView; 56 | extern Matrix matTemp; 57 | 58 | extern Face faceBuffer[FACEBUFFER_SIZE]; 59 | extern Vertex vertexBuffer[VERTEXBUFFER_SIZE]; 60 | extern Vertex vertexBufferT[VERTEXBUFFER_SIZE]; 61 | 62 | extern DrawListEntry3D drawList3D[FACEBUFFER_SIZE]; 63 | 64 | extern int projectionX; 65 | extern int projectionY; 66 | extern int fogColour; 67 | extern int fogStrength; 68 | 69 | extern int faceLineStart[SCREEN_YSIZE]; 70 | extern int faceLineEnd[SCREEN_YSIZE]; 71 | extern int faceLineStartU[SCREEN_YSIZE]; 72 | extern int faceLineEndU[SCREEN_YSIZE]; 73 | extern int faceLineStartV[SCREEN_YSIZE]; 74 | extern int faceLineEndV[SCREEN_YSIZE]; 75 | 76 | void setIdentityMatrix(Matrix *matrix); 77 | void matrixMultiply(Matrix *matrixA, Matrix *matrixB); 78 | void matrixTranslateXYZ(Matrix *Matrix, int XPos, int YPos, int ZPos); 79 | void matrixScaleXYZ(Matrix *matrix, int scaleX, int scaleY, int scaleZ); 80 | void matrixRotateX(Matrix *matrix, int rotationX); 81 | void matrixRotateY(Matrix *matrix, int rotationY); 82 | void matrixRotateZ(Matrix *matrix, int rotationZ); 83 | void matrixRotateXYZ(Matrix *matrix, short rotationX, short rotationY, short rotationZ); 84 | void matrixInverse(Matrix *matrix); 85 | void transformVertexBuffer(); 86 | void transformVertices(Matrix *matrix, int startIndex, int endIndex); 87 | void sort3DDrawList(); 88 | void draw3DScene(int spriteSheetID); 89 | 90 | void processScanEdge(Vertex *vertA, Vertex *vertB); 91 | void processScanEdgeUV(Vertex *vertA, Vertex *vertB); 92 | 93 | #endif // !DRAWING3D_H 94 | -------------------------------------------------------------------------------- /Sonic12Decomp/Script.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SCRIPT_H 2 | #define SCRIPT_H 3 | 4 | #define SCRIPTDATA_COUNT (0x100000) 5 | #define JUMPTABLE_COUNT (0x10000) 6 | #define FUNCTION_COUNT (0x200) 7 | 8 | #define JUMPSTACK_COUNT (0x400) 9 | #define FUNCSTACK_COUNT (0x400) 10 | #define FORSTACK_COUNT (0x400) 11 | 12 | struct ScriptPtr { 13 | int scriptCodePtr; 14 | int jumpTablePtr; 15 | }; 16 | 17 | struct ObjectScript { 18 | int frameCount; 19 | int spriteSheetID; 20 | ScriptPtr eventMain; 21 | ScriptPtr eventDraw; 22 | ScriptPtr eventStartup; 23 | int frameListOffset; 24 | AnimationFile* animFile; 25 | }; 26 | 27 | struct ScriptEngine { 28 | int operands[0x10]; 29 | int tempValue[8]; 30 | int arrayPosition[9]; 31 | // int currentPlayer; // ArrayPos[6] 32 | // int playerCount; // ArrayPos[7] 33 | // int tempObjectPos; // ArrayPos[8] 34 | int checkResult; 35 | }; 36 | 37 | #define TABLE_COUNT (0x200) 38 | #define TABLE_ENTRY_COUNT (0x400) 39 | 40 | struct StaticInfo { 41 | StaticInfo() 42 | { 43 | StrCopy(name, ""); 44 | value = 0; 45 | dataPos = SCRIPTDATA_COUNT - 1; 46 | } 47 | StaticInfo(const char *aliasName, int val) 48 | { 49 | StrCopy(name, aliasName); 50 | value = val; 51 | dataPos = SCRIPTDATA_COUNT - 1; 52 | } 53 | 54 | char name[0x20]; 55 | int value; 56 | int dataPos; 57 | }; 58 | 59 | struct TableInfo { 60 | TableInfo() 61 | { 62 | StrCopy(name, ""); 63 | valueCount = 0; 64 | dataPos = SCRIPTDATA_COUNT - 1; 65 | } 66 | TableInfo(const char *aliasName, int valCnt) 67 | { 68 | StrCopy(name, aliasName); 69 | valueCount = valCnt; 70 | dataPos = SCRIPTDATA_COUNT - 1; 71 | } 72 | 73 | char name[0x20]; 74 | int valueCount; 75 | StaticInfo values[TABLE_ENTRY_COUNT]; 76 | int dataPos; 77 | }; 78 | 79 | enum ScriptSubs { EVENT_MAIN = 0, EVENT_DRAW = 1, EVENT_SETUP = 2 }; 80 | 81 | extern ObjectScript objectScriptList[OBJECT_COUNT]; 82 | extern ScriptPtr functionScriptList[FUNCTION_COUNT]; 83 | 84 | extern int scriptData[SCRIPTDATA_COUNT]; 85 | extern int jumpTableData[JUMPTABLE_COUNT]; 86 | 87 | extern int jumpTableStack[JUMPSTACK_COUNT]; 88 | extern int functionStack[FUNCSTACK_COUNT]; 89 | extern int foreachStack[FORSTACK_COUNT]; 90 | 91 | extern int scriptCodePos; //Bytecode reading offset 92 | extern int jumpTablePos; //Bytecode reading offset 93 | extern int jumpTableStackPos; 94 | extern int functionStackPos; 95 | extern int foreachStackPos; 96 | 97 | extern ScriptEngine scriptEng; 98 | extern char scriptText[0x4000]; 99 | 100 | extern int scriptDataPos; 101 | extern int scriptDataOffset; 102 | extern int jumpTableDataPos; 103 | extern int jumpTableDataOffset; 104 | 105 | extern int scriptFunctionCount; 106 | extern char scriptFunctionNames[FUNCTION_COUNT][0x20]; 107 | 108 | extern int lineID; 109 | 110 | void CheckAliasText(char *text); 111 | void CheckStaticText(char *text); 112 | TableInfo *CheckTableText(char *text); 113 | void ConvertArithmaticSyntax(char *text); 114 | void ConvertIfWhileStatement(char *text); 115 | void ConvertForeachStatement(char *text); 116 | bool ConvertSwitchStatement(char *text); 117 | void ConvertFunctionText(char *text); 118 | void CheckCaseNumber(char *text); 119 | bool ReadSwitchCase(char *text); 120 | void ReadTableValues(char *text); 121 | void AppendIntegerToString(char *text, int value); 122 | void AppendIntegerToStringW(ushort *text, int value); 123 | bool ConvertStringToInteger(const char *text, int *value); 124 | void CopyAliasStr(char *dest, char *text, bool arrayIndex); 125 | bool CheckOpcodeType(char *text); // Never actually used 126 | 127 | void ParseScriptFile(char *scriptName, int scriptID); 128 | void LoadBytecode(int stageListID, int scriptID); 129 | 130 | void ProcessScript(int scriptCodePtr, int jumpTablePtr, byte scriptSub); 131 | 132 | void ClearScriptData(void); 133 | 134 | #endif // !SCRIPT_H 135 | -------------------------------------------------------------------------------- /Sonic12Decomp/Sonic12Decomp.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #include "resource.h" 4 | 5 | #define APSTUDIO_READONLY_SYMBOLS 6 | ///////////////////////////////////////////////////////////////////////////// 7 | // 8 | // Generated from the TEXTINCLUDE 2 resource. 9 | // 10 | #include "winres.h" 11 | 12 | ///////////////////////////////////////////////////////////////////////////// 13 | #undef APSTUDIO_READONLY_SYMBOLS 14 | 15 | ///////////////////////////////////////////////////////////////////////////// 16 | // English (United Kingdom) resources 17 | 18 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG) 19 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK 20 | #pragma code_page(1252) 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""winres.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | #endif // English (United Kingdom) resources 48 | ///////////////////////////////////////////////////////////////////////////// 49 | 50 | 51 | ///////////////////////////////////////////////////////////////////////////// 52 | // English (Australia) resources 53 | 54 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA) 55 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS 56 | #pragma code_page(1252) 57 | 58 | ///////////////////////////////////////////////////////////////////////////// 59 | // 60 | // Version 61 | // 62 | 63 | VS_VERSION_INFO VERSIONINFO 64 | FILEVERSION 1,0,0,0 65 | PRODUCTVERSION 1,0,0,0 66 | FILEFLAGSMASK 0x3fL 67 | #ifdef _DEBUG 68 | FILEFLAGS 0x1L 69 | #else 70 | FILEFLAGS 0x0L 71 | #endif 72 | FILEOS 0x40004L 73 | FILETYPE 0x1L 74 | FILESUBTYPE 0x0L 75 | BEGIN 76 | BLOCK "StringFileInfo" 77 | BEGIN 78 | BLOCK "0c0904b0" 79 | BEGIN 80 | VALUE "CompanyName", "RSDKv4/Sonic 1/2 (2013) By Christian 'The Taxman' Whitehead & Simon 'Stealth' Thomley, Decompilation By Rubberduckycooly/RMGRich" 81 | VALUE "FileDescription", "RSDKv4" 82 | VALUE "FileVersion", "1.0.0" 83 | VALUE "InternalName", "RSDKv4.exe" 84 | VALUE "OriginalFilename", "RSDKv4.exe" 85 | VALUE "ProductName", "RSDKv4" 86 | VALUE "ProductVersion", "1.0.0" 87 | END 88 | END 89 | BLOCK "VarFileInfo" 90 | BEGIN 91 | VALUE "Translation", 0xc09, 1200 92 | END 93 | END 94 | 95 | #endif // English (Australia) resources 96 | ///////////////////////////////////////////////////////////////////////////// 97 | 98 | 99 | 100 | #ifndef APSTUDIO_INVOKED 101 | ///////////////////////////////////////////////////////////////////////////// 102 | // 103 | // Generated from the TEXTINCLUDE 3 resource. 104 | // 105 | 106 | 107 | ///////////////////////////////////////////////////////////////////////////// 108 | #endif // not APSTUDIO_INVOKED 109 | 110 | -------------------------------------------------------------------------------- /Sonic12Decomp/Sonic12Decomp.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | {BD4A4266-8ED9-491E-B4CC-3F63F0D39C1B} 24 | Win32Proj 25 | Sonic12Decomp 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | $(SolutionDir)build\$(Platform)\$(Configuration)\ 76 | RSDKv4 77 | $(Platform)\$(Configuration)\ 78 | 79 | 80 | true 81 | $(SolutionDir)build\$(Platform)\$(Configuration)\ 82 | RSDKv4_64 83 | 84 | 85 | false 86 | $(SolutionDir)build\$(Platform)\$(Configuration)\ 87 | RSDKv4 88 | $(Platform)\$(Configuration)\ 89 | 90 | 91 | false 92 | $(SolutionDir)build\$(Platform)\$(Configuration)\ 93 | RSDKv4_64 94 | 95 | 96 | 97 | NotUsing 98 | Level1 99 | true 100 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 101 | true 102 | $(SolutionDir)dependencies/windows/SDL2/include/;$(SolutionDir)dependencies/windows/libogg/include/;$(SolutionDir)dependencies/windows/libvorbis/include/;$(SolutionDir)dependencies/windows/libtheora/include/;$(SolutionDir)dependencies/all/asio/include;%(AdditionalIncludeDirectories) 103 | 104 | 105 | Console 106 | true 107 | $(SolutionDir)dependencies/windows/SDL2/lib/$(PlatformTargetAsMSBuildArchitecture)/;$(SolutionDir)dependencies/windows/libvorbis/win32/VS2010/$(Platform)/$(Configuration)/;$(SolutionDir)dependencies/windows/libogg/win32/VS2015/$(Platform)/$(Configuration)/ 108 | SDL2.lib;SDL2main.lib;libogg.lib;libvorbis_static.lib;libvorbisfile_static.lib;Shell32.lib 109 | 110 | 111 | copy /Y "$(SolutionDir)dependencies\windows\SDL2\lib\$(PlatformTargetAsMSBuildArchitecture)\SDL2.dll" "$(OutDir)" 112 | 113 | 114 | copying SDL2 library DLL to the build directory. build should fail if it's not found 115 | 116 | 117 | 118 | 119 | NotUsing 120 | Level1 121 | true 122 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 123 | true 124 | $(SolutionDir)dependencies/windows/SDL2/include/;$(SolutionDir)dependencies/windows/libogg/include/;$(SolutionDir)dependencies/windows/libvorbis/include/;$(SolutionDir)dependencies/windows/libtheora/include/;$(SolutionDir)dependencies/all/asio/include;%(AdditionalIncludeDirectories) 125 | 126 | 127 | Console 128 | true 129 | $(SolutionDir)dependencies/windows/SDL2/lib/$(PlatformTargetAsMSBuildArchitecture)/;$(SolutionDir)dependencies/windows/libvorbis/win32/VS2010/$(Platform)/$(Configuration)/;$(SolutionDir)dependencies/windows/libogg/win32/VS2015/$(Platform)/$(Configuration)/ 130 | SDL2.lib;SDL2main.lib;libogg.lib;libvorbis_static.lib;libvorbisfile_static.lib;Shell32.lib 131 | 132 | 133 | copy /Y "$(SolutionDir)dependencies\windows\SDL2\lib\$(PlatformTargetAsMSBuildArchitecture)\SDL2.dll" "$(OutDir)" 134 | 135 | 136 | copying SDL2 library DLL to the build directory. build should fail if it's not found 137 | 138 | 139 | 140 | 141 | NotUsing 142 | Level1 143 | true 144 | true 145 | true 146 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 147 | true 148 | $(SolutionDir)dependencies/windows/SDL2/include/;$(SolutionDir)dependencies/windows/libogg/include/;$(SolutionDir)dependencies/windows/libvorbis/include/;$(SolutionDir)dependencies/windows/libtheora/include/;$(SolutionDir)dependencies/all/asio/include;%(AdditionalIncludeDirectories) 149 | 150 | 151 | Console 152 | true 153 | true 154 | true 155 | $(SolutionDir)dependencies/windows/SDL2/lib/$(PlatformTargetAsMSBuildArchitecture)/;$(SolutionDir)dependencies/windows/libvorbis/win32/VS2010/$(Platform)/$(Configuration)/;$(SolutionDir)dependencies/windows/libogg/win32/VS2015/$(Platform)/$(Configuration)/ 156 | SDL2.lib;SDL2main.lib;libogg.lib;libvorbis_static.lib;libvorbisfile_static.lib;Shell32.lib 157 | 158 | 159 | copy /Y "$(SolutionDir)dependencies\windows\SDL2\lib\$(PlatformTargetAsMSBuildArchitecture)\SDL2.dll" "$(OutDir)" 160 | 161 | 162 | copying SDL2 library DLL to the build directory. build should fail if it's not found 163 | 164 | 165 | 166 | 167 | NotUsing 168 | Level1 169 | true 170 | true 171 | true 172 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 173 | true 174 | $(SolutionDir)dependencies/windows/SDL2/include/;$(SolutionDir)dependencies/windows/libogg/include/;$(SolutionDir)dependencies/windows/libvorbis/include/;$(SolutionDir)dependencies/windows/libtheora/include/;$(SolutionDir)dependencies/all/asio/include;%(AdditionalIncludeDirectories) 175 | 176 | 177 | Console 178 | true 179 | true 180 | true 181 | $(SolutionDir)dependencies/windows/SDL2/lib/$(PlatformTargetAsMSBuildArchitecture)/;$(SolutionDir)dependencies/windows/libvorbis/win32/VS2010/$(Platform)/$(Configuration)/;$(SolutionDir)dependencies/windows/libogg/win32/VS2015/$(Platform)/$(Configuration)/ 182 | SDL2.lib;SDL2main.lib;libogg.lib;libvorbis_static.lib;libvorbisfile_static.lib;Shell32.lib 183 | 184 | 185 | copy /Y "$(SolutionDir)dependencies\windows\SDL2\lib\$(PlatformTargetAsMSBuildArchitecture)\SDL2.dll" "$(OutDir)" 186 | 187 | 188 | copying SDL2 library DLL to the build directory. build should fail if it's not found 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | -------------------------------------------------------------------------------- /Sonic12Decomp/Sonic12Decomp.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {1890ac66-74c7-48ee-b3f4-2b4280f370b4} 18 | 19 | 20 | {65197e20-a952-4014-93ca-c21e07885ac7} 21 | 22 | 23 | 24 | 25 | Header Files 26 | 27 | 28 | Header Files 29 | 30 | 31 | Header Files 32 | 33 | 34 | Header Files 35 | 36 | 37 | Header Files 38 | 39 | 40 | Header Files 41 | 42 | 43 | Header Files 44 | 45 | 46 | Header Files 47 | 48 | 49 | Header Files 50 | 51 | 52 | Header Files 53 | 54 | 55 | Header Files 56 | 57 | 58 | Header Files 59 | 60 | 61 | Header Files 62 | 63 | 64 | Header Files 65 | 66 | 67 | Header Files 68 | 69 | 70 | Header Files 71 | 72 | 73 | Header Files 74 | 75 | 76 | Header Files 77 | 78 | 79 | Header Files 80 | 81 | 82 | Header Files 83 | 84 | 85 | Header Files\nativeEntities 86 | 87 | 88 | Header Files\nativeEntities 89 | 90 | 91 | Header Files 92 | 93 | 94 | 95 | 96 | Source Files 97 | 98 | 99 | Source Files 100 | 101 | 102 | Source Files 103 | 104 | 105 | Source Files 106 | 107 | 108 | Source Files 109 | 110 | 111 | Source Files 112 | 113 | 114 | Source Files 115 | 116 | 117 | Source Files 118 | 119 | 120 | Source Files 121 | 122 | 123 | Source Files 124 | 125 | 126 | Source Files 127 | 128 | 129 | Source Files 130 | 131 | 132 | Source Files 133 | 134 | 135 | Source Files 136 | 137 | 138 | Source Files 139 | 140 | 141 | Source Files 142 | 143 | 144 | Source Files 145 | 146 | 147 | Source Files 148 | 149 | 150 | Source Files 151 | 152 | 153 | Source Files 154 | 155 | 156 | Source Files\nativeEntities 157 | 158 | 159 | Source Files\nativeEntities 160 | 161 | 162 | Source Files 163 | 164 | 165 | 166 | 167 | Resource Files 168 | 169 | 170 | -------------------------------------------------------------------------------- /Sonic12Decomp/Sprite.cpp: -------------------------------------------------------------------------------- 1 | #include "RetroEngine.hpp" 2 | 3 | struct GifDecoder { 4 | int depth; 5 | int clearCode; 6 | int eofCode; 7 | int runningCode; 8 | int runningBits; 9 | int prevCode; 10 | int currentCode; 11 | int maxCodePlusOne; 12 | int stackPtr; 13 | int shiftState; 14 | int fileState; 15 | int position; 16 | int bufferSize; 17 | uint shiftData; 18 | uint pixelCount; 19 | byte buffer[256]; 20 | byte stack[4096]; 21 | byte suffix[4096]; 22 | uint prefix[4096]; 23 | }; 24 | 25 | const int LOADING_IMAGE = 0; 26 | const int LOAD_COMPLETE = 1; 27 | const int LZ_MAX_CODE = 4095; 28 | const int LZ_BITS = 12; 29 | const int FIRST_CODE = 4097; 30 | const int NO_SUCH_CODE = 4098; 31 | 32 | struct GifDecoder gifDecoder; 33 | int codeMasks[] = { 0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095 }; 34 | 35 | int ReadGifCode(void); 36 | byte ReadGifByte(void); 37 | byte TraceGifPrefix(uint *prefix, int code, int clearCode); 38 | 39 | void InitGifDecoder() 40 | { 41 | int val = 0; 42 | FileRead(&val, 1); 43 | gifDecoder.fileState = LOADING_IMAGE; 44 | gifDecoder.position = 0; 45 | gifDecoder.bufferSize = 0; 46 | gifDecoder.buffer[0] = 0; 47 | gifDecoder.depth = val; 48 | gifDecoder.clearCode = 1 << val; 49 | gifDecoder.eofCode = gifDecoder.clearCode + 1; 50 | gifDecoder.runningCode = gifDecoder.eofCode + 1; 51 | gifDecoder.runningBits = val + 1; 52 | gifDecoder.maxCodePlusOne = 1 << gifDecoder.runningBits; 53 | gifDecoder.stackPtr = 0; 54 | gifDecoder.prevCode = NO_SUCH_CODE; 55 | gifDecoder.shiftState = 0; 56 | gifDecoder.shiftData = 0u; 57 | for (int i = 0; i <= LZ_MAX_CODE; ++i) gifDecoder.prefix[i] = (byte)NO_SUCH_CODE; 58 | } 59 | void ReadGifLine(byte *line, int length, int offset) 60 | { 61 | int i = 0; 62 | int stackPtr = gifDecoder.stackPtr; 63 | int eofCode = gifDecoder.eofCode; 64 | int clearCode = gifDecoder.clearCode; 65 | int prevCode = gifDecoder.prevCode; 66 | if (stackPtr != 0) { 67 | while (stackPtr != 0) { 68 | if (i >= length) { 69 | break; 70 | } 71 | line[offset++] = gifDecoder.stack[--stackPtr]; 72 | i++; 73 | } 74 | } 75 | while (i < length) { 76 | int gifCode = ReadGifCode(); 77 | if (gifCode == eofCode) { 78 | if (i != length - 1 | gifDecoder.pixelCount != 0u) { 79 | return; 80 | } 81 | i++; 82 | } 83 | else { 84 | if (gifCode == clearCode) { 85 | for (int j = 0; j <= LZ_MAX_CODE; j++) { 86 | gifDecoder.prefix[j] = NO_SUCH_CODE; 87 | } 88 | gifDecoder.runningCode = gifDecoder.eofCode + 1; 89 | gifDecoder.runningBits = gifDecoder.depth + 1; 90 | gifDecoder.maxCodePlusOne = 1 << gifDecoder.runningBits; 91 | prevCode = (gifDecoder.prevCode = NO_SUCH_CODE); 92 | } 93 | else { 94 | if (gifCode < clearCode) { 95 | line[offset] = (byte)gifCode; 96 | offset++; 97 | i++; 98 | } 99 | else { 100 | if (gifCode<0 | gifCode> LZ_MAX_CODE) { 101 | return; 102 | } 103 | int code; 104 | if (gifDecoder.prefix[gifCode] == NO_SUCH_CODE) { 105 | if (gifCode != gifDecoder.runningCode - 2) { 106 | return; 107 | } 108 | code = prevCode; 109 | gifDecoder.suffix[gifDecoder.runningCode - 2] = 110 | (gifDecoder.stack[stackPtr++] = TraceGifPrefix(gifDecoder.prefix, prevCode, clearCode)); 111 | } 112 | else { 113 | code = gifCode; 114 | } 115 | int c = 0; 116 | while (c++ <= LZ_MAX_CODE && code > clearCode && code <= LZ_MAX_CODE) { 117 | gifDecoder.stack[stackPtr++] = gifDecoder.suffix[code]; 118 | code = gifDecoder.prefix[code]; 119 | } 120 | if (c >= LZ_MAX_CODE | code > LZ_MAX_CODE) { 121 | return; 122 | } 123 | gifDecoder.stack[stackPtr++] = (byte)code; 124 | while (stackPtr != 0 && i++ < length) { 125 | line[offset++] = gifDecoder.stack[--stackPtr]; 126 | } 127 | } 128 | if (prevCode != NO_SUCH_CODE) { 129 | if (gifDecoder.runningCode<2 | gifDecoder.runningCode> FIRST_CODE) { 130 | return; 131 | } 132 | gifDecoder.prefix[gifDecoder.runningCode - 2] = prevCode; 133 | if (gifCode == gifDecoder.runningCode - 2) { 134 | gifDecoder.suffix[gifDecoder.runningCode - 2] = TraceGifPrefix(gifDecoder.prefix, prevCode, clearCode); 135 | } 136 | else { 137 | gifDecoder.suffix[gifDecoder.runningCode - 2] = TraceGifPrefix(gifDecoder.prefix, gifCode, clearCode); 138 | } 139 | } 140 | prevCode = gifCode; 141 | } 142 | } 143 | } 144 | gifDecoder.prevCode = prevCode; 145 | gifDecoder.stackPtr = stackPtr; 146 | } 147 | 148 | int ReadGifCode() 149 | { 150 | while (gifDecoder.shiftState < gifDecoder.runningBits) { 151 | byte b = ReadGifByte(); 152 | gifDecoder.shiftData |= (uint)((uint)b << gifDecoder.shiftState); 153 | gifDecoder.shiftState += 8; 154 | } 155 | int result = (int)((unsigned long)gifDecoder.shiftData & (unsigned long)(codeMasks[gifDecoder.runningBits])); 156 | gifDecoder.shiftData >>= gifDecoder.runningBits; 157 | gifDecoder.shiftState -= gifDecoder.runningBits; 158 | if (++gifDecoder.runningCode > gifDecoder.maxCodePlusOne && gifDecoder.runningBits < LZ_BITS) { 159 | gifDecoder.maxCodePlusOne <<= 1; 160 | gifDecoder.runningBits++; 161 | } 162 | return result; 163 | } 164 | 165 | byte ReadGifByte() 166 | { 167 | byte c = '\0'; 168 | if (gifDecoder.fileState == LOAD_COMPLETE) 169 | return c; 170 | 171 | byte b; 172 | if (gifDecoder.position == gifDecoder.bufferSize) { 173 | FileRead(&b, 1); 174 | gifDecoder.bufferSize = (int)b; 175 | if (gifDecoder.bufferSize == 0) { 176 | gifDecoder.fileState = LOAD_COMPLETE; 177 | return c; 178 | } 179 | FileRead(gifDecoder.buffer, gifDecoder.bufferSize); 180 | b = gifDecoder.buffer[0]; 181 | gifDecoder.position = 1; 182 | } 183 | else { 184 | b = gifDecoder.buffer[gifDecoder.position++]; 185 | } 186 | return b; 187 | } 188 | 189 | byte TraceGifPrefix(uint *prefix, int code, int clearCode) 190 | { 191 | int i = 0; 192 | while (code > clearCode && i++ <= LZ_MAX_CODE) code = prefix[code]; 193 | 194 | return code; 195 | } 196 | void ReadGifPictureData(int width, int height, bool interlaced, byte *gfxData, int offset) 197 | { 198 | int array[] = { 0, 4, 2, 1 }; 199 | int array2[] = { 8, 8, 4, 2 }; 200 | InitGifDecoder(); 201 | if (interlaced) { 202 | for (int i = 0; i < 4; ++i) { 203 | for (int j = array[i]; j < height; j += array2[i]) { 204 | ReadGifLine(gfxData, width, j * width + offset); 205 | } 206 | } 207 | return; 208 | } 209 | for (int h = 0; h < height; ++h) ReadGifLine(gfxData, width, h * width + offset); 210 | } 211 | 212 | int AddGraphicsFile(const char *filePath) 213 | { 214 | char sheetPath[0x100]; 215 | 216 | StrCopy(sheetPath, "Data/Sprites/"); 217 | StrAdd(sheetPath, filePath); 218 | int sheetID = 0; 219 | while (StrLength(gfxSurface[sheetID].fileName) > 0) { 220 | if (StrComp(gfxSurface[sheetID].fileName, sheetPath)) 221 | return sheetID; 222 | if (++sheetID == SURFACE_MAX) // Max Sheet cnt 223 | return 0; 224 | } 225 | byte fileExtension = (byte)sheetPath[(StrLength(sheetPath) - 1) & 0xFF]; 226 | switch (fileExtension) { 227 | case 'f': LoadGIFFile(sheetPath, sheetID); break; 228 | case 'p': LoadBMPFile(sheetPath, sheetID); break; 229 | case 'r': LoadPVRFile(sheetPath, sheetID); break; 230 | } 231 | 232 | return sheetID; 233 | } 234 | void RemoveGraphicsFile(const char *filePath, int sheetID) 235 | { 236 | if (sheetID < 0) { 237 | for (int i = 0; i < SURFACE_MAX; ++i) { 238 | if (StrLength(gfxSurface[i].fileName) > 0 && StrComp(gfxSurface[i].fileName, filePath)) 239 | sheetID = i; 240 | } 241 | } 242 | 243 | if (sheetID >= 0 && StrLength(gfxSurface[sheetID].fileName)) { 244 | StrCopy(gfxSurface[sheetID].fileName, ""); 245 | int dataPosStart = gfxSurface[sheetID].dataPosition; 246 | int dataPosEnd = gfxSurface[sheetID].dataPosition + gfxSurface[sheetID].height * gfxSurface[sheetID].width; 247 | for (int i = 0x200000 - dataPosEnd; i > 0; --i) graphicData[dataPosStart++] = graphicData[dataPosEnd++]; 248 | gfxDataPosition -= gfxSurface[sheetID].height * gfxSurface[sheetID].width; 249 | for (int i = 0; i < SURFACE_MAX; ++i) { 250 | if (gfxSurface[i].dataPosition > gfxSurface[sheetID].dataPosition) 251 | gfxSurface[i].dataPosition -= gfxSurface[sheetID].height * gfxSurface[sheetID].width; 252 | } 253 | } 254 | } 255 | 256 | int LoadBMPFile(const char *filePath, byte sheetID) 257 | { 258 | FileInfo info; 259 | if (LoadFile(filePath, &info)) { 260 | GFXSurface *surface = &gfxSurface[sheetID]; 261 | StrCopy(surface->fileName, filePath); 262 | 263 | int fileBuffer = 0; 264 | 265 | SetFilePosition(18); 266 | FileRead(&fileBuffer, 1); 267 | surface->width = fileBuffer; 268 | FileRead(&fileBuffer, 1); 269 | surface->width += fileBuffer << 8; 270 | FileRead(&fileBuffer, 1); 271 | surface->width += fileBuffer << 16; 272 | FileRead(&fileBuffer, 1); 273 | surface->width += fileBuffer << 24; 274 | 275 | FileRead(&fileBuffer, 1); 276 | surface->height = fileBuffer; 277 | FileRead(&fileBuffer, 1); 278 | surface->height += fileBuffer << 8; 279 | FileRead(&fileBuffer, 1); 280 | surface->height += fileBuffer << 16; 281 | FileRead(&fileBuffer, 1); 282 | surface->height += fileBuffer << 24; 283 | 284 | SetFilePosition(info.vfileSize - surface->height * surface->width); 285 | surface->dataPosition = gfxDataPosition; 286 | byte *gfxData = &graphicData[surface->dataPosition + surface->width * (surface->height - 1)]; 287 | for (int y = 0; y < surface->height; ++y) { 288 | for (int x = 0; x < surface->width; ++x) { 289 | FileRead(&fileBuffer, 1); 290 | *gfxData++ = fileBuffer; 291 | } 292 | gfxData -= 2 * surface->width; 293 | } 294 | gfxDataPosition += surface->height * surface->width; 295 | 296 | surface->widthShift = 0; 297 | int w = surface->width; 298 | while (w > 1) { 299 | w >>= 1; 300 | ++surface->widthShift; 301 | } 302 | if (gfxDataPosition >= 0x400000) 303 | gfxDataPosition = 0; 304 | 305 | CloseFile(); 306 | return true; 307 | } 308 | return false; 309 | } 310 | int LoadGIFFile(const char *filePath, byte sheetID) 311 | { 312 | FileInfo info; 313 | if (LoadFile(filePath, &info)) { 314 | GFXSurface *surface = &gfxSurface[sheetID]; 315 | StrCopy(surface->fileName, filePath); 316 | 317 | int fileBuffer = 0; 318 | 319 | SetFilePosition(6); // GIF89a 320 | FileRead(&fileBuffer, 1); 321 | surface->width = fileBuffer; 322 | FileRead(&fileBuffer, 1); 323 | surface->width += (fileBuffer << 8); 324 | FileRead(&fileBuffer, 1); 325 | surface->height = fileBuffer; 326 | FileRead(&fileBuffer, 1); 327 | surface->height += (fileBuffer << 8); 328 | 329 | FileRead(&fileBuffer, 1); // Palette Size (thrown away) :/ 330 | FileRead(&fileBuffer, 1); // BG Colour index (thrown away) 331 | FileRead(&fileBuffer, 1); // idk actually (still thrown away) 332 | 333 | int c = 0; 334 | byte clr[3]; 335 | do { 336 | ++c; 337 | FileRead(clr, 3); 338 | } while (c != 0x100); 339 | 340 | FileRead(&fileBuffer, 1); 341 | while (fileBuffer != ',') FileRead(&fileBuffer, 1); // gif image start identifier 342 | 343 | FileRead(&fileBuffer, 2); 344 | FileRead(&fileBuffer, 2); 345 | FileRead(&fileBuffer, 2); 346 | FileRead(&fileBuffer, 2); 347 | FileRead(&fileBuffer, 1); 348 | bool interlaced = (fileBuffer & 0x40) >> 6; 349 | if (fileBuffer >> 7 == 1) { 350 | int c = 128; 351 | do { 352 | ++c; 353 | FileRead(clr, 3); 354 | } while (c != 256); 355 | } 356 | 357 | surface->dataPosition = gfxDataPosition; 358 | surface->widthShift = 0; 359 | int w = surface->width; 360 | while (w > 1) { 361 | w >>= 1; 362 | ++surface->widthShift; 363 | } 364 | 365 | gfxDataPosition += surface->width * surface->height; 366 | if (gfxDataPosition <= 0x3FFFFF) 367 | ReadGifPictureData(surface->width, surface->height, interlaced, graphicData, surface->dataPosition); 368 | else 369 | gfxDataPosition = 0; 370 | 371 | CloseFile(); 372 | return true; 373 | } 374 | return false; 375 | } 376 | int LoadPVRFile(const char *filePath, byte sheetID) 377 | { 378 | // ONLY READS "PVRTC 2bpp RGB" PVR FILES 379 | FileInfo info; 380 | if (LoadFile(filePath, &info)) { 381 | GFXSurface *surface = &gfxSurface[sheetID]; 382 | StrCopy(surface->fileName, filePath); 383 | 384 | byte fileBuffer[2]; 385 | 386 | SetFilePosition(28); 387 | FileRead(fileBuffer, 1); 388 | int width = fileBuffer[0]; 389 | FileRead(fileBuffer, 1); 390 | width += fileBuffer[0] << 8; 391 | FileRead(fileBuffer, 1); 392 | int height = fileBuffer[0]; 393 | FileRead(fileBuffer, 1); 394 | height = fileBuffer[0] << 8; 395 | 396 | surface->width = width; 397 | surface->height = height; 398 | surface->dataPosition = gfxDataPosition; 399 | gfxDataPosition += surface->width * surface->height; 400 | 401 | surface->widthShift = 0; 402 | int w = surface->width; 403 | while (w > 1) { 404 | w >>= 1; 405 | ++surface->widthShift; 406 | } 407 | 408 | return false; // yeah I have no clue how to handle this, cd lite has this be loaded every frame on framebuffer update and does it that way 409 | 410 | ushort *buffer = NULL; 411 | for (int h = 0; h < height; ++h) { 412 | for (int w = 0; w < width; ++w) { 413 | FileRead(fileBuffer, 2); 414 | buffer[w] = 2 * (fileBuffer[0] + (fileBuffer[1] << 8)) | 1; 415 | } 416 | buffer += width; 417 | } 418 | buffer += 0x400 - width; 419 | 420 | CloseFile(); 421 | return true; 422 | } 423 | return false; 424 | } 425 | -------------------------------------------------------------------------------- /Sonic12Decomp/Sprite.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SPRITE_H 2 | #define SPRITE_H 3 | 4 | int AddGraphicsFile(const char *filePath); 5 | void RemoveGraphicsFile(const char *filePath, int sheetID); 6 | 7 | int LoadBMPFile(const char *filePath, byte sheetID); 8 | int LoadGIFFile(const char *filePath, byte sheetID); 9 | int LoadPVRFile(const char *filePath, byte sheetID); 10 | 11 | void ReadGifPictureData(int width, int height, bool interlaced, byte *gfxData, int offset); 12 | 13 | #endif // !SPRITE_H 14 | -------------------------------------------------------------------------------- /Sonic12Decomp/String.hpp: -------------------------------------------------------------------------------- 1 | #ifndef STRING_H 2 | #define STRING_H 3 | 4 | #define STRSTORAGE_SIZE (1000) 5 | #define STRING_SIZE (0x200) 6 | 7 | #define CREDITS_LIST_SIZE (0x200) 8 | 9 | #define USE_STDLIB 10 | 11 | extern ushort *strPressStart; 12 | extern ushort *strTouchToStart; 13 | extern ushort *strStartGame; 14 | extern ushort *strTimeAttack; 15 | extern ushort *strAchievements; 16 | extern ushort *strLeaderboards; 17 | extern ushort *strHelpAndOptions; 18 | extern ushort *strSoundTest; 19 | extern ushort *str2PlayerVS; 20 | extern ushort *strSaveSelect; 21 | extern ushort *strPlayerSelect; 22 | extern ushort *strNoSave; 23 | extern ushort *strNewGame; 24 | extern ushort *strDelete; 25 | extern ushort *strDeleteMessage; 26 | extern ushort *strYes; 27 | extern ushort *strNo; 28 | extern ushort *strSonic; 29 | extern ushort *strTails; 30 | extern ushort *strKnuckles; 31 | extern ushort *strPause; 32 | extern ushort *strContinue; 33 | extern ushort *strRestart; 34 | extern ushort *strExit; 35 | extern ushort *strDevMenu; 36 | extern ushort *strRestartMessage; 37 | extern ushort *strExitMessage; 38 | extern ushort *strNSRestartMessage; 39 | extern ushort *strNSExitMessage; 40 | extern ushort *strExitGame; 41 | extern ushort *strNetworkMessage; 42 | extern ushort *strStageList[8]; 43 | extern ushort *strSaveStageList[26]; 44 | extern ushort *strNewBestTime; 45 | extern ushort *strRecords; 46 | extern ushort *strNextAct; 47 | extern ushort *strPlay; 48 | extern ushort *strTotalTime; 49 | extern ushort *strInstructions; 50 | extern ushort *strSettings; 51 | extern ushort *strStaffCredits; 52 | extern ushort *strAbout; 53 | extern ushort *strMusic; 54 | extern ushort *strSoundFX; 55 | extern ushort *strSpindash; 56 | extern ushort *strBoxArt; 57 | extern ushort *strControls; 58 | extern ushort *strOn; 59 | extern ushort *strOff; 60 | extern ushort *strCustomizeDPad; 61 | extern ushort *strDPadSize; 62 | extern ushort *strDPadOpacity; 63 | extern ushort *strHelpText1; 64 | extern ushort *strHelpText2; 65 | extern ushort *strHelpText3; 66 | extern ushort *strHelpText4; 67 | extern ushort *strHelpText5; 68 | extern ushort *strVersionName; 69 | extern ushort *strPrivacy; 70 | extern ushort *strTerms; 71 | 72 | extern int stageStrCount; 73 | 74 | extern ushort stringStorage[STRSTORAGE_SIZE][STRING_SIZE]; 75 | extern int stringStorePos; 76 | 77 | extern int creditsListSize; 78 | extern const ushort *strCreditsList[CREDITS_LIST_SIZE]; 79 | extern byte creditsType[CREDITS_LIST_SIZE]; 80 | extern float creditsAdvanceY[CREDITS_LIST_SIZE]; 81 | 82 | inline void StrCopy(char *dest, const char *src) 83 | { 84 | #ifdef USE_STDLIB 85 | strcpy(dest, src); 86 | 87 | #else 88 | int i = 0; 89 | 90 | for (; src[i]; ++i) dest[i] = src[i]; 91 | 92 | dest[i] = 0; 93 | 94 | #endif 95 | } 96 | 97 | inline void StrAdd(char *dest, const char *src) 98 | { 99 | int destStrPos = 0; 100 | int srcStrPos = 0; 101 | while (dest[destStrPos]) ++destStrPos; 102 | while (true) { 103 | if (!src[srcStrPos]) { 104 | break; 105 | } 106 | dest[destStrPos++] = src[srcStrPos++]; 107 | } 108 | dest[destStrPos] = 0; 109 | } 110 | 111 | inline bool StrComp(const char *stringA, const char *stringB) 112 | { 113 | bool match = true; 114 | bool finished = false; 115 | while (!finished) { 116 | if (*stringA == *stringB || *stringA == *stringB + ' ' || *stringA == *stringB - ' ') { 117 | if (*stringA) { 118 | ++stringA; 119 | ++stringB; 120 | } 121 | else { 122 | finished = true; 123 | } 124 | } 125 | else { 126 | match = false; 127 | finished = true; 128 | } 129 | } 130 | return match; 131 | } 132 | 133 | inline int StrLength(const char *string) 134 | { 135 | #ifdef USE_STDLIB 136 | return strlen(string); 137 | 138 | #else 139 | int len = 0; 140 | for (len = 0; string[len]; len++) 141 | ; 142 | return len; 143 | 144 | #endif 145 | } 146 | int FindStringToken(const char *string, const char *token, char stopID); 147 | 148 | inline void StrCopyW(ushort *dest, const ushort *src) 149 | { 150 | int i = 0; 151 | 152 | for (; src[i]; ++i) dest[i] = src[i]; 153 | 154 | dest[i] = 0; 155 | } 156 | 157 | 158 | inline void StrAddW(ushort *dest, const ushort *src) 159 | { 160 | int destStrPos = 0; 161 | int srcStrPos = 0; 162 | while (dest[destStrPos]) ++destStrPos; 163 | while (true) { 164 | if (!src[srcStrPos]) { 165 | break; 166 | } 167 | dest[destStrPos++] = src[srcStrPos++]; 168 | } 169 | dest[destStrPos] = 0; 170 | } 171 | inline void StrCopyW(ushort *dest, const char *src) 172 | { 173 | int i = 0; 174 | 175 | for (; src[i]; ++i) dest[i] = src[i]; 176 | 177 | dest[i] = 0; 178 | } 179 | 180 | inline void StrAddW(ushort *dest, const char *src) 181 | { 182 | int destStrPos = 0; 183 | int srcStrPos = 0; 184 | while (dest[destStrPos]) ++destStrPos; 185 | while (true) { 186 | if (!src[srcStrPos]) { 187 | break; 188 | } 189 | dest[destStrPos++] = src[srcStrPos++]; 190 | } 191 | dest[destStrPos] = 0; 192 | } 193 | 194 | inline bool StrCompW(const ushort *stringA, const ushort *stringB) 195 | { 196 | bool match = true; 197 | bool finished = false; 198 | while (!finished) { 199 | if (*stringA == *stringB || *stringA == *stringB + ' ' || *stringA == *stringB - ' ') { 200 | if (*stringA) { 201 | ++stringA; 202 | ++stringB; 203 | } 204 | else { 205 | finished = true; 206 | } 207 | } 208 | else { 209 | match = false; 210 | finished = true; 211 | } 212 | } 213 | return match; 214 | } 215 | 216 | inline bool StrCompW(const ushort *stringA, const char *stringB) 217 | { 218 | bool match = true; 219 | bool finished = false; 220 | while (!finished) { 221 | if (*stringA == *stringB || *stringA == *stringB + ' ' || *stringA == *stringB - ' ') { 222 | if (*stringA) { 223 | ++stringA; 224 | ++stringB; 225 | } 226 | else { 227 | finished = true; 228 | } 229 | } 230 | else { 231 | match = false; 232 | finished = true; 233 | } 234 | } 235 | return match; 236 | } 237 | 238 | inline int StrLengthW(const ushort *string) 239 | { 240 | int len = 0; 241 | for (len = 0; string[len]; len++) 242 | ; 243 | return len; 244 | } 245 | 246 | int FindStringTokenUnicode(const ushort *string, const ushort *token, char stopID); 247 | 248 | inline void StringLowerCase(char *dest, const char *src) 249 | { 250 | int destPos = 0; 251 | int curChar = *src; 252 | if (*src) { 253 | int srcPos = 0; 254 | do { 255 | while (curChar - 'A' <= 0x19u) { 256 | destPos = srcPos; 257 | dest[destPos] = curChar + ' '; 258 | curChar = src[++srcPos]; 259 | if (!curChar) { 260 | dest[++destPos] = 0; 261 | return; 262 | } 263 | } 264 | destPos = srcPos; 265 | dest[destPos] = curChar; 266 | curChar = src[++srcPos]; 267 | } while (curChar); 268 | } 269 | dest[++destPos] = 0; 270 | } 271 | 272 | inline void StringUpperCase(char *dest, const char *src) 273 | { 274 | int destPos = 0; 275 | int curChar = *src; 276 | if (*src) { 277 | int srcPos = 0; 278 | do { 279 | while (curChar - 'a' <= 0x19u) { 280 | destPos = srcPos; 281 | dest[destPos] = curChar - ' '; 282 | curChar = src[++srcPos]; 283 | if (!curChar) { 284 | dest[++destPos] = 0; 285 | return; 286 | } 287 | } 288 | destPos = srcPos; 289 | dest[destPos] = curChar; 290 | curChar = src[++srcPos]; 291 | } while (curChar); 292 | } 293 | dest[++destPos] = 0; 294 | } 295 | 296 | void ConvertIntegerToString(char *text, int value); 297 | 298 | void GenerateMD5FromString(const char *string, int len, byte *buffer); 299 | 300 | void InitLocalizedStrings(); 301 | ushort *ReadLocalizedString(const char *stringName, const char *language, const char *filePath); 302 | 303 | inline void ReadStringLine(char *text) 304 | { 305 | char curChar = 0; 306 | 307 | int textPos = 0; 308 | while (true) { 309 | FileRead(&curChar, 1); 310 | if (curChar == '\t' || curChar == ' ') 311 | break; 312 | if (curChar == '\r' || curChar == '\n') 313 | break; 314 | if (curChar != ';') 315 | text[textPos++] = curChar; 316 | 317 | if (ReachedEndOfFile()) { 318 | text[textPos] = 0; 319 | return; 320 | } 321 | } 322 | if (curChar != '\n' && curChar != '\r') { 323 | if (ReachedEndOfFile()) { 324 | text[textPos] = 0; 325 | return; 326 | } 327 | } 328 | 329 | text[textPos] = 0; 330 | if (ReachedEndOfFile()) 331 | text[textPos] = 0; 332 | } 333 | 334 | inline void ReadStringLineUnicode(ushort *text) 335 | { 336 | int curChar = 0; 337 | byte fileBuffer[2]; 338 | 339 | int textPos = 0; 340 | while (true) { 341 | FileRead(fileBuffer, 2); 342 | curChar = fileBuffer[0] + (fileBuffer[1] << 8); 343 | if (curChar != ' ' && curChar != '\t') { 344 | if (curChar == '\r') { 345 | int pos = (int)GetFilePosition(); 346 | FileRead(fileBuffer, 2); 347 | curChar = fileBuffer[0] + (fileBuffer[1] << 8); 348 | if (curChar == '\n') 349 | break; 350 | SetFilePosition(pos); 351 | } 352 | if (curChar != ';') 353 | text[textPos++] = curChar; 354 | } 355 | else if (curChar == '\n' || curChar == '\r') 356 | break; 357 | 358 | if (ReachedEndOfFile()) { 359 | text[textPos] = 0; 360 | return; 361 | } 362 | } 363 | text[textPos] = 0; 364 | if (ReachedEndOfFile()) 365 | text[textPos] = 0; 366 | } 367 | 368 | void ReadCreditsList(const char *filePath); 369 | inline void ReadCreditsLine(char *line) 370 | { 371 | byte fileBuffer = 0; 372 | 373 | int strPos = 0; 374 | while (true) { 375 | FileRead(&fileBuffer, 1); 376 | if (fileBuffer == '\r') 377 | break; 378 | line[strPos++] = fileBuffer; 379 | if (ReachedEndOfFile()) { 380 | line[strPos] = 0; 381 | return; 382 | } 383 | } 384 | FileRead(&fileBuffer, 1); 385 | line[strPos] = 0; 386 | if (ReachedEndOfFile()) { 387 | line[strPos] = 0; 388 | return; 389 | } 390 | } 391 | 392 | #endif // !STRING_H 393 | -------------------------------------------------------------------------------- /Sonic12Decomp/Text.cpp: -------------------------------------------------------------------------------- 1 | #include "RetroEngine.hpp" 2 | 3 | TextMenu gameMenu[TEXTMENU_COUNT]; 4 | int textMenuSurfaceNo = 0; 5 | 6 | char playerListText[0x80][0x20]; 7 | 8 | void LoadTextFile(TextMenu *menu, const char *filePath) 9 | { 10 | FileInfo info; 11 | byte fileBuffer = 0; 12 | if (LoadFile(filePath, &info)) { 13 | menu->textDataPos = 0; 14 | menu->rowCount = 0; 15 | menu->entryStart[menu->rowCount] = menu->textDataPos; 16 | menu->entrySize[menu->rowCount] = 0; 17 | 18 | while (menu->textDataPos < TEXTDATA_COUNT && !ReachedEndOfFile()) { 19 | FileRead(&fileBuffer, 1); 20 | if (fileBuffer != '\n') { 21 | if (fileBuffer == '\r') { 22 | menu->rowCount++; 23 | menu->entryStart[menu->rowCount] = menu->textDataPos; 24 | menu->entrySize[menu->rowCount] = 0; 25 | } 26 | else { 27 | menu->textData[menu->textDataPos++] = fileBuffer; 28 | menu->entrySize[menu->rowCount]++; 29 | } 30 | } 31 | } 32 | 33 | menu->rowCount++; 34 | CloseFile(); 35 | } 36 | } 37 | void SetupTextMenu(TextMenu *menu, int rowCount) 38 | { 39 | menu->textDataPos = 0; 40 | menu->rowCount = rowCount; 41 | } 42 | void AddTextMenuEntry(TextMenu *menu, const char *text) 43 | { 44 | menu->entryStart[menu->rowCount] = menu->textDataPos; 45 | menu->entrySize[menu->rowCount] = 0; 46 | int textLength = StrLength(text); 47 | for (int i = 0; i < textLength;) { 48 | if (text[i] != '\0') { 49 | menu->textData[menu->textDataPos++] = text[i]; 50 | menu->entrySize[menu->rowCount]++; 51 | ++i; 52 | } 53 | else { 54 | break; 55 | } 56 | } 57 | menu->rowCount++; 58 | } 59 | void AddTextMenuEntryW(TextMenu *menu, const ushort *text) 60 | { 61 | menu->entryStart[menu->rowCount] = menu->textDataPos; 62 | menu->entrySize[menu->rowCount] = 0; 63 | int textLength = StrLengthW(text); 64 | for (int i = 0; i < textLength;) { 65 | if (text[i] != '\0') { 66 | menu->textData[menu->textDataPos++] = text[i]; 67 | menu->entrySize[menu->rowCount]++; 68 | ++i; 69 | } 70 | else { 71 | break; 72 | } 73 | } 74 | menu->rowCount++; 75 | } 76 | void SetTextMenuEntry(TextMenu *menu, const char *text, int rowID) 77 | { 78 | menu->entryStart[rowID] = menu->textDataPos; 79 | menu->entrySize[rowID] = 0; 80 | int textLength = StrLength(text); 81 | for (int i = 0; i < textLength;) { 82 | if (text[i] != '\0') { 83 | menu->textData[menu->textDataPos++] = text[i]; 84 | menu->entrySize[rowID]++; 85 | ++i; 86 | } 87 | else { 88 | break; 89 | } 90 | } 91 | } 92 | void SetTextMenuEntryW(TextMenu *menu, const ushort *text, int rowID) 93 | { 94 | menu->entryStart[rowID] = menu->textDataPos; 95 | menu->entrySize[rowID] = 0; 96 | int textLength = StrLengthW(text); 97 | for (int i = 0; i < textLength;) { 98 | if (text[i] != '\0') { 99 | menu->textData[menu->textDataPos++] = text[i]; 100 | menu->entrySize[rowID]++; 101 | ++i; 102 | } 103 | else { 104 | break; 105 | } 106 | } 107 | } 108 | void EditTextMenuEntry(TextMenu *menu, const char *text, int rowID) 109 | { 110 | int entryPos = menu->entryStart[rowID]; 111 | menu->entrySize[rowID] = 0; 112 | int textLength = StrLength(text); 113 | for (int i = 0; i < textLength;) { 114 | if (text[i] != '\0') { 115 | menu->textData[entryPos++] = text[i]; 116 | menu->entrySize[rowID]++; 117 | ++i; 118 | } 119 | else { 120 | break; 121 | } 122 | } 123 | } 124 | void LoadConfigListText(TextMenu *menu, int listNo) 125 | { 126 | FileInfo info; 127 | char strBuf[0x100]; 128 | int fileBuffer = 0; 129 | int count = 0; 130 | int strLen = 0; 131 | if (LoadFile("Data/Game/GameConfig.bin", &info)) { 132 | // Name 133 | FileRead(&strLen, 1); 134 | FileRead(&strBuf, strLen); 135 | strBuf[strLen] = 0; 136 | 137 | // About 138 | FileRead(&strLen, 1); 139 | FileRead(&strBuf, strLen); 140 | strBuf[strLen] = 0; 141 | 142 | byte buf[3]; 143 | for (int c = 0; c < 0x60; ++c) FileRead(buf, 3); 144 | 145 | // Object Names 146 | FileRead(&count, 1); 147 | for (int o = 0; o < count; ++o) { 148 | FileRead(&strLen, 1); 149 | FileRead(&strBuf, strLen); 150 | strBuf[strLen] = 0; 151 | } 152 | 153 | // Script Paths 154 | for (int s = 0; s < count; ++s) { 155 | FileRead(&strLen, 1); 156 | FileRead(&strBuf, strLen); 157 | strBuf[strLen] = 0; 158 | } 159 | 160 | // Variables 161 | FileRead(&count, 1); 162 | for (int v = 0; v < count; ++v) { 163 | //Var Name 164 | FileRead(&strLen, 1); 165 | FileRead(&strBuf, strLen); 166 | strBuf[strLen] = 0; 167 | 168 | //Var Value 169 | FileRead(&fileBuffer, 1); 170 | FileRead(&fileBuffer, 1); 171 | FileRead(&fileBuffer, 1); 172 | FileRead(&fileBuffer, 1); 173 | } 174 | 175 | // SFX Names 176 | FileRead(&count, 1); 177 | for (int s = 0; s < count; ++s) { 178 | FileRead(&strLen, 1); 179 | FileRead(&strBuf, strLen); 180 | strBuf[strLen] = 0; 181 | } 182 | // SFX Paths 183 | for (int s = 0; s < count; ++s) { 184 | FileRead(&strLen, 1); 185 | FileRead(&strBuf, strLen); 186 | strBuf[strLen] = 0; 187 | } 188 | 189 | // Players 190 | FileRead(&count, 1); 191 | for (int p = 0; p < count; ++p) { 192 | FileRead(&strLen, 1); 193 | FileRead(&strBuf, strLen); 194 | strBuf[strLen] = '\0'; 195 | 196 | if (listNo == 0) { // Player List 197 | AddTextMenuEntry(menu, strBuf); 198 | StrCopy(playerListText[p], strBuf); 199 | } 200 | } 201 | 202 | // Categories 203 | for (int c = 1; c <= 4; ++c) { 204 | int stageCnt = 0; 205 | FileRead(&stageCnt, 1); 206 | for (int s = 0; s < stageCnt; ++s) { 207 | //Stage Folder 208 | FileRead(&strLen, 1); 209 | FileRead(&strBuf, strLen); 210 | strBuf[strLen] = 0; 211 | 212 | //Stage ID 213 | FileRead(&strLen, 1); 214 | FileRead(&strBuf, strLen); 215 | strBuf[strLen] = 0; 216 | 217 | //Stage Name 218 | FileRead(&strLen, 1); 219 | FileRead(&strBuf, strLen); 220 | strBuf[strLen] = '\0'; 221 | 222 | //IsHighlighted 223 | FileRead(&fileBuffer, 1); 224 | if (listNo == c) { 225 | menu->entryHighlight[s] = fileBuffer; 226 | AddTextMenuEntry(menu, strBuf); 227 | } 228 | } 229 | } 230 | CloseFile(); 231 | } 232 | } -------------------------------------------------------------------------------- /Sonic12Decomp/Text.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TEXTSYSTEM_H 2 | #define TEXTSYSTEM_H 3 | 4 | #define TEXTDATA_COUNT (0x2800) 5 | #define TEXTENTRY_COUNT (0x200) 6 | #define TEXTMENU_COUNT (0x2) 7 | 8 | enum TextInfoTypes { TEXTINFO_TEXTDATA = 0, TEXTINFO_TEXTSIZE = 1, TEXTINFO_ROWCOUNT = 2 }; 9 | 10 | struct TextMenu { 11 | ushort textData[TEXTDATA_COUNT]; 12 | int entryStart[TEXTENTRY_COUNT]; 13 | int entrySize[TEXTENTRY_COUNT]; 14 | byte entryHighlight[TEXTENTRY_COUNT]; 15 | int textDataPos; 16 | int selection1; 17 | int selection2; 18 | ushort rowCount; 19 | ushort visibleRowCount; 20 | ushort visibleRowOffset; 21 | byte alignment; 22 | byte selectionCount; 23 | sbyte timer; 24 | }; 25 | 26 | enum TextMenuAlignments { 27 | MENU_ALIGN_LEFT, 28 | MENU_ALIGN_RIGHT, 29 | MENU_ALIGN_CENTER, 30 | }; 31 | 32 | extern TextMenu gameMenu[TEXTMENU_COUNT]; 33 | extern int textMenuSurfaceNo; 34 | 35 | extern char playerListText[0x80][0x20]; 36 | 37 | void LoadTextFile(TextMenu *menu, const char *filePath); 38 | void LoadConfigListText(TextMenu *menu, int listNo); 39 | 40 | void SetupTextMenu(TextMenu *menu, int rowCount); 41 | void AddTextMenuEntry(TextMenu *menu, const char *text); 42 | void AddTextMenuEntryW(TextMenu *menu, const ushort *text); 43 | void SetTextMenuEntry(TextMenu *menu, const char *text, int rowID); 44 | void SetTextMenuEntryW(TextMenu *menu, const ushort *text, int rowID); 45 | void EditTextMenuEntry(TextMenu *menu, const char *text, int rowID); 46 | 47 | #endif // !TEXTSYSTEM_H 48 | -------------------------------------------------------------------------------- /Sonic12Decomp/Userdata.hpp: -------------------------------------------------------------------------------- 1 | #ifndef USERDATA_H 2 | #define USERDATA_H 3 | 4 | #define GLOBALVAR_COUNT (0x100) 5 | 6 | #define ACHIEVEMENT_MAX (0x40) 7 | #define LEADERBOARD_MAX (0x80) 8 | 9 | #define SAVEDATA_MAX (0x2000) 10 | 11 | enum OnlineMenuTypes { 12 | ONLINEMENU_ACHIEVEMENTS = 0, 13 | ONLINEMENU_LEADERBOARDS = 1, 14 | }; 15 | 16 | struct Achievement { 17 | char name[0x40]; 18 | int status; 19 | }; 20 | 21 | struct LeaderboardEntry { 22 | int status; 23 | }; 24 | 25 | struct MultiplayerData { 26 | int type; 27 | int data[0x1FF]; 28 | }; 29 | 30 | extern int (*nativeFunction[16])(int, void *); 31 | extern int nativeFunctionCount; 32 | 33 | extern int globalVariablesCount; 34 | extern int globalVariables[GLOBALVAR_COUNT]; 35 | extern char globalVariableNames[GLOBALVAR_COUNT][0x20]; 36 | 37 | extern char gamePath[0x100]; 38 | extern int saveRAM[SAVEDATA_MAX]; 39 | extern Achievement achievements[ACHIEVEMENT_MAX]; 40 | extern LeaderboardEntry leaderboard[LEADERBOARD_MAX]; 41 | 42 | extern MultiplayerData multiplayerDataIN; 43 | extern MultiplayerData multiplayerDataOUT; 44 | extern int matchValueData[0x100]; 45 | extern int matchValueReadPos; 46 | extern int matchValueWritePos; 47 | 48 | extern int sendDataMethod; 49 | extern int sendCounter; 50 | 51 | inline int GetGlobalVariableByName(const char *name) 52 | { 53 | for (int v = 0; v < globalVariablesCount; ++v) { 54 | if (StrComp(name, globalVariableNames[v])) 55 | return globalVariables[v]; 56 | } 57 | return 0; 58 | } 59 | 60 | inline void SetGlobalVariableByName(const char *name, int value) 61 | { 62 | for (int v = 0; v < globalVariablesCount; ++v) { 63 | if (StrComp(name, globalVariableNames[v])) { 64 | globalVariables[v] = value; 65 | break; 66 | } 67 | } 68 | } 69 | inline int GetGlobalVariableID(const char *name) 70 | { 71 | for (int v = 0; v < globalVariablesCount; ++v) { 72 | if (StrComp(name, globalVariableNames[v])) 73 | return v; 74 | } 75 | return 0; 76 | } 77 | 78 | inline void AddNativeFunction(const char *name, int (*funcPtr)(int, void *)) 79 | { 80 | if (nativeFunctionCount > 0xF) 81 | return; 82 | SetGlobalVariableByName(name, nativeFunctionCount); 83 | nativeFunction[nativeFunctionCount++] = funcPtr; 84 | } 85 | 86 | inline bool ReadSaveRAMData() 87 | { 88 | char buffer[0x100]; 89 | #if RETRO_PLATFORM == RETRO_OSX 90 | if (!usingCWD) 91 | sprintf(buffer, "%s/SData.bin", getResourcesPath()); 92 | else 93 | sprintf(buffer, "%sSData.bin", gamePath); 94 | #else 95 | sprintf(buffer, "%sSData.bin", gamePath); 96 | #endif 97 | FileIO *saveFile = fOpen(buffer, "rb"); 98 | if (!saveFile) 99 | return false; 100 | fRead(saveRAM, 4u, SAVEDATA_MAX, saveFile); 101 | fClose(saveFile); 102 | return true; 103 | } 104 | 105 | inline bool WriteSaveRAMData() 106 | { 107 | char buffer[0x100]; 108 | #if RETRO_PLATFORM == RETRO_OSX 109 | if (!usingCWD) 110 | sprintf(buffer, "%s/SData.bin", getResourcesPath()); 111 | else 112 | sprintf(buffer, "%sSData.bin", gamePath); 113 | #else 114 | sprintf(buffer, "%sSData.bin", gamePath); 115 | #endif 116 | 117 | FileIO *saveFile = fOpen(buffer, "wb"); 118 | if (!saveFile) 119 | return false; 120 | fWrite(saveRAM, 4u, SAVEDATA_MAX, saveFile); 121 | fClose(saveFile); 122 | return true; 123 | } 124 | 125 | void InitUserdata(); 126 | void writeSettings(); 127 | void ReadUserdata(); 128 | void WriteUserdata(); 129 | 130 | void AwardAchievement(int id, int status); 131 | 132 | int SetAchievement(int achievementID, void *achDone); 133 | int SetLeaderboard(int leaderboardID, void *res); 134 | inline void LoadAchievementsMenu() { ReadUserdata(); } 135 | inline void LoadLeaderboardsMenu() { ReadUserdata(); } 136 | 137 | int Connect2PVS(int a1, void *a2); 138 | int Disconnect2PVS(int a1, void *a2); 139 | int SendEntity(int a1, void *a2); 140 | int SendValue(int a1, void *a2); 141 | int ReceiveEntity(int a1, void *a2); 142 | int ReceiveValue(int a1, void *a2); 143 | int TransmitGlobal(int a1, void *a2); 144 | 145 | void receive2PVSData(MultiplayerData *data); 146 | void receive2PVSMatchCode(int code); 147 | 148 | int ShowPromoPopup(int a1, void *a2); 149 | 150 | #endif //! USERDATA_H 151 | -------------------------------------------------------------------------------- /Sonic12Decomp/main.cpp: -------------------------------------------------------------------------------- 1 | #include "RetroEngine.hpp" 2 | 3 | int main(int argc, char *argv[]) 4 | { 5 | for (int i = 0; i < argc; ++i) { 6 | if (StrComp(argv[i], "UsingCWD")) 7 | usingCWD = true; 8 | } 9 | 10 | Engine.Init(); 11 | controllerInit(0); 12 | Engine.Run(); 13 | 14 | return 0; 15 | } 16 | -------------------------------------------------------------------------------- /Sonic12Decomp/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Sonic12Decomp.rc 4 | // 5 | 6 | // Next default values for new objects 7 | // 8 | #ifdef APSTUDIO_INVOKED 9 | #ifndef APSTUDIO_READONLY_SYMBOLS 10 | #define _APS_NEXT_RESOURCE_VALUE 102 11 | #define _APS_NEXT_COMMAND_VALUE 40001 12 | #define _APS_NEXT_CONTROL_VALUE 1001 13 | #define _APS_NEXT_SYMED_VALUE 101 14 | #endif 15 | #endif 16 | -------------------------------------------------------------------------------- /Sonic2.Vita/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ## This file is a quick tutorial on writing CMakeLists for targeting the Vita 2 | cmake_minimum_required(VERSION 2.8) 3 | 4 | ## This includes the Vita toolchain, must go before project definition 5 | # It is a convenience so you do not have to type 6 | # -DCMAKE_TOOLCHAIN_FILE=$VITASDK/share/vita.toolchain.cmake for cmake. It is 7 | # highly recommended that you include this block for all projects. 8 | if(NOT DEFINED CMAKE_TOOLCHAIN_FILE) 9 | if(DEFINED ENV{VITASDK}) 10 | set(CMAKE_TOOLCHAIN_FILE "$ENV{VITASDK}/share/vita.toolchain.cmake" CACHE PATH "toolchain file") 11 | else() 12 | message(FATAL_ERROR "Please define VITASDK to point to your SDK path!") 13 | endif() 14 | endif() 15 | 16 | ## Define project parameters here 17 | # Name of the project 18 | project(Sonic2) 19 | # This line adds Vita helper macros, must go after project definition in order 20 | # to build Vita specific artifacts (self/vpk). 21 | include("${VITASDK}/share/vita.cmake" REQUIRED) 22 | 23 | ## Configuration options for this app 24 | # Display name (under bubble in LiveArea) 25 | set(VITA_APP_NAME "Sonic 2") 26 | # Unique ID must be exactly 9 characters. Recommended: XXXXYYYYY where X = 27 | # unique string of developer and Y = a unique number for this app 28 | set(VITA_TITLEID "RSDK00003") 29 | # Optional version string to show in LiveArea's more info screen 30 | set(VITA_VERSION "01.00") 31 | 32 | ## Flags and includes for building 33 | # Note that we make sure not to overwrite previous flags 34 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -O3 -fsigned-char -fno-lto") 35 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -O3 -fsigned-char -fno-lto -fno-rtti -fno-exceptions") 36 | # Optional. You can specify more param.sfo flags this way. 37 | set(VITA_MKSFOEX_FLAGS "${VITA_MKSFOEX_FLAGS} -d PARENTAL_LEVEL=1") 38 | 39 | add_definitions(-DRETRO_GAME_SONIC=2) 40 | 41 | # Add any additional include paths here 42 | include_directories( 43 | /home/user/vitasdk/arm-vita-eabi/include 44 | ) 45 | 46 | # Add any additional library paths here 47 | # ${CMAKE_CURRENT_BINARY_DIR} lets you use any library currently being built 48 | link_directories( 49 | ${CMAKE_CURRENT_BINARY_DIR} 50 | ) 51 | 52 | ## Build and link 53 | # Add all the files needed to compile here 54 | add_executable(${PROJECT_NAME} 55 | ../Sonic12Decomp/Animation.cpp 56 | ../Sonic12Decomp/Audio.cpp 57 | ../Sonic12Decomp/Collision.cpp 58 | ../Sonic12Decomp/Debug.cpp 59 | ../Sonic12Decomp/Drawing.cpp 60 | ../Sonic12Decomp/Ini.cpp 61 | ../Sonic12Decomp/Input.cpp 62 | ../Sonic12Decomp/main.cpp 63 | ../Sonic12Decomp/Math.cpp 64 | ../Sonic12Decomp/Network.cpp 65 | ../Sonic12Decomp/Object.cpp 66 | ../Sonic12Decomp/Palette.cpp 67 | ../Sonic12Decomp/PauseMenu.cpp 68 | ../Sonic12Decomp/Reader.cpp 69 | ../Sonic12Decomp/RetroEngine.cpp 70 | ../Sonic12Decomp/RetroGameLoop.cpp 71 | ../Sonic12Decomp/Scene.cpp 72 | ../Sonic12Decomp/Scene3D.cpp 73 | ../Sonic12Decomp/Script.cpp 74 | ../Sonic12Decomp/Sprite.cpp 75 | ../Sonic12Decomp/String.cpp 76 | ../Sonic12Decomp/Text.cpp 77 | ../Sonic12Decomp/Userdata.cpp 78 | ) 79 | 80 | # Library to link to (drop the -l prefix). This will mostly be stubs. 81 | target_link_libraries(${PROJECT_NAME} 82 | SDL2 83 | vita2d 84 | vorbisfile 85 | vorbis 86 | ogg 87 | pthread 88 | SceDisplay_stub 89 | SceAudio_stub 90 | SceCtrl_stub 91 | SceSysmodule_stub 92 | SceGxm_stub 93 | SceCommonDialog_stub 94 | SceAppMgr_stub 95 | SceTouch_stub 96 | SceHid_stub 97 | SceAudio_stub 98 | SceMotion_stub 99 | m 100 | ) 101 | 102 | ## Create Vita files 103 | vita_create_self(${PROJECT_NAME}.self ${PROJECT_NAME}) 104 | # The FILE directive lets you add additional files to the VPK, the syntax is 105 | # FILE src_path dst_path_in_vpk. In this case, we add the LiveArea paths. 106 | vita_create_vpk(${PROJECT_NAME}.vpk ${VITA_TITLEID} ${PROJECT_NAME}.self 107 | VERSION ${VITA_VERSION} 108 | NAME ${VITA_APP_NAME} 109 | # FILE sce_sys/icon0.png sce_sys/icon0.png 110 | # FILE sce_sys/livearea/contents/bg.png sce_sys/livearea/contents/bg.png 111 | # FILE sce_sys/livearea/contents/startup.png sce_sys/livearea/contents/startup.png 112 | FILE sce_sys/livearea/contents/template.xml sce_sys/livearea/contents/template.xml 113 | ) 114 | -------------------------------------------------------------------------------- /Sonic2.Vita/build-internal.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | apk add --no-cache make cmake 3 | cmake . && \ 4 | make -j$(nproc) 5 | rm Sonic2 # this is not ignored by .gitignore 6 | -------------------------------------------------------------------------------- /Sonic2.Vita/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | sudo docker run \ 3 | --rm \ 4 | -v $PWD/..:/work \ 5 | -w /work/Sonic2.Vita \ 6 | vitasdk/vitasdk \ 7 | /bin/sh -C "./build-internal.sh" 8 | -------------------------------------------------------------------------------- /Sonic2.Vita/sce_sys/livearea/contents/template.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | bg.png 6 | 7 | 8 | 9 | startup.png 10 | 11 | 12 | -------------------------------------------------------------------------------- /dependencies/all/dependencies.txt: -------------------------------------------------------------------------------- 1 | IF YOU WANT TO DO NETWORKING: 2 | download asio from https://think-async.com/Asio/ and extract it into ./asio/ -------------------------------------------------------------------------------- /dependencies/mac/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "scale" : "1x", 6 | "size" : "16x16" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "scale" : "2x", 11 | "size" : "16x16" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "scale" : "1x", 16 | "size" : "32x32" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "scale" : "2x", 21 | "size" : "32x32" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "scale" : "1x", 26 | "size" : "128x128" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "scale" : "2x", 31 | "size" : "128x128" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "scale" : "1x", 36 | "size" : "256x256" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "scale" : "2x", 41 | "size" : "256x256" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "scale" : "1x", 46 | "size" : "512x512" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "scale" : "2x", 51 | "size" : "512x512" 52 | } 53 | ], 54 | "info" : { 55 | "author" : "xcode", 56 | "version" : 1 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /dependencies/mac/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /dependencies/mac/cocoaHelpers.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COCOA_HELPERS_H 2 | #define COCOA_HELPERS_H 3 | 4 | const char* getResourcesPath(void); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /dependencies/mac/cocoaHelpers.mm: -------------------------------------------------------------------------------- 1 | #ifdef __APPLE__ 2 | #include "cocoaHelpers.hpp" 3 | 4 | #import 5 | 6 | const char* getResourcesPath(void) 7 | { 8 | @autoreleasepool 9 | { 10 | NSString* resource_path = [[NSBundle mainBundle] resourcePath]; 11 | 12 | return (char*)[resource_path UTF8String]; 13 | } 14 | } 15 | #endif 16 | -------------------------------------------------------------------------------- /dependencies/mac/dependencies.txt: -------------------------------------------------------------------------------- 1 | SDL2: https://www.libsdl.org/download-2.0.php 2 | just download the appropriate development library for your compiler and place it in "./SDL2/" 3 | 4 | libogg: https://xiph.org/downloads/ (libogg) 5 | download the libogg .zip file and unzip it in "./libogg/", then build the library as a framework, and include it in the xcode proj 6 | 7 | libvorbis: https://xiph.org/downloads/ (libvorbis) 8 | download the libogg .zip file and unzip it in "./libvorbis/", then build the library as a framework, and include it in the xcode proj -------------------------------------------------------------------------------- /dependencies/windows/dependencies.txt: -------------------------------------------------------------------------------- 1 | SDL2: https://www.libsdl.org/download-2.0.php 2 | download the appropriate development library for your compiler and unzip it in "./SDL2/" 3 | 4 | libogg: https://xiph.org/downloads/ (libogg) 5 | download libogg and unzip it in "./libogg/", then build the static library 6 | 7 | libvorbis: https://xiph.org/downloads/ (libvorbis) 8 | download libvorbis and unzip it in "./libvorbis/", then build the VS2010 static library (win32/VS2010/vorbis_static.sln) -------------------------------------------------------------------------------- /networking_code_proto.py: -------------------------------------------------------------------------------- 1 | from requests import get 2 | from io import BytesIO, StringIO 3 | from string import ascii_lowercase, ascii_uppercase, punctuation, digits 4 | from math import floor, log2 5 | 6 | index = ascii_uppercase + digits 7 | #use this for base 8 | 9 | 10 | ip = get('https://api.ipify.org').text 11 | #ip = "255.255.255.255" 12 | print("IP ", ip) 13 | 14 | ipbytes = tuple(int(x) for x in ip.split('.')) 15 | ipcode = 0 16 | for i in range(4): #TODO: should this be reverse? 17 | ipcode |= ipbytes[i] << (i * 8) 18 | print("IPC", bin(ipcode), ipcode) 19 | portcode = 300 20 | print("PRT", bin(portcode), portcode) 21 | 22 | gameinfo = (10, 3, 2) #len, itembox, player 23 | gamecode = gameinfo[0] | (gameinfo[1] << 4) | (gameinfo[2] << 6) 24 | print("GME", bin(gamecode), gamecode) 25 | 26 | def getbit(i, n): 27 | return (i & (1 << n)) >> n 28 | 29 | pattern = "AABAABC" 30 | pout = 0b0 31 | 32 | 33 | ipc = 0; pc = 0; gc = 0 34 | 35 | for i in range(len(pattern) * 8): #pattern length 36 | #we can check what's at the pattern and build/read from it 37 | #we can set this up similarly in C++ with a const char* 38 | print(pout, i) 39 | c = pattern[i % (len(pattern))] 40 | if c == "A": 41 | pout |= (getbit(ipcode, ipc) << i) 42 | ipc += 1 43 | elif c == 'B': 44 | pout |= (getbit(portcode, pc) << i) 45 | pc += 1 46 | else: 47 | pout |= (getbit(gamecode, gc) << i) 48 | gc += 1 49 | print(bin(pout)) 50 | 51 | #calculate code 52 | out = [] 53 | iter = pout 54 | i = 0 55 | 56 | base = len(index) 57 | 58 | while True: 59 | if iter < (base ** (i + 1)): break 60 | i += 1 61 | while iter: 62 | out.append(iter // (base ** i)) 63 | iter %= base ** i 64 | i -= 1 65 | code = ''.join(index[x] for x in out) 66 | print(code, len(code)) 67 | #BEGIN REVERSE 68 | #get int from code 69 | past = out 70 | out = 0 71 | i = 0 72 | for x in reversed(code): 73 | out += index.index(x) * base**i 74 | i += 1 75 | print(bin(out)) 76 | #discect bits 77 | #python OFFERS a bit length function, but since we're moving this to c++, im fucking killing you 78 | ipc = 0; pc = 0; gc = 0 79 | ipcode = 0; portcode = 0; gamecode = 0 80 | for i in range(floor(log2(out)) + 1): 81 | bit = getbit(out, i) 82 | c = pattern[i % len(pattern)] #the pattern is reversed cause of how we write 83 | if c == "A": 84 | ipcode |= bit << ipc 85 | ipc += 1 86 | elif c == "B": 87 | portcode |= bit << pc 88 | pc += 1 89 | else: 90 | gamecode |= bit << gc 91 | gc += 1 92 | ipbytes = [0, 0, 0, 0] 93 | for i in range(4): 94 | ipbytes[i] = (ipcode >> (i * 8)) & 0xFF 95 | ip = f"{ipbytes[0]}.{ipbytes[1]}.{ipbytes[2]}.{ipbytes[3]}" 96 | print("IP ", ip) 97 | print("IPC", bin(ipcode), ipcode) 98 | print("PRT", bin(portcode), portcode) 99 | print("GME", bin(gamecode), gamecode) --------------------------------------------------------------------------------