├── .editorconfig ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── CMakeSettings.json ├── LICENSE.md ├── README.md ├── XenonAnalyse ├── CMakeLists.txt ├── function.cpp ├── function.h └── main.cpp ├── XenonRecomp ├── CMakeLists.txt ├── main.cpp ├── pch.h ├── recompiler.cpp ├── recompiler.h ├── recompiler_config.cpp ├── recompiler_config.h ├── test_recompiler.cpp └── test_recompiler.h ├── XenonTests ├── .gitignore └── CMakeLists.txt ├── XenonUtils ├── CMakeLists.txt ├── byteswap.h ├── disasm.cpp ├── disasm.h ├── elf.h ├── file.h ├── image.cpp ├── image.h ├── memory_mapped_file.cpp ├── memory_mapped_file.h ├── ppc_context.h ├── section.h ├── symbol.h ├── symbol_table.h ├── xbox.h ├── xbox │ ├── xam_table.inc │ └── xboxkrnl_table.inc ├── xdbf.h ├── xdbf_wrapper.cpp ├── xdbf_wrapper.h ├── xex.cpp ├── xex.h ├── xex_patcher.cpp └── xex_patcher.h └── thirdparty ├── .gitignore ├── CMakeLists.txt ├── TinySHA1 └── TinySHA1.hpp └── disasm ├── CMakeLists.txt ├── dis-asm.h ├── disasm.c ├── ppc-dis.c ├── ppc-inst.h └── ppc.h /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 4 9 | insert_final_newline = true 10 | end_of_line = lf 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Ll]og/ 33 | [Ll]ogs/ 34 | [Oo]ut/ 35 | 36 | # Visual Studio 2015/2017 cache/options directory 37 | .vs/ 38 | # Uncomment if you have tasks that create the project's static files in wwwroot 39 | #wwwroot/ 40 | 41 | # Visual Studio 2017 auto generated files 42 | Generated\ Files/ 43 | 44 | # MSTest test Results 45 | [Tt]est[Rr]esult*/ 46 | [Bb]uild[Ll]og.* 47 | 48 | # NUnit 49 | *.VisualState.xml 50 | TestResult.xml 51 | nunit-*.xml 52 | 53 | # Build Results of an ATL Project 54 | [Dd]ebugPS/ 55 | [Rr]eleasePS/ 56 | dlldata.c 57 | 58 | # Benchmark Results 59 | BenchmarkDotNet.Artifacts/ 60 | 61 | # .NET Core 62 | project.lock.json 63 | project.fragment.lock.json 64 | artifacts/ 65 | 66 | # ASP.NET Scaffolding 67 | ScaffoldingReadMe.txt 68 | 69 | # StyleCop 70 | StyleCopReport.xml 71 | 72 | # Files built by Visual Studio 73 | *_i.c 74 | *_p.c 75 | *_h.h 76 | *.ilk 77 | *.meta 78 | *.obj 79 | *.iobj 80 | *.pch 81 | *.pdb 82 | *.ipdb 83 | *.pgc 84 | *.pgd 85 | *.rsp 86 | *.sbr 87 | *.tlb 88 | *.tli 89 | *.tlh 90 | *.tmp 91 | *.tmp_proj 92 | *_wpftmp.csproj 93 | *.log 94 | *.tlog 95 | *.vspscc 96 | *.vssscc 97 | .builds 98 | *.pidb 99 | *.svclog 100 | *.scc 101 | 102 | # Chutzpah Test files 103 | _Chutzpah* 104 | 105 | # Visual C++ cache files 106 | ipch/ 107 | *.aps 108 | *.ncb 109 | *.opendb 110 | *.opensdf 111 | *.sdf 112 | *.cachefile 113 | *.VC.db 114 | *.VC.VC.opendb 115 | 116 | # Visual Studio profiler 117 | *.psess 118 | *.vsp 119 | *.vspx 120 | *.sap 121 | 122 | # Visual Studio Trace Files 123 | *.e2e 124 | 125 | # TFS 2012 Local Workspace 126 | $tf/ 127 | 128 | # Guidance Automation Toolkit 129 | *.gpState 130 | 131 | # ReSharper is a .NET coding add-in 132 | _ReSharper*/ 133 | *.[Rr]e[Ss]harper 134 | *.DotSettings.user 135 | 136 | # TeamCity is a build add-in 137 | _TeamCity* 138 | 139 | # DotCover is a Code Coverage Tool 140 | *.dotCover 141 | 142 | # AxoCover is a Code Coverage Tool 143 | .axoCover/* 144 | !.axoCover/settings.json 145 | 146 | # Coverlet is a free, cross platform Code Coverage Tool 147 | coverage*.json 148 | coverage*.xml 149 | coverage*.info 150 | 151 | # Visual Studio code coverage results 152 | *.coverage 153 | *.coveragexml 154 | 155 | # NCrunch 156 | _NCrunch_* 157 | .*crunch*.local.xml 158 | nCrunchTemp_* 159 | 160 | # MightyMoose 161 | *.mm.* 162 | AutoTest.Net/ 163 | 164 | # Web workbench (sass) 165 | .sass-cache/ 166 | 167 | # Installshield output folder 168 | [Ee]xpress/ 169 | 170 | # DocProject is a documentation generator add-in 171 | DocProject/buildhelp/ 172 | DocProject/Help/*.HxT 173 | DocProject/Help/*.HxC 174 | DocProject/Help/*.hhc 175 | DocProject/Help/*.hhk 176 | DocProject/Help/*.hhp 177 | DocProject/Help/Html2 178 | DocProject/Help/html 179 | 180 | # Click-Once directory 181 | publish/ 182 | 183 | # Publish Web Output 184 | *.[Pp]ublish.xml 185 | *.azurePubxml 186 | # Note: Comment the next line if you want to checkin your web deploy settings, 187 | # but database connection strings (with potential passwords) will be unencrypted 188 | *.pubxml 189 | *.publishproj 190 | 191 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 192 | # checkin your Azure Web App publish settings, but sensitive information contained 193 | # in these scripts will be unencrypted 194 | PublishScripts/ 195 | 196 | # NuGet Packages 197 | *.nupkg 198 | # NuGet Symbol Packages 199 | *.snupkg 200 | # The packages folder can be ignored because of Package Restore 201 | **/[Pp]ackages/* 202 | # except build/, which is used as an MSBuild target. 203 | !**/[Pp]ackages/build/ 204 | # Uncomment if necessary however generally it will be regenerated when needed 205 | #!**/[Pp]ackages/repositories.config 206 | # NuGet v3's project.json files produces more ignorable files 207 | *.nuget.props 208 | *.nuget.targets 209 | 210 | # Microsoft Azure Build Output 211 | csx/ 212 | *.build.csdef 213 | 214 | # Microsoft Azure Emulator 215 | ecf/ 216 | rcf/ 217 | 218 | # Windows Store app package directories and files 219 | AppPackages/ 220 | BundleArtifacts/ 221 | Package.StoreAssociation.xml 222 | _pkginfo.txt 223 | *.appx 224 | *.appxbundle 225 | *.appxupload 226 | 227 | # Visual Studio cache files 228 | # files ending in .cache can be ignored 229 | *.[Cc]ache 230 | # but keep track of directories ending in .cache 231 | !?*.[Cc]ache/ 232 | 233 | # Others 234 | ClientBin/ 235 | ~$* 236 | *~ 237 | *.dbmdl 238 | *.dbproj.schemaview 239 | *.jfm 240 | *.pfx 241 | *.publishsettings 242 | orleans.codegen.cs 243 | 244 | # Including strong name files can present a security risk 245 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 246 | #*.snk 247 | 248 | # Since there are multiple workflows, uncomment next line to ignore bower_components 249 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 250 | #bower_components/ 251 | 252 | # RIA/Silverlight projects 253 | Generated_Code/ 254 | 255 | # Backup & report files from converting an old project file 256 | # to a newer Visual Studio version. Backup files are not needed, 257 | # because we have git ;-) 258 | _UpgradeReport_Files/ 259 | Backup*/ 260 | UpgradeLog*.XML 261 | UpgradeLog*.htm 262 | ServiceFabricBackup/ 263 | *.rptproj.bak 264 | 265 | # SQL Server files 266 | *.mdf 267 | *.ldf 268 | *.ndf 269 | 270 | # Business Intelligence projects 271 | *.rdl.data 272 | *.bim.layout 273 | *.bim_*.settings 274 | *.rptproj.rsuser 275 | *- [Bb]ackup.rdl 276 | *- [Bb]ackup ([0-9]).rdl 277 | *- [Bb]ackup ([0-9][0-9]).rdl 278 | 279 | # Microsoft Fakes 280 | FakesAssemblies/ 281 | 282 | # GhostDoc plugin setting file 283 | *.GhostDoc.xml 284 | 285 | # Node.js Tools for Visual Studio 286 | .ntvs_analysis.dat 287 | node_modules/ 288 | 289 | # Visual Studio 6 build log 290 | *.plg 291 | 292 | # Visual Studio 6 workspace options file 293 | *.opt 294 | 295 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 296 | *.vbw 297 | 298 | # Visual Studio 6 auto-generated project file (contains which files were open etc.) 299 | *.vbp 300 | 301 | # Visual Studio 6 workspace and project file (working project files containing files to include in project) 302 | *.dsw 303 | *.dsp 304 | 305 | # Visual Studio 6 technical files 306 | *.ncb 307 | *.aps 308 | 309 | # Visual Studio LightSwitch build output 310 | **/*.HTMLClient/GeneratedArtifacts 311 | **/*.DesktopClient/GeneratedArtifacts 312 | **/*.DesktopClient/ModelManifest.xml 313 | **/*.Server/GeneratedArtifacts 314 | **/*.Server/ModelManifest.xml 315 | _Pvt_Extensions 316 | 317 | # Paket dependency manager 318 | .paket/paket.exe 319 | paket-files/ 320 | 321 | # FAKE - F# Make 322 | .fake/ 323 | 324 | # CodeRush personal settings 325 | .cr/personal 326 | 327 | # Python Tools for Visual Studio (PTVS) 328 | __pycache__/ 329 | *.pyc 330 | 331 | # Cake - Uncomment if you are using it 332 | # tools/** 333 | # !tools/packages.config 334 | 335 | # Tabs Studio 336 | *.tss 337 | 338 | # Telerik's JustMock configuration file 339 | *.jmconfig 340 | 341 | # BizTalk build output 342 | *.btp.cs 343 | *.btm.cs 344 | *.odx.cs 345 | *.xsd.cs 346 | 347 | # OpenCover UI analysis results 348 | OpenCover/ 349 | 350 | # Azure Stream Analytics local run output 351 | ASALocalRun/ 352 | 353 | # MSBuild Binary and Structured Log 354 | *.binlog 355 | 356 | # NVidia Nsight GPU debugger configuration file 357 | *.nvuser 358 | 359 | # MFractors (Xamarin productivity tool) working folder 360 | .mfractor/ 361 | 362 | # Local History for Visual Studio 363 | .localhistory/ 364 | 365 | # Visual Studio History (VSHistory) files 366 | .vshistory/ 367 | 368 | # BeatPulse healthcheck temp database 369 | healthchecksdb 370 | 371 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 372 | MigrationBackup/ 373 | 374 | # Ionide (cross platform F# VS Code tools) working folder 375 | .ionide/ 376 | 377 | # Fody - auto-generated XML schema 378 | FodyWeavers.xsd 379 | 380 | # VS Code files for those working on multiple tools 381 | .vscode/* 382 | !.vscode/settings.json 383 | !.vscode/tasks.json 384 | !.vscode/launch.json 385 | !.vscode/extensions.json 386 | *.code-workspace 387 | 388 | # Local History for Visual Studio Code 389 | .history/ 390 | 391 | # Windows Installer files from build outputs 392 | *.cab 393 | *.msi 394 | *.msix 395 | *.msm 396 | *.msp 397 | 398 | # JetBrains Rider 399 | *.sln.iml 400 | 401 | # IntelliJ IDEs 402 | .idea/ 403 | 404 | # macOS metadata 405 | *.DS_Store 406 | 407 | # CMake Files 408 | **/cmake-build-debug 409 | **/CMakeCache.txt 410 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "thirdparty/xxHash"] 2 | path = thirdparty/xxHash 3 | url = https://github.com/Cyan4973/xxHash.git 4 | [submodule "thirdparty/fmt"] 5 | path = thirdparty/fmt 6 | url = https://github.com/fmtlib/fmt.git 7 | [submodule "thirdparty/tomlplusplus"] 8 | path = thirdparty/tomlplusplus 9 | url = https://github.com/marzer/tomlplusplus.git 10 | [submodule "thirdparty/libmspack"] 11 | path = thirdparty/libmspack 12 | url = https://github.com/kyz/libmspack 13 | [submodule "thirdparty/tiny-AES-c"] 14 | path = thirdparty/tiny-AES-c 15 | url = https://github.com/kokke/tiny-AES-c.git 16 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.20) 2 | 3 | set(THIRDPARTY_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty) 4 | 5 | set(CMAKE_CXX_STANDARD 17) 6 | set(BUILD_SHARED_LIBS OFF) 7 | 8 | # Enable Hot Reload for MSVC compilers if supported. 9 | if (POLICY CMP0141) 10 | cmake_policy(SET CMP0141 NEW) 11 | set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$,$>,$<$:EditAndContinue>,$<$:ProgramDatabase>>") 12 | endif() 13 | 14 | set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") 15 | 16 | add_subdirectory(${THIRDPARTY_ROOT}) 17 | set(XENONANALYSE_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/XenonAnalyse) 18 | set(XENONUTILS_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/XenonUtils) 19 | set(XENONRECOMP_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/XenonRecomp) 20 | 21 | project ("XenonRecomp-ALL") 22 | 23 | add_subdirectory(${XENONANALYSE_ROOT}) 24 | add_subdirectory(${XENONRECOMP_ROOT}) 25 | add_subdirectory(${XENONUTILS_ROOT}) 26 | 27 | # Only tests if this is the top level project 28 | if (${CMAKE_CURRENT_SOURCE_DIR} STREQUAL ${CMAKE_SOURCE_DIR}) 29 | add_subdirectory(XenonTests) 30 | endif() 31 | -------------------------------------------------------------------------------- /CMakeSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "x64-Clang-Debug", 5 | "generator": "Ninja", 6 | "configurationType": "Debug", 7 | "buildRoot": "${projectDir}\\out\\build\\${name}", 8 | "installRoot": "${projectDir}\\out\\install\\${name}", 9 | "cmakeCommandArgs": "", 10 | "buildCommandArgs": "", 11 | "ctestCommandArgs": "", 12 | "inheritEnvironments": [ "clang_cl_x64_x64" ], 13 | "variables": [] 14 | }, 15 | { 16 | "name": "x64-Clang-Release", 17 | "generator": "Ninja", 18 | "configurationType": "RelWithDebInfo", 19 | "buildRoot": "${projectDir}\\out\\build\\${name}", 20 | "installRoot": "${projectDir}\\out\\install\\${name}", 21 | "cmakeCommandArgs": "", 22 | "buildCommandArgs": "", 23 | "ctestCommandArgs": "", 24 | "inheritEnvironments": [ "clang_cl_x64_x64" ], 25 | "variables": [] 26 | } 27 | ] 28 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2025 hedge-dev and contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | - The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GeckoRecomp 2 | 3 | GeckoRecomp is an experimental fork of XenonRecomp that attempts to convert Wii/GameCube executables into C++ code. Below is the original README. 4 | 5 | # XenonRecomp 6 | 7 | XenonRecomp is a tool that converts Xbox 360 executables into C++ code, which can then be recompiled for any platform. Currently, it only supports x86 platforms due to the use of x86 intrinsics. 8 | 9 | This project was heavily inspired by [N64: Recompiled](https://github.com/N64Recomp/N64Recomp), a similar tool for N64 executables. 10 | 11 | ## Implementation Details 12 | 13 | ### Instructions 14 | 15 | The instructions are directly converted without any effort to make them resemble decompiled code, meaning the output is not very human-readable. The CPU state is passed as an argument to every PPC function, which includes definitions for every PPC register and their current values at the time of execution. The second argument is the base address pointer, as the Xbox 360 CPU uses 32-bit pointers. 16 | 17 | A good amount of PPC instructions are implemented, with missing ones primarily being variants of already implemented instructions. Some instructions, like the D3D unpack/pack instructions, do not support all operand types. When a missing case is encountered, a warning is generated, or a debug break is inserted into the converted C++ code. 18 | 19 | The instruction implementations operate on little-endian values. However, since the Xbox 360 is a big-endian machine, the memory load instructions swap endianness when reading values, and memory store instructions reverse it to big-endian before writing. All the memory loads and stores are marked volatile to prevent Clang from doing unsafe code reordering. 20 | 21 | Vector registers' endianness handling is more complicated. Instead of swapping individual 32-bit elements, the recompiler chooses to reverse the entire 16-byte vector. Instructions must account for this reversed order, such as using the WZY components instead of XYZ in dot products or requiring reversed arguments for vector pack instructions. 22 | 23 | The FPU expects denormalized numbers to remain unmodified, while VMX instructions always flush them. This is managed by storing the current floating-point state in the CPU state struct and enabling or disabling denormal flushing as necessary before executing each instruction. 24 | 25 | Most VMX instructions are implemented using x86 intrinsics. Luckily, the number of AVX intrinsics used is relatively low, so adding support for other architectures using libraries like [SIMD Everywhere](https://github.com/simd-everywhere/simde) might be possible. 26 | 27 | ### MMIO 28 | 29 | MMIO, which is typically used for hardware operations such as XMA decoding, is currently unimplemented. There is an unfinished attempt to implement MMIO, but supporting it may be non-trivial and could require advanced analysis of instructions. 30 | 31 | ### Indirect Functions 32 | 33 | Virtual function calls are resolved by creating a "perfect hash table" at runtime, where dereferencing a 64-bit pointer (using the original instruction address multiplied by 2) gives the address of the recompiled function. This was previously implemented by creating an 8 GB virtual allocation, but it had too much memory pressure. Now it relies on function addresses being placed after the valid XEX memory region in the base memory pointer. These regions are exported as macros in the output `ppc_config.h` file. 34 | 35 | ### Jump Tables 36 | 37 | Jump tables, at least in older Xbox 360 binaries, often have predictable assembly patterns, making them easy to detect statically without needing a virtual machine. XenonAnalyse has logic for detecting jump tables in Sonic Unleashed, though variations in other games (likely due to updates in the Xbox 360 compiler) may require modifications to the detection logic. Currently, there is no fully generic solution for handling jump tables, so updates to the detection logic may be needed for other games. 38 | 39 | The typical way to find jump tables is by searching for the `mtctr r0` instruction. It will almost always be followed with a `bctr`, with the previous instructions computing the jump address. 40 | 41 | XenonAnalyse generates a TOML file containing detected jump tables, which can be referenced in the main TOML config file. This allows the recompiler to generate real switch cases for these jump tables. 42 | 43 | ### Function Boundary Analysis 44 | 45 | XenonAnalyse includes a function boundary analyzer that works well in most cases. Functions with stack space have their boundaries defined in the `.pdata` segment of the XEX. For functions not found in this segment, the analyzer detects the start of functions by searching for branch link instructions, and determines their length via static analysis. 46 | 47 | However, the analyzer struggles with functions containing jump tables, since they look like tail calls without enough information. While there is currently no solution for this, it might be relatively simple to extend the function analyzer to account for jump tables defined in the TOML file. As a workaround, the recompiler TOML file allows users to manually define function boundaries. 48 | 49 | ### Exceptions 50 | 51 | The recompiler currently does not support exceptions. This is challenging due to the use of the link register and the fact that exception handlers can jump to arbitrary code locations. 52 | 53 | ### setjmp 54 | 55 | `setjmp` and `longjmp` are implemented by redirecting them to native implementations. Thanks to the Xbox 360's large number of vector registers, the guest CPU state struct is large enough to hold the x86 CPU state and potentially states from other architectures. 56 | 57 | ### Optimizations 58 | 59 | Since Xbox 360 binaries typically follow a stable ABI, we can make certain assumptions about code structure, allowing the Clang compiler to generate better code. Several optimization options are available in the recompiler, but it's recommended to test them only after having a successfully functioning recompilation. 60 | 61 | The link register can be skipped assuming the game does not utilize exceptions, as the whole process of recompilation already takes care of function return behavior. 62 | 63 | The following registers, assuming the game doesn't violate the ABI, can be safely converted into local variables, as they never leave the function scope: 64 | * Count register 65 | * XER 66 | * Reserved register 67 | * Condition registers 68 | * Non argument registers 69 | * Non volatile registers 70 | 71 | The local variable optimization particularly introduces the most improvements, as the calls to the register restore/save functions can be completely removed, and the redundant stores to the PPC context struct can be eliminated. In [Unleashed Recompiled](https://github.com/hedge-dev/UnleashedRecomp), the executable size decreases by around 20 MB with these optimizations, and frame times are reduced by several milliseconds. 72 | 73 | ### Patch Mechanisms 74 | 75 | XenonRecomp defines PPC functions in a way that makes them easy to hook, using techniques in the Clang compiler. By aliasing a PPC function to an "implementation function" and marking the original function as weakly linked, users can override it with a custom implementation while retaining access to the original function: 76 | 77 | ```cpp 78 | PPC_FUNC_IMPL(__imp__sub_XXXXXXXX); 79 | PPC_FUNC(sub_XXXXXXXX) 80 | { 81 | __imp__sub_XXXXXXXX(ctx, base); 82 | } 83 | ``` 84 | 85 | Additionally, mid-asm hooks can be inserted directly into the translated C++ code at specific instruction addresses. The recompiler inserts these function calls, and users are responsible for implementing them in their recompilation project. The linker resolves them during compilation. 86 | 87 | ## Usage 88 | 89 | ### XenonAnalyse 90 | 91 | XenonAnalyse, when used as a command-line application, allows an XEX file to be passed as an input argument to output a TOML file containing all the detected jump tables in the executable: 92 | 93 | ``` 94 | XenonAnalyse [input XEX file path] [output jump table TOML file path] 95 | ``` 96 | 97 | However, as explained in the earlier sections, due to variations between games, additional support may be needed to handle different patterns. 98 | 99 | [An example jump table TOML file can be viewed in the Unleashed Recompiled repository.](https://github.com/hedge-dev/UnleashedRecomp/blob/main/UnleashedRecompLib/config/SWA_switch_tables.toml) 100 | 101 | ### XenonRecomp 102 | 103 | XenonRecomp accepts a TOML file with recompiler configurations and the path to the `ppc_context.h` file located in the XenonUtils directory: 104 | 105 | ``` 106 | XenonRecomp [input TOML file path] [input PPC context header file path] 107 | ``` 108 | 109 | [An example recompiler TOML file can be viewed in the Unleashed Recompiled repository.](https://github.com/hedge-dev/UnleashedRecomp/blob/main/UnleashedRecompLib/config/SWA.toml) 110 | 111 | #### Main 112 | 113 | ```toml 114 | [main] 115 | file_path = "../private/default.xex" 116 | patch_file_path = "../private/default.xexp" 117 | patched_file_path = "../private/default_patched.xex" 118 | out_directory_path = "../ppc" 119 | switch_table_file_path = "SWA_switch_tables.toml" 120 | ``` 121 | 122 | All the paths are relative to the directory where the TOML file is stored. 123 | 124 | Property|Description 125 | -|- 126 | file_path|Path to the XEX file. 127 | patch_file_path|Path to the XEXP file. This is not required if the game has no title updates. 128 | patched_file_path|Path to the patched XEX file. XenonRecomp will create this file automatically if it is missing and reuse it in subsequent recompilations. It does nothing if no XEXP file is specified. You can pass this output file to XenonAnalyse. 129 | out_directory_path|Path to the directory that will contain the output C++ code. This directory must exist before running the recompiler. 130 | switch_table_file_path|Path to the TOML file containing the jump table definitions. The recompiler uses this file to convert jump tables to real switch cases. 131 | 132 | #### Optimizations 133 | 134 | ```toml 135 | skip_lr = false 136 | skip_msr = false 137 | ctr_as_local = false 138 | xer_as_local = false 139 | reserved_as_local = false 140 | cr_as_local = false 141 | non_argument_as_local = false 142 | non_volatile_as_local = false 143 | ``` 144 | 145 | Enables or disables various optimizations explained earlier in the documentation. It is recommended not to enable these optimizations until you have a successfully running recompilation. 146 | 147 | #### Register Restore & Save Functions 148 | 149 | ```toml 150 | restgprlr_14_address = 0x831B0B40 151 | savegprlr_14_address = 0x831B0AF0 152 | restfpr_14_address = 0x831B144C 153 | savefpr_14_address = 0x831B1400 154 | restvmx_14_address = 0x831B36E8 155 | savevmx_14_address = 0x831B3450 156 | restvmx_64_address = 0x831B377C 157 | savevmx_64_address = 0x831B34E4 158 | ``` 159 | 160 | Xbox 360 binaries feature specialized register restore & save functions that act similarly to switch case fallthroughs. Every function that utilizes non-volatile registers either has an inlined version of these functions or explicitly calls them. The recompiler requires the starting address of each restore/save function in the TOML file to recompile them correctly. These functions could likely be auto-detected, but there is currently no mechanism for it. 161 | 162 | Property|Description|Byte Pattern 163 | -|-|- 164 | restgprlr_14_address|Start address of the `__restgprlr_14` function. It starts with `ld r14, -0x98(r1)`, repeating the same operation for the rest of the non-volatile registers and restoring the link register at the end.|`e9 c1 ff 68` 165 | savegprlr_14_address|Start address of the `__savegprlr_14` function. It starts with `std r14, -0x98(r1)`, repeating the same operation for the rest of the non-volatile registers and saving the link register at the end.|`f9 c1 ff 68` 166 | restfpr_14_address|Start address of the `__restfpr_14` function. It starts with `lfd f14, -0x90(r12)`, repeating the same operation for the rest of the non-volatile FPU registers.|`c9 cc ff 70` 167 | savefpr_14_address|Start address of the `__savefpr_14` function. It starts with `stfd r14, -0x90(r12)`, repeating the same operation for the rest of the non-volatile FPU registers.|`d9 cc ff 70` 168 | restvmx_14_address|Start address of the `__restvmx_14` function. It starts with `li r11, -0x120` and `lvx v14, r11, r12`, repeating the same operation for the rest of the non-volatile VMX registers until `v31`.|`39 60 fe e0 7d cb 60 ce` 169 | savevmx_14_address|Start address of the `__savevmx_14` function. It starts with `li r11, -0x120` and `stvx v14, r11, r12`, repeating the same operation for the rest of the non-volatile VMX registers until `v31`.|`39 60 fe e0 7d cb 61 ce` 170 | restvmx_64_address|Start address of the `__restvmx_64` function. It starts with `li r11, -0x400` and `lvx128 v64, r11, r12`, repeating the same operation for the rest of the non-volatile VMX registers.|`39 60 fc 00 10 0b 60 cb` 171 | savevmx_64_address|Start address of the `__savevmx_64` function. It starts with `li r11, -0x400` and `stvx128 v64, r11, r12`, repeating the same operation for the rest of the non-volatile VMX registers.|`39 60 fc 00 10 0b 61 cb` 172 | 173 | #### longjmp & setjmp 174 | 175 | ```toml 176 | longjmp_address = 0x831B6790 177 | setjmp_address = 0x831B6AB0 178 | ``` 179 | 180 | These are addresses for the `longjmp` and `setjmp` functions in the executable. The recompiler directly redirects these functions to native versions. The implementation of these functions might vary between games. In some cases, you might find `longjmp` by looking for calls to `RtlUnwind`, and `setjmp` typically appears just after it. 181 | 182 | If the game does not use these functions, you can remove the properties from the TOML file. 183 | 184 | #### Explicit Function Boundaries 185 | 186 | ```toml 187 | functions = [ 188 | { address = 0x824E7EF0, size = 0x98 }, 189 | { address = 0x824E7F28, size = 0x60 }, 190 | ] 191 | ``` 192 | 193 | You can define function boundaries explicitly using the `functions` property if XenonAnalyse fails to analyze them correctly, for example, with functions containing jump tables. 194 | 195 | #### Invalid Instruction Skips 196 | 197 | ```toml 198 | invalid_instructions = [ 199 | { data = 0x00000000, size = 4 }, # Padding 200 | { data = 0x831B1C90, size = 8 }, # C++ Frame Handler 201 | { data = 0x8324B3BC, size = 8 }, # C Specific Frame Handler 202 | { data = 0x831C8B50, size = 8 }, 203 | { data = 0x00485645, size = 44 } # End of .text 204 | ] 205 | ``` 206 | 207 | In the `invalid_instructions` property, you can define 32-bit integer values that instruct the recompiler to skip over certain bytes when it encounters them. For example, in Unleashed Recompiled, these are used to skip over exception handling data, which is placed between functions but is not valid code. 208 | 209 | #### Mid-asm Hooks 210 | 211 | ```toml 212 | [[midasm_hook]] 213 | name = "IndexBufferLengthMidAsmHook" 214 | address = 0x82E26244 215 | registers = ["r3"] 216 | ``` 217 | 218 | ```cpp 219 | void IndexBufferLengthMidAsmHook(PPCRegister& r3) 220 | { 221 | // ... 222 | } 223 | ``` 224 | 225 | You can define multiple mid-asm hooks in the TOML file, allowing the recompiler to insert function calls at specified addresses. When implementing them in your recompilation project, the linker will resolve the calls automatically. 226 | 227 | Property|Description 228 | -|- 229 | name|Function name of the mid-asm hook. You can reuse function names to place the same implementation at multiple addresses. Otherwise, unique implementations must have unique names. 230 | address|Address of the instruction where the function call will be placed. This does not overwrite the instruction at the specified address. 231 | registers|Registers to pass as arguments to the mid-asm hook. This is a list of registers because the local variable optimization does not keep optimized registers within the PPC context struct. 232 | return|Set to `true` to indicate that the function where the hook was inserted should immediately return after calling the mid-asm hook. 233 | return_on_true|Set to `true` to indicate that the function should return if the mid-asm hook call returns `true`. 234 | return_on_false|Set to `true` to indicate that the function should return if the mid-asm hook call returns `false`. 235 | jump_address|The address to jump to immediately after calling the mid-asm hook. The address must be within the same function where the hook was placed. 236 | jump_address_on_true|The address to jump to if the mid-asm hook returns `true`. The address must be within the same function where the hook was placed. 237 | jump_address_on_false|The address to jump to if the mid-asm hook returns `false`. The address must be within the same function where the hook was placed. 238 | after_instruction|Set to `true` to place the mid-asm hook immediately after the instruction, instead of before. 239 | 240 | Certain properties are mutually exclusive. For example, you cannot use both `return` and `jump_address`, and direct or conditional returns/jumps cannot be mixed. The recompiler is going to show warnings if this is not followed. 241 | 242 | ### Tests 243 | 244 | XenonRecomp can recompile Xenia's PPC tests and execute them through the XenonTests project in the repository. After building the tests using Xenia's build system, XenonRecomp can process the `src/xenia/cpu/ppc/testing/bin` directory as input, generating C++ files in the specified output directory: 245 | 246 | ``` 247 | XenonRecomp [input testing directory path] [input PPC context header file path] [output directory path] 248 | ``` 249 | 250 | Once the files are generated, refresh XenonTests' CMake cache to make them appear in the project. The tests can then be executed to compare the results of instructions against the expected values. 251 | 252 | ## Building 253 | 254 | The project requires CMake 3.20 or later and Clang 18 or later to build. Since the repository includes submodules, ensure you clone it recursively. 255 | 256 | Compilers other than Clang have not been tested and are not recommended, including for recompilation output. The project relies on compiler-specific intrinsics and techniques that may not function correctly on other compilers, and many optimization methods depend on Clang's code generation. 257 | 258 | On Windows, you can use the clang-cl toolset and open the project in Visual Studio's CMake integration. 259 | 260 | ## Special Thanks 261 | 262 | This project could not have been possible without the [Xenia](https://github.com/xenia-project/xenia) emulator, as many parts of the CPU code conversion process has been implemented by heavily referencing its PPC code translator. The project also uses code from [Xenia Canary](https://github.com/xenia-canary/xenia-canary) to patch XEX binaries. 263 | -------------------------------------------------------------------------------- /XenonAnalyse/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # cmake_minimum_required (VERSION 3.16) 2 | 3 | project("XenonAnalyse") 4 | 5 | add_executable(XenonAnalyse 6 | "main.cpp" 7 | "function.cpp") 8 | 9 | target_link_libraries(XenonAnalyse PRIVATE XenonUtils fmt::fmt) 10 | 11 | add_library(LibXenonAnalyse "function.cpp") 12 | target_include_directories(LibXenonAnalyse PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) 13 | target_link_libraries(LibXenonAnalyse PUBLIC XenonUtils) 14 | 15 | -------------------------------------------------------------------------------- /XenonAnalyse/function.cpp: -------------------------------------------------------------------------------- 1 | #include "function.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | size_t Function::SearchBlock(size_t address) const 10 | { 11 | if (address < base) 12 | { 13 | return -1; 14 | } 15 | 16 | for (size_t i = 0; i < blocks.size(); i++) 17 | { 18 | const auto& block = blocks[i]; 19 | const auto begin = base + block.base; 20 | const auto end = begin + block.size; 21 | 22 | if (begin != end) 23 | { 24 | if (address >= begin && address < end) 25 | { 26 | return i; 27 | } 28 | } 29 | else // fresh block 30 | { 31 | if (address == begin) 32 | { 33 | return i; 34 | } 35 | } 36 | } 37 | 38 | return -1; 39 | } 40 | 41 | Function Function::Analyze(const void* code, size_t size, size_t base) 42 | { 43 | Function fn{ base, 0 }; 44 | 45 | if (*((uint32_t*)code + 1) == 0x04000048) // shifted ptr tail call 46 | { 47 | fn.size = 0x8; 48 | return fn; 49 | } 50 | 51 | auto& blocks = fn.blocks; 52 | blocks.reserve(8); 53 | blocks.emplace_back(); 54 | 55 | const auto* data = (uint32_t*)code; 56 | const auto* dataStart = data; 57 | const auto* dataEnd = (uint32_t*)((uint8_t*)code + size); 58 | std::vector blockStack{}; 59 | blockStack.reserve(32); 60 | blockStack.emplace_back(); 61 | 62 | #define RESTORE_DATA() if (!blockStack.empty()) data = (dataStart + ((blocks[blockStack.back()].base + blocks[blockStack.back()].size) / sizeof(*data))) - 1; // continue adds one 63 | 64 | // TODO: Branch fallthrough 65 | for (; data <= dataEnd ; ++data) 66 | { 67 | const size_t addr = base + ((data - dataStart) * sizeof(*data)); 68 | if (blockStack.empty()) 69 | { 70 | break; // it's hideover 71 | } 72 | 73 | auto& curBlock = blocks[blockStack.back()]; 74 | DEBUG(const auto blockBase = curBlock.base); 75 | const uint32_t instruction = ByteSwap(*data); 76 | 77 | const uint32_t op = PPC_OP(instruction); 78 | const uint32_t xop = PPC_XOP(instruction); 79 | const uint32_t isLink = PPC_BL(instruction); // call 80 | 81 | ppc_insn insn; 82 | ppc::Disassemble(data, addr, insn); 83 | 84 | // Sanity check 85 | assert(addr == base + curBlock.base + curBlock.size); 86 | if (curBlock.projectedSize != -1 && curBlock.size >= curBlock.projectedSize) // fallthrough 87 | { 88 | blockStack.pop_back(); 89 | RESTORE_DATA(); 90 | continue; 91 | } 92 | 93 | curBlock.size += 4; 94 | if (op == PPC_OP_BC) // conditional branches all originate from one opcode, thanks RISC 95 | { 96 | if (isLink) // just a conditional call, nothing to see here 97 | { 98 | continue; 99 | } 100 | 101 | // TODO: carry projections over to false 102 | curBlock.projectedSize = -1; 103 | blockStack.pop_back(); 104 | 105 | // TODO: Handle absolute branches? 106 | assert(!PPC_BA(instruction)); 107 | const size_t branchDest = addr + PPC_BD(instruction); 108 | 109 | // true/false paths 110 | // left block: false case 111 | // right block: true case 112 | const size_t lBase = (addr - base) + 4; 113 | const size_t rBase = (addr + PPC_BD(instruction)) - base; 114 | 115 | // these will be -1 if it's our first time seeing these blocks 116 | auto lBlock = fn.SearchBlock(base + lBase); 117 | 118 | if (lBlock == -1) 119 | { 120 | blocks.emplace_back(lBase, 0).projectedSize = rBase - lBase; 121 | lBlock = blocks.size() - 1; 122 | 123 | // push this first, this gets overriden by the true case as it'd be further away 124 | DEBUG(blocks[lBlock].parent = blockBase); 125 | blockStack.emplace_back(lBlock); 126 | } 127 | 128 | size_t rBlock = fn.SearchBlock(base + rBase); 129 | if (rBlock == -1) 130 | { 131 | blocks.emplace_back(branchDest - base, 0); 132 | rBlock = blocks.size() - 1; 133 | 134 | DEBUG(blocks[rBlock].parent = blockBase); 135 | blockStack.emplace_back(rBlock); 136 | } 137 | 138 | RESTORE_DATA(); 139 | } 140 | else if (op == PPC_OP_B || instruction == 0 || (op == PPC_OP_CTR && (xop == 16 || xop == 528))) // b, blr, end padding 141 | { 142 | if (!isLink) 143 | { 144 | blockStack.pop_back(); 145 | 146 | if (op == PPC_OP_B) 147 | { 148 | assert(!PPC_BA(instruction)); 149 | const size_t branchDest = addr + PPC_BI(instruction); 150 | 151 | const size_t branchBase = branchDest - base; 152 | const size_t branchBlock = fn.SearchBlock(branchDest); 153 | 154 | if (branchDest < base) 155 | { 156 | // Branches before base are just tail calls, no need to chase after those 157 | RESTORE_DATA(); 158 | continue; 159 | } 160 | 161 | // carry over our projection if blocks are next to each other 162 | const bool isContinuous = branchBase == curBlock.base + curBlock.size; 163 | size_t sizeProjection = (size_t)-1; 164 | 165 | if (curBlock.projectedSize != -1 && isContinuous) 166 | { 167 | sizeProjection = curBlock.projectedSize - curBlock.size; 168 | } 169 | 170 | if (branchBlock == -1) 171 | { 172 | blocks.emplace_back(branchBase, 0, sizeProjection); 173 | 174 | blockStack.emplace_back(blocks.size() - 1); 175 | 176 | DEBUG(blocks.back().parent = blockBase); 177 | RESTORE_DATA(); 178 | continue; 179 | } 180 | } 181 | else if (op == PPC_OP_CTR) 182 | { 183 | // 5th bit of BO tells cpu to ignore the counter, which is a blr/bctr otherwise it's conditional 184 | const bool conditional = !(PPC_BO(instruction) & 0x10); 185 | if (conditional) 186 | { 187 | // right block's just going to return 188 | const size_t lBase = (addr - base) + 4; 189 | size_t lBlock = fn.SearchBlock(lBase); 190 | if (lBlock == -1) 191 | { 192 | blocks.emplace_back(lBase, 0); 193 | lBlock = blocks.size() - 1; 194 | 195 | DEBUG(blocks[lBlock].parent = blockBase); 196 | blockStack.emplace_back(lBlock); 197 | RESTORE_DATA(); 198 | continue; 199 | } 200 | } 201 | } 202 | 203 | RESTORE_DATA(); 204 | } 205 | } 206 | else if (insn.opcode == nullptr) 207 | { 208 | blockStack.pop_back(); 209 | RESTORE_DATA(); 210 | } 211 | } 212 | 213 | // Sort and invalidate discontinuous blocks 214 | if (blocks.size() > 1) 215 | { 216 | std::sort(blocks.begin(), blocks.end(), [](const Block& a, const Block& b) 217 | { 218 | return a.base < b.base; 219 | }); 220 | 221 | size_t discontinuity = -1; 222 | for (size_t i = 0; i < blocks.size() - 1; i++) 223 | { 224 | if (blocks[i].base + blocks[i].size >= blocks[i + 1].base) 225 | { 226 | continue; 227 | } 228 | 229 | discontinuity = i + 1; 230 | break; 231 | } 232 | 233 | if (discontinuity != -1) 234 | { 235 | blocks.erase(blocks.begin() + discontinuity, blocks.end()); 236 | } 237 | } 238 | 239 | fn.size = 0; 240 | for (const auto& block : blocks) 241 | { 242 | // pick the block furthest away 243 | fn.size = std::max(fn.size, block.base + block.size); 244 | } 245 | return fn; 246 | } 247 | -------------------------------------------------------------------------------- /XenonAnalyse/function.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #ifdef _DEBUG 7 | #define DEBUG(X) X 8 | #else 9 | #define DEBUG(X) 10 | #endif 11 | 12 | struct Function 13 | { 14 | struct Block 15 | { 16 | size_t base{}; 17 | size_t size{}; 18 | size_t projectedSize{ static_cast(-1) }; // scratch 19 | DEBUG(size_t parent{}); 20 | 21 | Block() 22 | { 23 | } 24 | 25 | Block(size_t base, size_t size) 26 | : base(base), size(size) 27 | { 28 | } 29 | 30 | Block(size_t base, size_t size, size_t projectedSize) 31 | : base(base), size(size), projectedSize(projectedSize) 32 | { 33 | } 34 | }; 35 | 36 | size_t base{}; 37 | size_t size{}; 38 | std::vector blocks{}; 39 | 40 | Function() 41 | { 42 | } 43 | 44 | Function(size_t base, size_t size) 45 | : base(base), size(size) 46 | { 47 | } 48 | 49 | size_t SearchBlock(size_t address) const; 50 | static Function Analyze(const void* code, size_t size, size_t base); 51 | }; 52 | -------------------------------------------------------------------------------- /XenonAnalyse/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "function.h" 9 | 10 | #define SWITCH_ABSOLUTE 0 11 | #define SWITCH_COMPUTED 1 12 | #define SWITCH_BYTEOFFSET 2 13 | #define SWITCH_SHORTOFFSET 3 14 | 15 | struct SwitchTable 16 | { 17 | std::vector labels{}; 18 | size_t base{}; 19 | size_t defaultLabel{}; 20 | uint32_t r{}; 21 | uint32_t type{}; 22 | }; 23 | 24 | void ReadTable(Image& image, SwitchTable& table) 25 | { 26 | uint32_t pOffset; 27 | ppc_insn insn; 28 | auto* code = (uint32_t*)image.Find(table.base); 29 | ppc::Disassemble(code, table.base, insn); 30 | pOffset = insn.operands[1] << 16; 31 | 32 | ppc::Disassemble(code + 1, table.base + 4, insn); 33 | pOffset += insn.operands[2]; 34 | 35 | if (table.type == SWITCH_ABSOLUTE) 36 | { 37 | const auto* offsets = (be*)image.Find(pOffset); 38 | for (size_t i = 0; i < table.labels.size(); i++) 39 | { 40 | table.labels[i] = offsets[i]; 41 | } 42 | } 43 | else if (table.type == SWITCH_COMPUTED) 44 | { 45 | uint32_t base; 46 | uint32_t shift; 47 | const auto* offsets = (uint8_t*)image.Find(pOffset); 48 | 49 | ppc::Disassemble(code + 4, table.base + 0x10, insn); 50 | base = insn.operands[1] << 16; 51 | 52 | ppc::Disassemble(code + 5, table.base + 0x14, insn); 53 | base += insn.operands[2]; 54 | 55 | ppc::Disassemble(code + 3, table.base + 0x0C, insn); 56 | shift = insn.operands[2]; 57 | 58 | for (size_t i = 0; i < table.labels.size(); i++) 59 | { 60 | table.labels[i] = base + (offsets[i] << shift); 61 | } 62 | } 63 | else if (table.type == SWITCH_BYTEOFFSET || table.type == SWITCH_SHORTOFFSET) 64 | { 65 | if (table.type == SWITCH_BYTEOFFSET) 66 | { 67 | const auto* offsets = (uint8_t*)image.Find(pOffset); 68 | uint32_t base; 69 | 70 | ppc::Disassemble(code + 3, table.base + 0x0C, insn); 71 | base = insn.operands[1] << 16; 72 | 73 | ppc::Disassemble(code + 4, table.base + 0x10, insn); 74 | base += insn.operands[2]; 75 | 76 | for (size_t i = 0; i < table.labels.size(); i++) 77 | { 78 | table.labels[i] = base + offsets[i]; 79 | } 80 | } 81 | else if (table.type == SWITCH_SHORTOFFSET) 82 | { 83 | const auto* offsets = (be*)image.Find(pOffset); 84 | uint32_t base; 85 | 86 | ppc::Disassemble(code + 4, table.base + 0x10, insn); 87 | base = insn.operands[1] << 16; 88 | 89 | ppc::Disassemble(code + 5, table.base + 0x14, insn); 90 | base += insn.operands[2]; 91 | 92 | for (size_t i = 0; i < table.labels.size(); i++) 93 | { 94 | table.labels[i] = base + offsets[i]; 95 | } 96 | } 97 | } 98 | else 99 | { 100 | assert(false); 101 | } 102 | } 103 | 104 | void ScanTable(const uint32_t* code, size_t base, SwitchTable& table) 105 | { 106 | ppc_insn insn; 107 | uint32_t cr{ (uint32_t)-1 }; 108 | for (int i = 0; i < 32; i++) 109 | { 110 | ppc::Disassemble(&code[-i], base - (4 * i), insn); 111 | if (insn.opcode == nullptr) 112 | { 113 | continue; 114 | } 115 | 116 | if (cr == -1 && (insn.opcode->id == PPC_INST_BGT || insn.opcode->id == PPC_INST_BGTLR || insn.opcode->id == PPC_INST_BLE || insn.opcode->id == PPC_INST_BLELR)) 117 | { 118 | cr = insn.operands[0]; 119 | if (insn.opcode->operands[1] != 0) 120 | { 121 | table.defaultLabel = insn.operands[1]; 122 | } 123 | } 124 | else if (cr != -1) 125 | { 126 | if (insn.opcode->id == PPC_INST_CMPLWI && insn.operands[0] == cr) 127 | { 128 | table.r = insn.operands[1]; 129 | table.labels.resize(insn.operands[2] + 1); 130 | table.base = base; 131 | break; 132 | } 133 | } 134 | } 135 | } 136 | 137 | void MakeMask(const uint32_t* instructions, size_t count) 138 | { 139 | ppc_insn insn; 140 | for (size_t i = 0; i < count; i++) 141 | { 142 | ppc::Disassemble(&instructions[i], 0, insn); 143 | fmt::println("0x{:X}, // {}", ByteSwap(insn.opcode->opcode | (insn.instruction & insn.opcode->mask)), insn.opcode->name); 144 | } 145 | } 146 | 147 | void* SearchMask(const void* source, const uint32_t* compare, size_t compareCount, size_t size) 148 | { 149 | assert(size % 4 == 0); 150 | uint32_t* src = (uint32_t*)source; 151 | size_t count = size / 4; 152 | ppc_insn insn; 153 | 154 | for (size_t i = 0; i < count; i++) 155 | { 156 | size_t c = 0; 157 | for (c = 0; c < compareCount; c++) 158 | { 159 | ppc::Disassemble(&src[i + c], 0, insn); 160 | if (insn.opcode == nullptr || insn.opcode->id != compare[c]) 161 | { 162 | break; 163 | } 164 | } 165 | 166 | if (c == compareCount) 167 | { 168 | return &src[i]; 169 | } 170 | } 171 | 172 | return nullptr; 173 | } 174 | 175 | static std::string out; 176 | 177 | template 178 | static void println(fmt::format_string fmt, Args&&... args) 179 | { 180 | fmt::vformat_to(std::back_inserter(out), fmt.get(), fmt::make_format_args(args...)); 181 | out += '\n'; 182 | }; 183 | 184 | int main(int argc, char** argv) 185 | { 186 | if (argc < 3) 187 | { 188 | printf("Usage: XenonAnalyse [input XEX file path] [output jump table TOML file path]"); 189 | return EXIT_SUCCESS; 190 | } 191 | 192 | const auto file = LoadFile(argv[1]); 193 | auto image = Image::ParseImage(file.data(), file.size()); 194 | 195 | auto printTable = [&](const SwitchTable& table) 196 | { 197 | println("[[switch]]"); 198 | println("base = 0x{:X}", table.base); 199 | println("r = {}", table.r); 200 | println("default = 0x{:X}", table.defaultLabel); 201 | println("labels = ["); 202 | for (const auto& label : table.labels) 203 | { 204 | println(" 0x{:X},", label); 205 | } 206 | 207 | println("]"); 208 | println(""); 209 | }; 210 | 211 | std::vector switches{}; 212 | 213 | println("# Generated by XenonAnalyse"); 214 | 215 | auto scanPattern = [&](uint32_t* pattern, size_t count, size_t type) 216 | { 217 | for (const auto& section : image.sections) 218 | { 219 | if (!(section.flags & SectionFlags_Code)) 220 | { 221 | continue; 222 | } 223 | 224 | size_t base = section.base; 225 | uint8_t* data = section.data; 226 | uint8_t* dataStart = section.data; 227 | uint8_t* dataEnd = section.data + section.size; 228 | while (data < dataEnd && data != nullptr) 229 | { 230 | data = (uint8_t*)SearchMask(data, pattern, count, dataEnd - data); 231 | 232 | if (data != nullptr) 233 | { 234 | SwitchTable table{}; 235 | table.type = type; 236 | ScanTable((uint32_t*)data, base + (data - dataStart), table); 237 | 238 | // fmt::println("{:X} ; jmptable - {}", base + (data - dataStart), table.labels.size()); 239 | if (table.base != 0) 240 | { 241 | ReadTable(image, table); 242 | printTable(table); 243 | switches.emplace_back(std::move(table)); 244 | } 245 | 246 | data += 4; 247 | } 248 | continue; 249 | } 250 | } 251 | }; 252 | 253 | uint32_t absoluteSwitch[] = 254 | { 255 | PPC_INST_LIS, 256 | PPC_INST_ADDI, 257 | PPC_INST_RLWINM, 258 | PPC_INST_LWZX, 259 | PPC_INST_MTCTR, 260 | PPC_INST_BCTR, 261 | }; 262 | 263 | uint32_t computedSwitch[] = 264 | { 265 | PPC_INST_LIS, 266 | PPC_INST_ADDI, 267 | PPC_INST_LBZX, 268 | PPC_INST_RLWINM, 269 | PPC_INST_LIS, 270 | PPC_INST_ADDI, 271 | PPC_INST_ADD, 272 | PPC_INST_MTCTR, 273 | }; 274 | 275 | uint32_t offsetSwitch[] = 276 | { 277 | PPC_INST_LIS, 278 | PPC_INST_ADDI, 279 | PPC_INST_LBZX, 280 | PPC_INST_LIS, 281 | PPC_INST_ADDI, 282 | PPC_INST_ADD, 283 | PPC_INST_MTCTR, 284 | }; 285 | 286 | uint32_t wordOffsetSwitch[] = 287 | { 288 | PPC_INST_LIS, 289 | PPC_INST_ADDI, 290 | PPC_INST_RLWINM, 291 | PPC_INST_LHZX, 292 | PPC_INST_LIS, 293 | PPC_INST_ADDI, 294 | PPC_INST_ADD, 295 | PPC_INST_MTCTR, 296 | }; 297 | 298 | println("# ---- ABSOLUTE JUMPTABLE ----"); 299 | scanPattern(absoluteSwitch, std::size(absoluteSwitch), SWITCH_ABSOLUTE); 300 | 301 | println("# ---- COMPUTED JUMPTABLE ----"); 302 | scanPattern(computedSwitch, std::size(computedSwitch), SWITCH_COMPUTED); 303 | 304 | println("# ---- OFFSETED JUMPTABLE ----"); 305 | scanPattern(offsetSwitch, std::size(offsetSwitch), SWITCH_BYTEOFFSET); 306 | scanPattern(wordOffsetSwitch, std::size(wordOffsetSwitch), SWITCH_SHORTOFFSET); 307 | 308 | std::ofstream f(argv[2]); 309 | f.write(out.data(), out.size()); 310 | 311 | return EXIT_SUCCESS; 312 | } 313 | -------------------------------------------------------------------------------- /XenonRecomp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.8) 2 | 3 | project("XenonRecomp") 4 | 5 | add_executable(XenonRecomp 6 | "main.cpp" 7 | "recompiler.cpp" 8 | "test_recompiler.cpp" 9 | "recompiler_config.cpp") 10 | 11 | target_precompile_headers(XenonRecomp PUBLIC "pch.h") 12 | 13 | target_link_libraries(XenonRecomp PRIVATE 14 | LibXenonAnalyse 15 | XenonUtils 16 | fmt::fmt 17 | tomlplusplus::tomlplusplus 18 | xxHash::xxhash) 19 | 20 | if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") 21 | target_compile_options(XenonRecomp PRIVATE -Wno-switch -Wno-unused-variable -Wno-null-arithmetic) 22 | 23 | # alias attribute not supported on Apple. 24 | if (NOT APPLE) 25 | target_compile_definitions(XenonRecomp PRIVATE XENON_RECOMP_USE_ALIAS) 26 | endif() 27 | endif() 28 | 29 | target_compile_definitions(XenonRecomp PRIVATE _CRT_SECURE_NO_WARNINGS) 30 | -------------------------------------------------------------------------------- /XenonRecomp/main.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "test_recompiler.h" 3 | 4 | int main(int argc, char* argv[]) 5 | { 6 | #ifndef XENON_RECOMP_CONFIG_FILE_PATH 7 | if (argc < 3) 8 | { 9 | printf("Usage: XenonRecomp [input TOML file path] [PPC context header file path]"); 10 | return EXIT_SUCCESS; 11 | } 12 | #endif 13 | 14 | const char* path = 15 | #ifdef XENON_RECOMP_CONFIG_FILE_PATH 16 | XENON_RECOMP_CONFIG_FILE_PATH 17 | #else 18 | argv[1] 19 | #endif 20 | ; 21 | 22 | if (std::filesystem::is_regular_file(path)) 23 | { 24 | Recompiler recompiler; 25 | if (!recompiler.LoadConfig(path)) 26 | return -1; 27 | 28 | recompiler.Analyse(); 29 | 30 | auto entry = recompiler.image.symbols.find(recompiler.image.entry_point); 31 | if (entry != recompiler.image.symbols.end()) 32 | { 33 | entry->name = "_xstart"; 34 | } 35 | 36 | const char* headerFilePath = 37 | #ifdef XENON_RECOMP_HEADER_FILE_PATH 38 | XENON_RECOMP_HEADER_FILE_PATH 39 | #else 40 | argv[2] 41 | #endif 42 | ; 43 | 44 | recompiler.Recompile(headerFilePath); 45 | } 46 | else 47 | { 48 | TestRecompiler::RecompileTests(path, argv[2]); 49 | } 50 | 51 | return EXIT_SUCCESS; 52 | } 53 | -------------------------------------------------------------------------------- /XenonRecomp/pch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | -------------------------------------------------------------------------------- /XenonRecomp/recompiler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "pch.h" 4 | #include "recompiler_config.h" 5 | 6 | struct RecompilerLocalVariables 7 | { 8 | bool ctr{}; 9 | bool xer{}; 10 | bool reserved{}; 11 | bool cr[8]{}; 12 | bool r[32]{}; 13 | bool f[32]{}; 14 | bool v[128]{}; 15 | bool env{}; 16 | bool temp{}; 17 | bool vTemp{}; 18 | bool ea{}; 19 | }; 20 | 21 | enum class CSRState 22 | { 23 | Unknown, 24 | FPU, 25 | VMX 26 | }; 27 | 28 | struct Recompiler 29 | { 30 | // Enforce In-order Execution of I/O constant for quick comparison 31 | static constexpr uint32_t c_eieio = 0xAC06007C; 32 | Image image; 33 | std::vector functions; 34 | std::string out; 35 | size_t cppFileIndex = 0; 36 | RecompilerConfig config; 37 | 38 | bool LoadConfig(const std::string_view& configFilePath); 39 | 40 | template 41 | void print(fmt::format_string fmt, Args&&... args) 42 | { 43 | fmt::vformat_to(std::back_inserter(out), fmt.get(), fmt::make_format_args(args...)); 44 | } 45 | 46 | template 47 | void println(fmt::format_string fmt, Args&&... args) 48 | { 49 | fmt::vformat_to(std::back_inserter(out), fmt.get(), fmt::make_format_args(args...)); 50 | out += '\n'; 51 | } 52 | 53 | void Analyse(); 54 | 55 | // TODO: make a RecompileArgs struct instead this is getting messy 56 | bool Recompile( 57 | const Function& fn, 58 | uint32_t base, 59 | const ppc_insn& insn, 60 | const uint32_t* data, 61 | std::unordered_map::iterator& switchTable, 62 | RecompilerLocalVariables& localVariables, 63 | CSRState& csrState); 64 | 65 | bool Recompile(const Function& fn); 66 | 67 | void Recompile(const std::filesystem::path& headerFilePath); 68 | 69 | void SaveCurrentOutData(const std::string_view& name = std::string_view()); 70 | }; 71 | -------------------------------------------------------------------------------- /XenonRecomp/recompiler_config.cpp: -------------------------------------------------------------------------------- 1 | #include "recompiler_config.h" 2 | 3 | void RecompilerConfig::Load(const std::string_view& configFilePath) 4 | { 5 | directoryPath = configFilePath.substr(0, configFilePath.find_last_of("\\/") + 1); 6 | toml::table toml = toml::parse_file(configFilePath) 7 | #if !TOML_EXCEPTIONS 8 | .table() 9 | #endif 10 | ; 11 | 12 | if (auto mainPtr = toml["main"].as_table()) 13 | { 14 | const auto& main = *mainPtr; 15 | filePath = main["file_path"].value_or(""); 16 | patchFilePath = main["patch_file_path"].value_or(""); 17 | patchedFilePath = main["patched_file_path"].value_or(""); 18 | outDirectoryPath = main["out_directory_path"].value_or(""); 19 | switchTableFilePath = main["switch_table_file_path"].value_or(""); 20 | 21 | skipLr = main["skip_lr"].value_or(false); 22 | skipMsr = main["skip_msr"].value_or(false); 23 | ctrAsLocalVariable = main["ctr_as_local"].value_or(false); 24 | xerAsLocalVariable = main["xer_as_local"].value_or(false); 25 | reservedRegisterAsLocalVariable = main["reserved_as_local"].value_or(false); 26 | crRegistersAsLocalVariables = main["cr_as_local"].value_or(false); 27 | nonArgumentRegistersAsLocalVariables = main["non_argument_as_local"].value_or(false); 28 | nonVolatileRegistersAsLocalVariables = main["non_volatile_as_local"].value_or(false); 29 | 30 | restGpr14Address = main["restgprlr_14_address"].value_or(0u); 31 | saveGpr14Address = main["savegprlr_14_address"].value_or(0u); 32 | restFpr14Address = main["restfpr_14_address"].value_or(0u); 33 | saveFpr14Address = main["savefpr_14_address"].value_or(0u); 34 | restVmx14Address = main["restvmx_14_address"].value_or(0u); 35 | saveVmx14Address = main["savevmx_14_address"].value_or(0u); 36 | restVmx64Address = main["restvmx_64_address"].value_or(0u); 37 | saveVmx64Address = main["savevmx_64_address"].value_or(0u); 38 | longJmpAddress = main["longjmp_address"].value_or(0u); 39 | setJmpAddress = main["setjmp_address"].value_or(0u); 40 | 41 | if (restGpr14Address == 0) fmt::println("ERROR: __restgprlr_14 address is unspecified"); 42 | if (saveGpr14Address == 0) fmt::println("ERROR: __savegprlr_14 address is unspecified"); 43 | if (restFpr14Address == 0) fmt::println("ERROR: __restfpr_14 address is unspecified"); 44 | if (saveFpr14Address == 0) fmt::println("ERROR: __savefpr_14 address is unspecified"); 45 | if (restVmx14Address == 0) fmt::println("ERROR: __restvmx_14 address is unspecified"); 46 | if (saveVmx14Address == 0) fmt::println("ERROR: __savevmx_14 address is unspecified"); 47 | if (restVmx64Address == 0) fmt::println("ERROR: __restvmx_64 address is unspecified"); 48 | if (saveVmx64Address == 0) fmt::println("ERROR: __savevmx_64 address is unspecified"); 49 | 50 | if (auto functionsArray = main["functions"].as_array()) 51 | { 52 | for (auto& func : *functionsArray) 53 | { 54 | auto& funcTable = *func.as_table(); 55 | uint32_t address = *funcTable["address"].value(); 56 | uint32_t size = *funcTable["size"].value(); 57 | functions.emplace(address, size); 58 | } 59 | } 60 | 61 | if (auto invalidArray = main["invalid_instructions"].as_array()) 62 | { 63 | for (auto& instr : *invalidArray) 64 | { 65 | auto& instrTable = *instr.as_table(); 66 | uint32_t data = *instrTable["data"].value(); 67 | uint32_t size = *instrTable["size"].value(); 68 | invalidInstructions.emplace(data, size); 69 | } 70 | } 71 | 72 | if (!switchTableFilePath.empty()) 73 | { 74 | toml::table switchToml = toml::parse_file(directoryPath + switchTableFilePath) 75 | #if !TOML_EXCEPTIONS 76 | .table() 77 | #endif 78 | ; 79 | if (auto switchArray = switchToml["switch"].as_array()) 80 | { 81 | for (auto& entry : *switchArray) 82 | { 83 | auto& table = *entry.as_table(); 84 | RecompilerSwitchTable switchTable; 85 | switchTable.r = *table["r"].value(); 86 | for (auto& label : *table["labels"].as_array()) 87 | { 88 | switchTable.labels.push_back(*label.value()); 89 | } 90 | switchTables.emplace(*table["base"].value(), std::move(switchTable)); 91 | } 92 | } 93 | } 94 | } 95 | 96 | if (auto midAsmHookArray = toml["midasm_hook"].as_array()) 97 | { 98 | for (auto& entry : *midAsmHookArray) 99 | { 100 | auto& table = *entry.as_table(); 101 | 102 | RecompilerMidAsmHook midAsmHook; 103 | midAsmHook.name = *table["name"].value(); 104 | if (auto registerArray = table["registers"].as_array()) 105 | { 106 | for (auto& reg : *registerArray) 107 | midAsmHook.registers.push_back(*reg.value()); 108 | } 109 | 110 | midAsmHook.ret = table["return"].value_or(false); 111 | midAsmHook.returnOnTrue = table["return_on_true"].value_or(false); 112 | midAsmHook.returnOnFalse = table["return_on_false"].value_or(false); 113 | 114 | midAsmHook.jumpAddress = table["jump_address"].value_or(0u); 115 | midAsmHook.jumpAddressOnTrue = table["jump_address_on_true"].value_or(0u); 116 | midAsmHook.jumpAddressOnFalse = table["jump_address_on_false"].value_or(0u); 117 | 118 | if ((midAsmHook.ret && midAsmHook.jumpAddress != NULL) || 119 | (midAsmHook.returnOnTrue && midAsmHook.jumpAddressOnTrue != NULL) || 120 | (midAsmHook.returnOnFalse && midAsmHook.jumpAddressOnFalse != NULL)) 121 | { 122 | fmt::println("{}: can't return and jump at the same time", midAsmHook.name); 123 | } 124 | 125 | if ((midAsmHook.ret || midAsmHook.jumpAddress != NULL) && 126 | (midAsmHook.returnOnFalse != NULL || midAsmHook.returnOnTrue != NULL || 127 | midAsmHook.jumpAddressOnFalse != NULL || midAsmHook.jumpAddressOnTrue != NULL)) 128 | { 129 | fmt::println("{}: can't mix direct and conditional return/jump", midAsmHook.name); 130 | } 131 | 132 | midAsmHook.afterInstruction = table["after_instruction"].value_or(false); 133 | 134 | midAsmHooks.emplace(*table["address"].value(), std::move(midAsmHook)); 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /XenonRecomp/recompiler_config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct RecompilerSwitchTable 4 | { 5 | uint32_t r; 6 | std::vector labels; 7 | }; 8 | 9 | struct RecompilerMidAsmHook 10 | { 11 | std::string name; 12 | std::vector registers; 13 | 14 | bool ret = false; 15 | bool returnOnTrue = false; 16 | bool returnOnFalse = false; 17 | 18 | uint32_t jumpAddress = 0; 19 | uint32_t jumpAddressOnTrue = 0; 20 | uint32_t jumpAddressOnFalse = 0; 21 | 22 | bool afterInstruction = false; 23 | }; 24 | 25 | struct RecompilerConfig 26 | { 27 | std::string directoryPath; 28 | std::string filePath; 29 | std::string patchFilePath; 30 | std::string patchedFilePath; 31 | std::string outDirectoryPath; 32 | std::string switchTableFilePath; 33 | std::unordered_map switchTables; 34 | bool skipLr = false; 35 | bool ctrAsLocalVariable = false; 36 | bool xerAsLocalVariable = false; 37 | bool reservedRegisterAsLocalVariable = false; 38 | bool skipMsr = false; 39 | bool crRegistersAsLocalVariables = false; 40 | bool nonArgumentRegistersAsLocalVariables = false; 41 | bool nonVolatileRegistersAsLocalVariables = false; 42 | uint32_t restGpr14Address = 0; 43 | uint32_t saveGpr14Address = 0; 44 | uint32_t restFpr14Address = 0; 45 | uint32_t saveFpr14Address = 0; 46 | uint32_t restVmx14Address = 0; 47 | uint32_t saveVmx14Address = 0; 48 | uint32_t restVmx64Address = 0; 49 | uint32_t saveVmx64Address = 0; 50 | uint32_t longJmpAddress = 0; 51 | uint32_t setJmpAddress = 0; 52 | std::unordered_map functions; 53 | std::unordered_map invalidInstructions; 54 | std::unordered_map midAsmHooks; 55 | 56 | void Load(const std::string_view& configFilePath); 57 | }; 58 | -------------------------------------------------------------------------------- /XenonRecomp/test_recompiler.cpp: -------------------------------------------------------------------------------- 1 | #include "test_recompiler.h" 2 | 3 | void TestRecompiler::Analyse(const std::string_view& testName) 4 | { 5 | for (const auto& section : image.sections) 6 | { 7 | if (!(section.flags & SectionFlags_Code)) 8 | { 9 | continue; 10 | } 11 | size_t base = section.base; 12 | uint8_t* data = section.data; 13 | uint8_t* dataEnd = section.data + section.size; 14 | 15 | while (data < dataEnd) 16 | { 17 | if (*(uint32_t*)data == 0) 18 | { 19 | data += 4; 20 | base += 4; 21 | continue; 22 | } 23 | 24 | auto& fn = functions.emplace_back(Function::Analyze(data, dataEnd - data, base)); 25 | image.symbols.emplace(fmt::format("{}_{:X}", testName, fn.base), fn.base, fn.size, Symbol_Function); 26 | 27 | base += fn.size; 28 | data += fn.size; 29 | } 30 | } 31 | 32 | std::sort(functions.begin(), functions.end(), [](auto& lhs, auto& rhs) { return lhs.base < rhs.base; }); 33 | } 34 | 35 | void TestRecompiler::RecompileTests(const char* srcDirectoryPath, const char* dstDirectoryPath) 36 | { 37 | std::map> functions; 38 | 39 | for (auto& file : std::filesystem::directory_iterator(srcDirectoryPath)) 40 | { 41 | if (file.path().extension() == ".o") 42 | { 43 | const auto exeFile = LoadFile(file.path().string().c_str()); 44 | 45 | TestRecompiler recompiler; 46 | recompiler.config.outDirectoryPath = dstDirectoryPath; 47 | recompiler.image = Image::ParseImage(exeFile.data(), exeFile.size()); 48 | 49 | auto stem = file.path().stem().string(); 50 | recompiler.Analyse(stem); 51 | 52 | recompiler.println("#define PPC_CONFIG_H_INCLUDED"); 53 | recompiler.println("#include \n"); 54 | recompiler.println("#define __builtin_debugtrap()\n"); 55 | 56 | for (auto& fn : recompiler.functions) 57 | { 58 | if (recompiler.Recompile(fn)) 59 | { 60 | functions[stem].emplace(fn.base); 61 | } 62 | else 63 | { 64 | fmt::println("Function {:X} in {} has unimplemented instructions", fn.base, stem); 65 | } 66 | } 67 | stem += ".cpp"; 68 | recompiler.SaveCurrentOutData(stem); 69 | } 70 | } 71 | 72 | std::unordered_map symbols; 73 | 74 | for (auto& [fn, addr] : functions) 75 | { 76 | std::ifstream in(fmt::format("{}/{}.dis", srcDirectoryPath, fn)); 77 | if (in.is_open()) 78 | { 79 | std::string line; 80 | while (std::getline(in, line)) 81 | { 82 | int spaceIndex = line.find(' '); 83 | int bracketIndex = line.find('>'); 84 | if (spaceIndex != std::string::npos && bracketIndex != std::string::npos) 85 | { 86 | size_t address = ~0; 87 | std::from_chars(&line[0], &line[spaceIndex], address, 16); 88 | address &= 0xFFFFF; 89 | if (addr.find(address) != addr.end()) 90 | symbols.emplace(line.substr(spaceIndex + 2, bracketIndex - spaceIndex - 2), fmt::format("{}_{:X}", fn, address)); 91 | } 92 | } 93 | } 94 | else 95 | { 96 | fmt::println("Unable to locate disassembly file for {}", fn); 97 | } 98 | } 99 | 100 | FILE* file = fopen(fmt::format("{}/main.cpp", dstDirectoryPath).c_str(), "w"); 101 | std::string main; 102 | 103 | fmt::println(file, "#define PPC_CONFIG_H_INCLUDED"); 104 | fmt::println(file, "#include "); 105 | fmt::println(file, "#ifdef _WIN32"); 106 | fmt::println(file, "#include "); 107 | fmt::println(file, "#else"); 108 | fmt::println(file, "#include "); 109 | fmt::println(file, "#endif"); 110 | fmt::println(file, "#include \n"); 111 | fmt::println(file, "#define PPC_CHECK_VALUE_U(f, lhs, rhs) if (lhs != rhs) fmt::println(#f \" \" #lhs \" EXPECTED \" #rhs \" ACTUAL {{:X}}\", lhs)\n"); 112 | fmt::println(file, "#define PPC_CHECK_VALUE_F(f, lhs, rhs) if (lhs != rhs) fmt::println(#f \" \" #lhs \" EXPECTED \" #rhs \" ACTUAL {{}}\", lhs)\n"); 113 | 114 | for (auto& [fn, addr] : functions) 115 | { 116 | std::ifstream in(fmt::format("{}/../{}.s", srcDirectoryPath, fn)); 117 | if (in.is_open()) 118 | { 119 | std::string str; 120 | auto getline = [&]() 121 | { 122 | if (std::getline(in, str)) 123 | { 124 | str.erase(str.find_last_not_of(' ') + 1); 125 | str.erase(0, str.find_first_not_of(' ')); 126 | return true; 127 | } 128 | return false; 129 | }; 130 | 131 | while (getline()) 132 | { 133 | if (!str.empty() && str[0] != '#') 134 | { 135 | int colonIndex = str.find(':'); 136 | if (colonIndex != std::string::npos) 137 | { 138 | auto name = str.substr(0, colonIndex); 139 | auto symbol = symbols.find(name); 140 | if (symbol != symbols.end()) 141 | { 142 | fmt::println(file, "PPC_FUNC({});\n", symbol->second); 143 | fmt::println(file, "void {}(uint8_t* base) {{", name); 144 | fmt::println(file, "\tPPCContext ctx{{}};"); 145 | fmt::println(file, "\tctx.fpscr.loadFromHost();"); 146 | 147 | while (getline() && !str.empty() && str[0] == '#') 148 | { 149 | if (str.size() > 1 && str[1] == '_') 150 | { 151 | int registerInIndex = str.find("REGISTER_IN"); 152 | if (registerInIndex != std::string::npos) 153 | { 154 | int spaceIndex = str.find(' ', registerInIndex); 155 | int secondSpaceIndex = str.find(' ', spaceIndex + 1); 156 | auto reg = str.substr(spaceIndex + 1, secondSpaceIndex - spaceIndex - 1); 157 | if (reg[0] == 'v') 158 | { 159 | int openingBracketIndex = str.find('[', secondSpaceIndex + 1); 160 | int commaIndex0 = str.find(',', openingBracketIndex + 1); 161 | int commaIndex1 = str.find(',', commaIndex0 + 1); 162 | int commaIndex2 = str.find(',', commaIndex1 + 1); 163 | int closingBracketIndex = str.find(']', commaIndex2 + 1); 164 | 165 | fmt::println(file, "\tctx.{}.u32[3] = 0x{};", reg, str.substr(openingBracketIndex + 1, commaIndex0 - openingBracketIndex - 1)); 166 | fmt::println(file, "\tctx.{}.u32[2] = 0x{};", reg, str.substr(commaIndex0 + 2, commaIndex1 - commaIndex0 - 2)); 167 | fmt::println(file, "\tctx.{}.u32[1] = 0x{};", reg, str.substr(commaIndex1 + 2, commaIndex2 - commaIndex1 - 2)); 168 | fmt::println(file, "\tctx.{}.u32[0] = 0x{};", reg, str.substr(commaIndex2 + 2, closingBracketIndex - commaIndex2 - 2)); 169 | } 170 | else 171 | { 172 | fmt::println(file, "\tctx.{}.{}64 = {};", 173 | reg, 174 | str.find('.', secondSpaceIndex) != std::string::npos ? 'f' : 'u', 175 | str.substr(secondSpaceIndex + 1)); 176 | } 177 | } 178 | else 179 | { 180 | int memoryInIndex = str.find("MEMORY_IN"); 181 | if (memoryInIndex != std::string::npos) 182 | { 183 | int spaceIndex = str.find(' ', memoryInIndex); 184 | int secondSpaceIndex = str.find(' ', spaceIndex + 1); 185 | auto address = str.substr(spaceIndex + 1, secondSpaceIndex - spaceIndex - 1); 186 | for (size_t i = secondSpaceIndex + 1, j = 0; i < str.size(); i++) 187 | { 188 | if (str[i] != ' ') 189 | { 190 | fmt::println(file, "\tbase[0x{} + 0x{:X}] = 0x{}{};", address, j, str[i], str[i + 1]); 191 | ++i; // the loop adds another 192 | ++j; 193 | } 194 | } 195 | } 196 | } 197 | } 198 | } 199 | 200 | while (getline() && (str.empty() || str[0] != '#')) 201 | ; 202 | 203 | fmt::println(file, "\t{}(ctx, base);", symbol->second); 204 | 205 | do 206 | { 207 | if (str.size() > 1 && str[1] == '_') 208 | { 209 | int registerOutIndex = str.find("REGISTER_OUT"); 210 | if (registerOutIndex != std::string::npos) 211 | { 212 | int spaceIndex = str.find(' ', registerOutIndex); 213 | int secondSpaceIndex = str.find(' ', spaceIndex + 1); 214 | auto reg = str.substr(spaceIndex + 1, secondSpaceIndex - spaceIndex - 1); 215 | if (reg[0] == 'c') 216 | continue; // TODO 217 | if (reg[0] == 'v') 218 | { 219 | int openingBracketIndex = str.find('[', secondSpaceIndex + 1); 220 | int commaIndex0 = str.find(',', openingBracketIndex + 1); 221 | int commaIndex1 = str.find(',', commaIndex0 + 1); 222 | int commaIndex2 = str.find(',', commaIndex1 + 1); 223 | int closingBracketIndex = str.find(']', commaIndex2 + 1); 224 | 225 | fmt::println(file, "\tPPC_CHECK_VALUE_U({}, ctx.{}.u32[3], 0x{});", name, reg, str.substr(openingBracketIndex + 1, commaIndex0 - openingBracketIndex - 1)); 226 | fmt::println(file, "\tPPC_CHECK_VALUE_U({}, ctx.{}.u32[2], 0x{});", name, reg, str.substr(commaIndex0 + 2, commaIndex1 - commaIndex0 - 2)); 227 | fmt::println(file, "\tPPC_CHECK_VALUE_U({}, ctx.{}.u32[1], 0x{});", name, reg, str.substr(commaIndex1 + 2, commaIndex2 - commaIndex1 - 2)); 228 | fmt::println(file, "\tPPC_CHECK_VALUE_U({}, ctx.{}.u32[0], 0x{});", name, reg, str.substr(commaIndex2 + 2, closingBracketIndex - commaIndex2 - 2)); 229 | } 230 | else 231 | { 232 | fmt::println(file, "\tPPC_CHECK_VALUE_{}({}, ctx.{}.{}64, {});", 233 | str.find('.', secondSpaceIndex) != std::string::npos ? 'F' : 'U', 234 | name, 235 | reg, 236 | str.find('.', secondSpaceIndex) != std::string::npos ? 'f' : 'u', 237 | str.substr(secondSpaceIndex + 1)); 238 | } 239 | } 240 | else 241 | { 242 | int memoryOutIndex = str.find("MEMORY_OUT"); 243 | if (memoryOutIndex != std::string::npos) 244 | { 245 | int spaceIndex = str.find(' ', memoryOutIndex); 246 | int secondSpaceIndex = str.find(' ', spaceIndex + 1); 247 | auto address = str.substr(spaceIndex + 1, secondSpaceIndex - spaceIndex - 1); 248 | for (size_t i = secondSpaceIndex + 1, j = 0; i < str.size(); i++) 249 | { 250 | if (str[i] != ' ') 251 | { 252 | fmt::println(file, "\tPPC_CHECK_VALUE_U({}, base[0x{} + 0x{:X}], 0x{}{});", name, address, j, str[i], str[i + 1]); 253 | ++i; // the loop adds another 254 | ++j; 255 | } 256 | } 257 | } 258 | } 259 | } 260 | } while (getline() && !str.empty() && str[0] == '#'); 261 | 262 | fmt::println(file, "}}\n"); 263 | 264 | fmt::format_to(std::back_inserter(main), "\t{}(base);\n", name); 265 | } 266 | else 267 | { 268 | fmt::println("Found no symbol for {}", name); 269 | } 270 | } 271 | } 272 | } 273 | } 274 | else 275 | { 276 | fmt::println("Unable to locate source file for {}", fn); 277 | } 278 | } 279 | 280 | fmt::println(file, "int main() {{"); 281 | fmt::println(file, "#ifdef _WIN32"); 282 | fmt::println(file, "\tuint8_t* base = reinterpret_cast(VirtualAlloc(nullptr, 0x100000000ull, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));"); 283 | fmt::println(file, "#else"); 284 | fmt::println(file, "\tuint8_t* base = reinterpret_cast(mmap(NULL, 0x100000000ull, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0));"); 285 | fmt::println(file, "#endif"); 286 | fwrite(main.data(), 1, main.size(), file); 287 | fmt::println(file, "\treturn 0;"); 288 | fmt::println(file, "}}"); 289 | 290 | fclose(file); 291 | } 292 | -------------------------------------------------------------------------------- /XenonRecomp/test_recompiler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "recompiler.h" 3 | 4 | struct TestRecompiler : Recompiler 5 | { 6 | void Analyse(const std::string_view& testName); 7 | void Reset(); 8 | 9 | static void RecompileTests(const char* srcDirectoryPath, const char* dstDirectoryPath); 10 | }; 11 | -------------------------------------------------------------------------------- /XenonTests/.gitignore: -------------------------------------------------------------------------------- 1 | *.cpp -------------------------------------------------------------------------------- /XenonTests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project("XenonTests") 2 | 3 | file(GLOB TEST_FILES *.cpp) 4 | 5 | if(TEST_FILES) 6 | add_executable(XenonTests 7 | ${TEST_FILES} 8 | ) 9 | target_link_libraries(XenonTests 10 | PUBLIC 11 | XenonUtils 12 | fmt::fmt 13 | ) 14 | target_compile_options(XenonTests 15 | PRIVATE 16 | "-march=sandybridge" 17 | "-Wno-unused-label" 18 | "-Wno-unused-variable" 19 | ) 20 | endif() 21 | -------------------------------------------------------------------------------- /XenonUtils/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project("XenonUtils") 2 | 3 | add_library(XenonUtils 4 | "disasm.cpp" 5 | "xex.cpp" 6 | "image.cpp" 7 | "xdbf_wrapper.cpp" 8 | "xex_patcher.cpp" 9 | "memory_mapped_file.cpp" 10 | "${THIRDPARTY_ROOT}/libmspack/libmspack/mspack/lzxd.c" 11 | "${THIRDPARTY_ROOT}/tiny-AES-c/aes.c" 12 | ) 13 | 14 | target_compile_definitions(XenonUtils 15 | PRIVATE 16 | NOMINMAX 17 | ) 18 | 19 | target_include_directories(XenonUtils 20 | PUBLIC 21 | . 22 | PRIVATE 23 | "${THIRDPARTY_ROOT}/libmspack/libmspack/mspack" 24 | "${THIRDPARTY_ROOT}/tiny-AES-c" 25 | "${THIRDPARTY_ROOT}/TinySHA1" 26 | ) 27 | 28 | target_link_libraries(XenonUtils 29 | PUBLIC 30 | disasm 31 | ) 32 | -------------------------------------------------------------------------------- /XenonUtils/byteswap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | template 6 | inline T ByteSwap(T value) 7 | { 8 | if constexpr (sizeof(T) == 1) 9 | return value; 10 | else if constexpr (sizeof(T) == 2) 11 | return static_cast(__builtin_bswap16(static_cast(value))); 12 | else if constexpr (sizeof(T) == 4) 13 | return static_cast(__builtin_bswap32(static_cast(value))); 14 | else if constexpr (sizeof(T) == 8) 15 | return static_cast(__builtin_bswap64(static_cast(value))); 16 | 17 | assert(false && "Unexpected byte size."); 18 | return value; 19 | } 20 | 21 | template 22 | inline void ByteSwapInplace(T& value) 23 | { 24 | value = ByteSwap(value); 25 | } 26 | -------------------------------------------------------------------------------- /XenonUtils/disasm.cpp: -------------------------------------------------------------------------------- 1 | #include "disasm.h" 2 | 3 | thread_local ppc::DisassemblerEngine ppc::gBigEndianDisassembler{ BFD_ENDIAN_BIG, "cell 64"}; 4 | 5 | ppc::DisassemblerEngine::DisassemblerEngine(bfd_endian endian, const char* options) 6 | { 7 | INIT_DISASSEMBLE_INFO(info, stdout, fprintf); 8 | info.arch = bfd_arch_powerpc; 9 | info.endian = endian; 10 | info.disassembler_options = options; 11 | } 12 | 13 | int ppc::DisassemblerEngine::Disassemble(const void* code, size_t size, uint64_t base, ppc_insn& out) 14 | { 15 | if (size < 4) 16 | { 17 | return 0; 18 | } 19 | 20 | info.buffer = (bfd_byte*)code; 21 | info.buffer_vma = base; 22 | info.buffer_length = size; 23 | return decode_insn_ppc(base, &info, &out); 24 | } 25 | 26 | int ppc::Disassemble(const void* code, uint64_t base, ppc_insn* out, size_t nOut) 27 | { 28 | for (size_t i = 0; i < nOut; i++) 29 | { 30 | Disassemble(static_cast(code) + i, base, out[i]); 31 | } 32 | return static_cast(nOut) * 4; 33 | } 34 | -------------------------------------------------------------------------------- /XenonUtils/disasm.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace ppc 6 | { 7 | struct DisassemblerEngine 8 | { 9 | disassemble_info info{}; 10 | DisassemblerEngine(const DisassemblerEngine&) = default; 11 | DisassemblerEngine& operator=(const DisassemblerEngine&) = delete; 12 | 13 | DisassemblerEngine(bfd_endian endian, const char* options); 14 | ~DisassemblerEngine() = default; 15 | 16 | /** 17 | * \return Numbers of bytes decoded 18 | */ 19 | int Disassemble(const void* code, size_t size, uint64_t base, ppc_insn& out); 20 | }; 21 | 22 | thread_local extern DisassemblerEngine gBigEndianDisassembler; 23 | 24 | static int Disassemble(const void* code, size_t size, uint64_t base, ppc_insn& out) 25 | { 26 | return gBigEndianDisassembler.Disassemble(code, size, base, out); 27 | } 28 | 29 | static int Disassemble(const void* code, uint64_t base, ppc_insn& out) 30 | { 31 | return Disassemble(code, 4, base, out); 32 | } 33 | 34 | static int Disassemble(const void* code, uint64_t base, ppc_insn* out, size_t nOut); 35 | } 36 | -------------------------------------------------------------------------------- /XenonUtils/elf.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WhiteTPoison/GeckoRecomp/d978f746197ddbc8432794db20debc98f72d57ca/XenonUtils/elf.h -------------------------------------------------------------------------------- /XenonUtils/file.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | inline std::vector LoadFile(const std::filesystem::path& path) 8 | { 9 | std::ifstream stream(path, std::ios::binary); 10 | if (!stream.is_open()) 11 | { 12 | return {}; 13 | } 14 | 15 | stream.seekg(0, std::ios::end); 16 | std::streampos size = stream.tellg(); 17 | stream.seekg(0, std::ios::beg); 18 | 19 | std::vector data; 20 | data.resize(size); 21 | stream.read((char *)(data.data()), size); 22 | if (stream.bad()) 23 | { 24 | return {}; 25 | } 26 | 27 | return data; 28 | } 29 | -------------------------------------------------------------------------------- /XenonUtils/image.cpp: -------------------------------------------------------------------------------- 1 | #include "image.h" 2 | #include "elf.h" 3 | #include "xex.h" 4 | #include 5 | #include 6 | 7 | void Image::Map(const std::string_view& name, size_t base, uint32_t size, uint8_t flags, uint8_t* data) 8 | { 9 | sections.insert({ std::string(name), this->base + base, 10 | size, static_cast(flags), data }); 11 | } 12 | 13 | const void* Image::Find(size_t address) const 14 | { 15 | const auto section = std::prev(sections.upper_bound(address)); 16 | return section->data + (address - section->base); 17 | } 18 | 19 | const Section* Image::Find(const std::string_view& name) const 20 | { 21 | for (const auto& section : sections) 22 | { 23 | if (section.name == name) 24 | { 25 | return §ion; 26 | } 27 | } 28 | 29 | return nullptr; 30 | } 31 | 32 | Image Image::ParseImage(const uint8_t* data, size_t size) 33 | { 34 | if (data[0] == ELFMAG0 && data[1] == ELFMAG1 && data[2] == ELFMAG2 && data[3] == ELFMAG3) 35 | { 36 | return ElfLoadImage(data, size); 37 | } 38 | else if (data[0] == 'X' && data[1] == 'E' && data[2] == 'X' && data[3] == '2') 39 | { 40 | return Xex2LoadImage(data, size); 41 | } 42 | 43 | return {}; 44 | } 45 | 46 | Image ElfLoadImage(const uint8_t* data, size_t size) 47 | { 48 | const auto* header = (elf32_hdr*)data; 49 | assert(header->e_ident[EI_DATA] == 2); 50 | 51 | Image image{}; 52 | image.size = size; 53 | image.data = std::make_unique(size); 54 | image.entry_point = ByteSwap(header->e_entry); 55 | memcpy(image.data.get(), data, size); 56 | 57 | auto stringTableIndex = ByteSwap(header->e_shstrndx); 58 | 59 | const auto numSections = ByteSwap(header->e_shnum); 60 | const auto numpSections = ByteSwap(header->e_phnum); 61 | 62 | const auto* sections = (elf32_shdr*)(data + ByteSwap(header->e_shoff)); 63 | const auto* psections = (elf32_phdr*)(data + ByteSwap(header->e_phoff)); 64 | 65 | for (size_t i = 0; i < numpSections; i++) 66 | { 67 | if (psections[i].p_type == ByteSwap((Elf32_Word)PT_LOAD)) 68 | { 69 | image.base = ByteSwap(psections[i].p_vaddr); 70 | break; 71 | } 72 | } 73 | 74 | auto* stringTable = reinterpret_cast(data + ByteSwap(sections[stringTableIndex].sh_offset)); 75 | 76 | for (size_t i = 0; i < numSections; i++) 77 | { 78 | const auto& section = sections[i]; 79 | if (section.sh_type == 0) 80 | { 81 | continue; 82 | } 83 | 84 | uint8_t flags{}; 85 | 86 | if (section.sh_flags & ByteSwap(SHF_EXECINSTR)) 87 | { 88 | flags |= SectionFlags_Code; 89 | } 90 | 91 | auto* name = section.sh_name != 0 ? stringTable + ByteSwap(section.sh_name) : nullptr; 92 | const auto rva = ByteSwap(section.sh_addr) - image.base; 93 | const auto size = ByteSwap(section.sh_size); 94 | 95 | image.Map(name, rva, size, flags, image.data.get() + ByteSwap(section.sh_offset)); 96 | } 97 | 98 | return image; 99 | } 100 | -------------------------------------------------------------------------------- /XenonUtils/image.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "symbol_table.h" 7 | 8 | struct Image 9 | { 10 | std::unique_ptr data{}; 11 | size_t base{}; 12 | uint32_t size{}; 13 | 14 | size_t entry_point{}; 15 | std::set sections{}; 16 | SymbolTable symbols{}; 17 | 18 | /** 19 | * \brief Map data to image by RVA 20 | * \param name Name of section 21 | * \param base Section RVA 22 | * \param size Section Size 23 | * \param flags Section Flags, enum SectionFlags 24 | * \param data Section data 25 | */ 26 | void Map(const std::string_view& name, size_t base, uint32_t size, uint8_t flags, uint8_t* data); 27 | 28 | /** 29 | * \param address Virtual Address 30 | * \return Pointer to image owned data 31 | */ 32 | const void* Find(size_t address) const; 33 | 34 | /** 35 | * \param name Name of section 36 | * \return Section 37 | */ 38 | const Section* Find(const std::string_view& name) const; 39 | 40 | /** 41 | * \brief Parse given data to an image, reallocates with ownership 42 | * \param data Pointer to data 43 | * \param size Size of data 44 | * \return Parsed image 45 | */ 46 | static Image ParseImage(const uint8_t* data, size_t size); 47 | }; 48 | 49 | Image ElfLoadImage(const uint8_t* data, size_t size); 50 | -------------------------------------------------------------------------------- /XenonUtils/memory_mapped_file.cpp: -------------------------------------------------------------------------------- 1 | #include "memory_mapped_file.h" 2 | 3 | #if !defined(_WIN32) 4 | # include 5 | # include 6 | # include 7 | # include 8 | #endif 9 | 10 | MemoryMappedFile::MemoryMappedFile() 11 | { 12 | // Default constructor. 13 | } 14 | 15 | MemoryMappedFile::MemoryMappedFile(const std::filesystem::path &path) 16 | { 17 | open(path); 18 | } 19 | 20 | MemoryMappedFile::~MemoryMappedFile() 21 | { 22 | close(); 23 | } 24 | 25 | MemoryMappedFile::MemoryMappedFile(MemoryMappedFile &&other) 26 | { 27 | #if defined(_WIN32) 28 | fileHandle = other.fileHandle; 29 | fileMappingHandle = other.fileMappingHandle; 30 | fileView = other.fileView; 31 | fileSize = other.fileSize; 32 | 33 | other.fileHandle = nullptr; 34 | other.fileMappingHandle = nullptr; 35 | other.fileView = nullptr; 36 | other.fileSize.QuadPart = 0; 37 | #else 38 | fileHandle = other.fileHandle; 39 | fileView = other.fileView; 40 | fileSize = other.fileSize; 41 | 42 | other.fileHandle = -1; 43 | other.fileView = MAP_FAILED; 44 | other.fileSize = 0; 45 | #endif 46 | } 47 | 48 | bool MemoryMappedFile::open(const std::filesystem::path &path) 49 | { 50 | #if defined(_WIN32) 51 | fileHandle = CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); 52 | if (fileHandle == INVALID_HANDLE_VALUE) 53 | { 54 | fprintf(stderr, "CreateFileW failed with error %lu.\n", GetLastError()); 55 | fileHandle = nullptr; 56 | return false; 57 | } 58 | 59 | if (!GetFileSizeEx(fileHandle, &fileSize)) 60 | { 61 | fprintf(stderr, "GetFileSizeEx failed with error %lu.\n", GetLastError()); 62 | CloseHandle(fileHandle); 63 | fileHandle = nullptr; 64 | return false; 65 | } 66 | 67 | fileMappingHandle = CreateFileMappingW(fileHandle, nullptr, PAGE_READONLY, 0, 0, nullptr); 68 | if (fileMappingHandle == nullptr) 69 | { 70 | fprintf(stderr, "CreateFileMappingW failed with error %lu.\n", GetLastError()); 71 | CloseHandle(fileHandle); 72 | fileHandle = nullptr; 73 | return false; 74 | } 75 | 76 | fileView = MapViewOfFile(fileMappingHandle, FILE_MAP_READ, 0, 0, 0); 77 | if (fileView == nullptr) 78 | { 79 | fprintf(stderr, "MapViewOfFile failed with error %lu.\n", GetLastError()); 80 | CloseHandle(fileMappingHandle); 81 | CloseHandle(fileHandle); 82 | fileMappingHandle = nullptr; 83 | fileHandle = nullptr; 84 | return false; 85 | } 86 | 87 | return true; 88 | #else 89 | fileHandle = ::open(path.c_str(), O_RDONLY); 90 | if (fileHandle == -1) 91 | { 92 | fprintf(stderr, "open for %s failed with error %s.\n", path.c_str(), strerror(errno)); 93 | return false; 94 | } 95 | 96 | fileSize = lseek(fileHandle, 0, SEEK_END); 97 | if (fileSize == (off_t)(-1)) 98 | { 99 | fprintf(stderr, "lseek failed with error %s.\n", strerror(errno)); 100 | ::close(fileHandle); 101 | fileHandle = -1; 102 | return false; 103 | } 104 | 105 | fileView = mmap(nullptr, fileSize, PROT_READ, MAP_PRIVATE, fileHandle, 0); 106 | if (fileView == MAP_FAILED) 107 | { 108 | fprintf(stderr, "mmap failed with error %s.\n", strerror(errno)); 109 | ::close(fileHandle); 110 | fileHandle = -1; 111 | return false; 112 | } 113 | 114 | return true; 115 | #endif 116 | } 117 | 118 | void MemoryMappedFile::close() 119 | { 120 | #if defined(_WIN32) 121 | if (fileView != nullptr) 122 | { 123 | UnmapViewOfFile(fileView); 124 | } 125 | 126 | if (fileMappingHandle != nullptr) 127 | { 128 | CloseHandle(fileMappingHandle); 129 | } 130 | 131 | if (fileHandle != nullptr) 132 | { 133 | CloseHandle(fileHandle); 134 | } 135 | #else 136 | if (fileView != MAP_FAILED) 137 | { 138 | munmap(fileView, fileSize); 139 | } 140 | 141 | if (fileHandle != -1) 142 | { 143 | ::close(fileHandle); 144 | } 145 | #endif 146 | } 147 | 148 | bool MemoryMappedFile::isOpen() const 149 | { 150 | #if defined(_WIN32) 151 | return (fileView != nullptr); 152 | #else 153 | return (fileView != MAP_FAILED); 154 | #endif 155 | } 156 | 157 | uint8_t *MemoryMappedFile::data() const 158 | { 159 | return reinterpret_cast(fileView); 160 | } 161 | 162 | size_t MemoryMappedFile::size() const 163 | { 164 | #if defined(_WIN32) 165 | return fileSize.QuadPart; 166 | #else 167 | return static_cast(fileSize); 168 | #endif 169 | } 170 | -------------------------------------------------------------------------------- /XenonUtils/memory_mapped_file.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #if defined(_WIN32) 6 | # include 7 | #else 8 | # include 9 | #endif 10 | 11 | struct MemoryMappedFile { 12 | #if defined(_WIN32) 13 | HANDLE fileHandle = nullptr; 14 | HANDLE fileMappingHandle = nullptr; 15 | LPVOID fileView = nullptr; 16 | LARGE_INTEGER fileSize = {}; 17 | #else 18 | int fileHandle = -1; 19 | void *fileView = MAP_FAILED; 20 | off_t fileSize = 0; 21 | #endif 22 | 23 | MemoryMappedFile(); 24 | MemoryMappedFile(const std::filesystem::path &path); 25 | MemoryMappedFile(MemoryMappedFile &&other); 26 | ~MemoryMappedFile(); 27 | bool open(const std::filesystem::path &path); 28 | void close(); 29 | bool isOpen() const; 30 | uint8_t *data() const; 31 | size_t size() const; 32 | }; 33 | -------------------------------------------------------------------------------- /XenonUtils/ppc_context.h: -------------------------------------------------------------------------------- 1 | #ifndef PPC_CONTEXT_H_INCLUDED 2 | #define PPC_CONTEXT_H_INCLUDED 3 | 4 | #ifndef PPC_CONFIG_H_INCLUDED 5 | #error "ppc_config.h must be included before ppc_context.h" 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | #ifdef _WIN32 18 | #include 19 | #else 20 | #include 21 | #include 22 | #endif 23 | 24 | #define PPC_JOIN(x, y) x##y 25 | #define PPC_XSTRINGIFY(x) #x 26 | #define PPC_STRINGIFY(x) PPC_XSTRINGIFY(x) 27 | #define PPC_FUNC(x) void x(PPCContext& __restrict ctx, uint8_t* base) 28 | #define PPC_FUNC_IMPL(x) extern "C" PPC_FUNC(x) 29 | #define PPC_EXTERN_FUNC(x) extern PPC_FUNC(x) 30 | #define PPC_WEAK_FUNC(x) __attribute__((weak,noinline)) PPC_FUNC(x) 31 | 32 | #define PPC_FUNC_PROLOGUE() __builtin_assume(((size_t)base & 0x1F) == 0) 33 | 34 | #ifndef PPC_LOAD_U8 35 | #define PPC_LOAD_U8(x) *(volatile uint8_t*)(base + (x)) 36 | #endif 37 | 38 | #ifndef PPC_LOAD_U16 39 | #define PPC_LOAD_U16(x) __builtin_bswap16(*(volatile uint16_t*)(base + (x))) 40 | #endif 41 | 42 | #ifndef PPC_LOAD_U32 43 | #define PPC_LOAD_U32(x) __builtin_bswap32(*(volatile uint32_t*)(base + (x))) 44 | #endif 45 | 46 | #ifndef PPC_LOAD_U64 47 | #define PPC_LOAD_U64(x) __builtin_bswap64(*(volatile uint64_t*)(base + (x))) 48 | #endif 49 | 50 | // TODO: Implement. 51 | // These are currently unused. However, MMIO loads could possibly be handled statically with some profiling and a fallback. 52 | // The fallback would be a runtime exception handler which will intercept reads from MMIO regions 53 | // and log the PC for compiling to static code later. 54 | #ifndef PPC_MM_LOAD_U8 55 | #define PPC_MM_LOAD_U8(x) PPC_LOAD_U8 (x) 56 | #endif 57 | 58 | #ifndef PPC_MM_LOAD_U16 59 | #define PPC_MM_LOAD_U16(x) PPC_LOAD_U16(x) 60 | #endif 61 | 62 | #ifndef PPC_MM_LOAD_U32 63 | #define PPC_MM_LOAD_U32(x) PPC_LOAD_U32(x) 64 | #endif 65 | 66 | #ifndef PPC_MM_LOAD_U64 67 | #define PPC_MM_LOAD_U64(x) PPC_LOAD_U64(x) 68 | #endif 69 | 70 | #ifndef PPC_STORE_U8 71 | #define PPC_STORE_U8(x, y) *(volatile uint8_t*)(base + (x)) = (y) 72 | #endif 73 | 74 | #ifndef PPC_STORE_U16 75 | #define PPC_STORE_U16(x, y) *(volatile uint16_t*)(base + (x)) = __builtin_bswap16(y) 76 | #endif 77 | 78 | #ifndef PPC_STORE_U32 79 | #define PPC_STORE_U32(x, y) *(volatile uint32_t*)(base + (x)) = __builtin_bswap32(y) 80 | #endif 81 | 82 | #ifndef PPC_STORE_U64 83 | #define PPC_STORE_U64(x, y) *(volatile uint64_t*)(base + (x)) = __builtin_bswap64(y) 84 | #endif 85 | 86 | // MMIO Store handling is completely reliant on being preeceded by eieio. 87 | // TODO: Verify if that's always the case. 88 | #ifndef PPC_MM_STORE_U8 89 | #define PPC_MM_STORE_U8(x, y) PPC_STORE_U8 (x, y) 90 | #endif 91 | 92 | #ifndef PPC_MM_STORE_U16 93 | #define PPC_MM_STORE_U16(x, y) PPC_STORE_U16(x, y) 94 | #endif 95 | 96 | #ifndef PPC_MM_STORE_U32 97 | #define PPC_MM_STORE_U32(x, y) PPC_STORE_U32(x, y) 98 | #endif 99 | 100 | #ifndef PPC_MM_STORE_U64 101 | #define PPC_MM_STORE_U64(x, y) PPC_STORE_U64(x, y) 102 | #endif 103 | 104 | #ifndef PPC_CALL_FUNC 105 | #define PPC_CALL_FUNC(x) x(ctx, base) 106 | #endif 107 | 108 | #define PPC_MEMORY_SIZE 0x100000000ull 109 | 110 | #define PPC_LOOKUP_FUNC(x, y) *(PPCFunc**)(x + PPC_IMAGE_BASE + PPC_IMAGE_SIZE + (uint64_t(uint32_t(y) - PPC_CODE_BASE) * 2)) 111 | 112 | #ifndef PPC_CALL_INDIRECT_FUNC 113 | #define PPC_CALL_INDIRECT_FUNC(x) (PPC_LOOKUP_FUNC(base, x))(ctx, base) 114 | #endif 115 | 116 | typedef void PPCFunc(struct PPCContext& __restrict__ ctx, uint8_t* base); 117 | 118 | struct PPCFuncMapping 119 | { 120 | size_t guest; 121 | PPCFunc* host; 122 | }; 123 | 124 | extern PPCFuncMapping PPCFuncMappings[]; 125 | 126 | struct PPCRegister 127 | { 128 | union 129 | { 130 | int8_t s8; 131 | uint8_t u8; 132 | int16_t s16; 133 | uint16_t u16; 134 | int32_t s32; 135 | uint32_t u32; 136 | int64_t s64; 137 | uint64_t u64; 138 | float f32; 139 | double f64; 140 | }; 141 | }; 142 | 143 | struct PPCXERRegister 144 | { 145 | uint8_t so; 146 | uint8_t ov; 147 | uint8_t ca; 148 | }; 149 | 150 | struct PPCCRRegister 151 | { 152 | uint8_t lt; 153 | uint8_t gt; 154 | uint8_t eq; 155 | union 156 | { 157 | uint8_t so; 158 | uint8_t un; 159 | }; 160 | 161 | template 162 | inline void compare(T left, T right, const PPCXERRegister& xer) noexcept 163 | { 164 | lt = left < right; 165 | gt = left > right; 166 | eq = left == right; 167 | so = xer.so; 168 | } 169 | 170 | inline void compare(double left, double right) noexcept 171 | { 172 | un = __builtin_isnan(left) || __builtin_isnan(right); 173 | lt = !un && (left < right); 174 | gt = !un && (left > right); 175 | eq = !un && (left == right); 176 | } 177 | 178 | inline void setFromMask(__m128 mask, int imm) noexcept 179 | { 180 | int m = _mm_movemask_ps(mask); 181 | lt = m == imm; // all equal 182 | gt = 0; 183 | eq = m == 0; // none equal 184 | so = 0; 185 | } 186 | 187 | inline void setFromMask(__m128i mask, int imm) noexcept 188 | { 189 | int m = _mm_movemask_epi8(mask); 190 | lt = m == imm; // all equal 191 | gt = 0; 192 | eq = m == 0; // none equal 193 | so = 0; 194 | } 195 | }; 196 | 197 | struct alignas(0x10) PPCVRegister 198 | { 199 | union 200 | { 201 | int8_t s8[16]; 202 | uint8_t u8[16]; 203 | int16_t s16[8]; 204 | uint16_t u16[8]; 205 | int32_t s32[4]; 206 | uint32_t u32[4]; 207 | int64_t s64[2]; 208 | uint64_t u64[2]; 209 | float f32[4]; 210 | double f64[2]; 211 | }; 212 | }; 213 | 214 | #define PPC_ROUND_NEAREST 0x00 215 | #define PPC_ROUND_TOWARD_ZERO 0x01 216 | #define PPC_ROUND_UP 0x02 217 | #define PPC_ROUND_DOWN 0x03 218 | #define PPC_ROUND_MASK 0x03 219 | 220 | struct PPCFPSCRRegister 221 | { 222 | uint32_t csr; 223 | 224 | static constexpr size_t GuestToHost[] = { _MM_ROUND_NEAREST, _MM_ROUND_TOWARD_ZERO, _MM_ROUND_UP, _MM_ROUND_DOWN }; 225 | static constexpr size_t HostToGuest[] = { PPC_ROUND_NEAREST, PPC_ROUND_DOWN, PPC_ROUND_UP, PPC_ROUND_TOWARD_ZERO }; 226 | 227 | inline uint32_t loadFromHost() noexcept 228 | { 229 | csr = _mm_getcsr(); 230 | return HostToGuest[(csr & _MM_ROUND_MASK) >> 13]; 231 | } 232 | 233 | inline void storeFromGuest(uint32_t value) noexcept 234 | { 235 | csr &= ~_MM_ROUND_MASK; 236 | csr |= GuestToHost[value & PPC_ROUND_MASK]; 237 | _mm_setcsr(csr); 238 | } 239 | 240 | static constexpr size_t FlushMask = _MM_FLUSH_ZERO_MASK | _MM_DENORMALS_ZERO_MASK; 241 | 242 | inline void enableFlushModeUnconditional() noexcept 243 | { 244 | csr |= FlushMask; 245 | _mm_setcsr(csr); 246 | } 247 | 248 | inline void disableFlushModeUnconditional() noexcept 249 | { 250 | csr &= ~FlushMask; 251 | _mm_setcsr(csr); 252 | } 253 | 254 | inline void enableFlushMode() noexcept 255 | { 256 | if ((csr & FlushMask) != FlushMask) [[unlikely]] 257 | { 258 | csr |= FlushMask; 259 | _mm_setcsr(csr); 260 | } 261 | } 262 | 263 | inline void disableFlushMode() noexcept 264 | { 265 | if ((csr & FlushMask) != 0) [[unlikely]] 266 | { 267 | csr &= ~FlushMask; 268 | _mm_setcsr(csr); 269 | } 270 | } 271 | }; 272 | 273 | struct PPCContext 274 | { 275 | PPCRegister r3; 276 | #ifndef PPC_CONFIG_NON_ARGUMENT_AS_LOCAL 277 | PPCRegister r0; 278 | #endif 279 | PPCRegister r1; 280 | #ifndef PPC_CONFIG_NON_ARGUMENT_AS_LOCAL 281 | PPCRegister r2; 282 | #endif 283 | PPCRegister r4; 284 | PPCRegister r5; 285 | PPCRegister r6; 286 | PPCRegister r7; 287 | PPCRegister r8; 288 | PPCRegister r9; 289 | PPCRegister r10; 290 | #ifndef PPC_CONFIG_NON_ARGUMENT_AS_LOCAL 291 | PPCRegister r11; 292 | PPCRegister r12; 293 | #endif 294 | PPCRegister r13; 295 | #ifndef PPC_CONFIG_NON_VOLATILE_AS_LOCAL 296 | PPCRegister r14; 297 | PPCRegister r15; 298 | PPCRegister r16; 299 | PPCRegister r17; 300 | PPCRegister r18; 301 | PPCRegister r19; 302 | PPCRegister r20; 303 | PPCRegister r21; 304 | PPCRegister r22; 305 | PPCRegister r23; 306 | PPCRegister r24; 307 | PPCRegister r25; 308 | PPCRegister r26; 309 | PPCRegister r27; 310 | PPCRegister r28; 311 | PPCRegister r29; 312 | PPCRegister r30; 313 | PPCRegister r31; 314 | #endif 315 | 316 | #ifndef PPC_CONFIG_SKIP_LR 317 | uint64_t lr; 318 | #endif 319 | #ifndef PPC_CONFIG_CTR_AS_LOCAL 320 | PPCRegister ctr; 321 | #endif 322 | #ifndef PPC_CONFIG_XER_AS_LOCAL 323 | PPCXERRegister xer; 324 | #endif 325 | #ifndef PPC_CONFIG_RESERVED_AS_LOCAL 326 | PPCRegister reserved; 327 | #endif 328 | #ifndef PPC_CONFIG_SKIP_MSR 329 | uint32_t msr = 0x200A000; 330 | #endif 331 | #ifndef PPC_CONFIG_CR_AS_LOCAL 332 | PPCCRRegister cr0; 333 | PPCCRRegister cr1; 334 | PPCCRRegister cr2; 335 | PPCCRRegister cr3; 336 | PPCCRRegister cr4; 337 | PPCCRRegister cr5; 338 | PPCCRRegister cr6; 339 | PPCCRRegister cr7; 340 | #endif 341 | PPCFPSCRRegister fpscr; 342 | 343 | #ifndef PPC_CONFIG_NON_ARGUMENT_AS_LOCAL 344 | PPCRegister f0; 345 | #endif 346 | PPCRegister f1; 347 | PPCRegister f2; 348 | PPCRegister f3; 349 | PPCRegister f4; 350 | PPCRegister f5; 351 | PPCRegister f6; 352 | PPCRegister f7; 353 | PPCRegister f8; 354 | PPCRegister f9; 355 | PPCRegister f10; 356 | PPCRegister f11; 357 | PPCRegister f12; 358 | PPCRegister f13; 359 | #ifndef PPC_CONFIG_NON_VOLATILE_AS_LOCAL 360 | PPCRegister f14; 361 | PPCRegister f15; 362 | PPCRegister f16; 363 | PPCRegister f17; 364 | PPCRegister f18; 365 | PPCRegister f19; 366 | PPCRegister f20; 367 | PPCRegister f21; 368 | PPCRegister f22; 369 | PPCRegister f23; 370 | PPCRegister f24; 371 | PPCRegister f25; 372 | PPCRegister f26; 373 | PPCRegister f27; 374 | PPCRegister f28; 375 | PPCRegister f29; 376 | PPCRegister f30; 377 | PPCRegister f31; 378 | #endif 379 | 380 | PPCVRegister v0; 381 | PPCVRegister v1; 382 | PPCVRegister v2; 383 | PPCVRegister v3; 384 | PPCVRegister v4; 385 | PPCVRegister v5; 386 | PPCVRegister v6; 387 | PPCVRegister v7; 388 | PPCVRegister v8; 389 | PPCVRegister v9; 390 | PPCVRegister v10; 391 | PPCVRegister v11; 392 | PPCVRegister v12; 393 | PPCVRegister v13; 394 | #ifndef PPC_CONFIG_NON_VOLATILE_AS_LOCAL 395 | PPCVRegister v14; 396 | PPCVRegister v15; 397 | PPCVRegister v16; 398 | PPCVRegister v17; 399 | PPCVRegister v18; 400 | PPCVRegister v19; 401 | PPCVRegister v20; 402 | PPCVRegister v21; 403 | PPCVRegister v22; 404 | PPCVRegister v23; 405 | PPCVRegister v24; 406 | PPCVRegister v25; 407 | PPCVRegister v26; 408 | PPCVRegister v27; 409 | PPCVRegister v28; 410 | PPCVRegister v29; 411 | PPCVRegister v30; 412 | PPCVRegister v31; 413 | #endif 414 | #ifndef PPC_CONFIG_NON_ARGUMENT_AS_LOCAL 415 | PPCVRegister v32; 416 | PPCVRegister v33; 417 | PPCVRegister v34; 418 | PPCVRegister v35; 419 | PPCVRegister v36; 420 | PPCVRegister v37; 421 | PPCVRegister v38; 422 | PPCVRegister v39; 423 | PPCVRegister v40; 424 | PPCVRegister v41; 425 | PPCVRegister v42; 426 | PPCVRegister v43; 427 | PPCVRegister v44; 428 | PPCVRegister v45; 429 | PPCVRegister v46; 430 | PPCVRegister v47; 431 | PPCVRegister v48; 432 | PPCVRegister v49; 433 | PPCVRegister v50; 434 | PPCVRegister v51; 435 | PPCVRegister v52; 436 | PPCVRegister v53; 437 | PPCVRegister v54; 438 | PPCVRegister v55; 439 | PPCVRegister v56; 440 | PPCVRegister v57; 441 | PPCVRegister v58; 442 | PPCVRegister v59; 443 | PPCVRegister v60; 444 | PPCVRegister v61; 445 | PPCVRegister v62; 446 | PPCVRegister v63; 447 | #endif 448 | #ifndef PPC_CONFIG_NON_VOLATILE_AS_LOCAL 449 | PPCVRegister v64; 450 | PPCVRegister v65; 451 | PPCVRegister v66; 452 | PPCVRegister v67; 453 | PPCVRegister v68; 454 | PPCVRegister v69; 455 | PPCVRegister v70; 456 | PPCVRegister v71; 457 | PPCVRegister v72; 458 | PPCVRegister v73; 459 | PPCVRegister v74; 460 | PPCVRegister v75; 461 | PPCVRegister v76; 462 | PPCVRegister v77; 463 | PPCVRegister v78; 464 | PPCVRegister v79; 465 | PPCVRegister v80; 466 | PPCVRegister v81; 467 | PPCVRegister v82; 468 | PPCVRegister v83; 469 | PPCVRegister v84; 470 | PPCVRegister v85; 471 | PPCVRegister v86; 472 | PPCVRegister v87; 473 | PPCVRegister v88; 474 | PPCVRegister v89; 475 | PPCVRegister v90; 476 | PPCVRegister v91; 477 | PPCVRegister v92; 478 | PPCVRegister v93; 479 | PPCVRegister v94; 480 | PPCVRegister v95; 481 | PPCVRegister v96; 482 | PPCVRegister v97; 483 | PPCVRegister v98; 484 | PPCVRegister v99; 485 | PPCVRegister v100; 486 | PPCVRegister v101; 487 | PPCVRegister v102; 488 | PPCVRegister v103; 489 | PPCVRegister v104; 490 | PPCVRegister v105; 491 | PPCVRegister v106; 492 | PPCVRegister v107; 493 | PPCVRegister v108; 494 | PPCVRegister v109; 495 | PPCVRegister v110; 496 | PPCVRegister v111; 497 | PPCVRegister v112; 498 | PPCVRegister v113; 499 | PPCVRegister v114; 500 | PPCVRegister v115; 501 | PPCVRegister v116; 502 | PPCVRegister v117; 503 | PPCVRegister v118; 504 | PPCVRegister v119; 505 | PPCVRegister v120; 506 | PPCVRegister v121; 507 | PPCVRegister v122; 508 | PPCVRegister v123; 509 | PPCVRegister v124; 510 | PPCVRegister v125; 511 | PPCVRegister v126; 512 | PPCVRegister v127; 513 | #endif 514 | }; 515 | 516 | inline uint8_t VectorMaskL[] = 517 | { 518 | 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 519 | 0xFF, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 520 | 0xFF, 0xFF, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 521 | 0xFF, 0xFF, 0xFF, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 522 | 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 523 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 524 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 525 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 526 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 527 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 528 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 529 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 530 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0E, 0x0D, 0x0C, 531 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0E, 0x0D, 532 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0E, 533 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 534 | }; 535 | 536 | inline uint8_t VectorMaskR[] = 537 | { 538 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 539 | 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 540 | 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 541 | 0x02, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 542 | 0x03, 0x02, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 543 | 0x04, 0x03, 0x02, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 544 | 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 545 | 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 546 | 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 547 | 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 548 | 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 549 | 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 550 | 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 551 | 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 552 | 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0xFF, 0xFF, 553 | 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0xFF, 554 | }; 555 | 556 | inline uint8_t VectorShiftTableL[] = 557 | { 558 | 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 559 | 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 560 | 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 561 | 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 562 | 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 563 | 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 564 | 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 565 | 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 566 | 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 567 | 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 568 | 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 569 | 0x1A, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 570 | 0x1B, 0x1A, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 571 | 0x1C, 0x1B, 0x1A, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 572 | 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 573 | 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 574 | }; 575 | 576 | inline uint8_t VectorShiftTableR[] = 577 | { 578 | 0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 579 | 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 580 | 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 581 | 0x1C, 0x1B, 0x1A, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 582 | 0x1B, 0x1A, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 583 | 0x1A, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 584 | 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 585 | 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 586 | 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 587 | 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 588 | 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 589 | 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 590 | 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 591 | 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 592 | 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 593 | 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 594 | }; 595 | 596 | inline __m128i _mm_adds_epu32(__m128i a, __m128i b) 597 | { 598 | return _mm_add_epi32(a, _mm_min_epu32(_mm_xor_si128(a, _mm_cmpeq_epi32(a, a)), b)); 599 | } 600 | 601 | inline __m128i _mm_avg_epi8(__m128i a, __m128i b) 602 | { 603 | __m128i c = _mm_set1_epi8(char(128)); 604 | return _mm_xor_si128(c, _mm_avg_epu8(_mm_xor_si128(c, a), _mm_xor_si128(c, b))); 605 | } 606 | 607 | inline __m128i _mm_avg_epi16(__m128i a, __m128i b) 608 | { 609 | __m128i c = _mm_set1_epi16(short(32768)); 610 | return _mm_xor_si128(c, _mm_avg_epu16(_mm_xor_si128(c, a), _mm_xor_si128(c, b))); 611 | } 612 | 613 | inline __m128 _mm_cvtepu32_ps_(__m128i src1) 614 | { 615 | __m128i xmm1 = _mm_add_epi32(src1, _mm_set1_epi32(127)); 616 | __m128i xmm0 = _mm_slli_epi32(src1, 31 - 8); 617 | xmm0 = _mm_srli_epi32(xmm0, 31); 618 | xmm0 = _mm_add_epi32(xmm0, xmm1); 619 | xmm0 = _mm_srai_epi32(xmm0, 8); 620 | xmm0 = _mm_add_epi32(xmm0, _mm_set1_epi32(0x4F800000)); 621 | __m128 xmm2 = _mm_cvtepi32_ps(src1); 622 | return _mm_blendv_ps(xmm2, _mm_castsi128_ps(xmm0), _mm_castsi128_ps(src1)); 623 | } 624 | 625 | inline __m128i _mm_perm_epi8_(__m128i a, __m128i b, __m128i c) 626 | { 627 | __m128i d = _mm_set1_epi8(0xF); 628 | __m128i e = _mm_sub_epi8(d, _mm_and_si128(c, d)); 629 | return _mm_blendv_epi8(_mm_shuffle_epi8(a, e), _mm_shuffle_epi8(b, e), _mm_slli_epi32(c, 3)); 630 | } 631 | 632 | inline __m128i _mm_cmpgt_epu8(__m128i a, __m128i b) 633 | { 634 | __m128i c = _mm_set1_epi8(char(128)); 635 | return _mm_cmpgt_epi8(_mm_xor_si128(a, c), _mm_xor_si128(b, c)); 636 | } 637 | 638 | inline __m128i _mm_cmpgt_epu16(__m128i a, __m128i b) 639 | { 640 | __m128i c = _mm_set1_epi16(short(32768)); 641 | return _mm_cmpgt_epi16(_mm_xor_si128(a, c), _mm_xor_si128(b, c)); 642 | } 643 | 644 | inline __m128i _mm_vctsxs(__m128 src1) 645 | { 646 | __m128 xmm2 = _mm_cmpunord_ps(src1, src1); 647 | __m128i xmm0 = _mm_cvttps_epi32(src1); 648 | __m128i xmm1 = _mm_cmpeq_epi32(xmm0, _mm_set1_epi32(INT_MIN)); 649 | xmm1 = _mm_andnot_si128(_mm_castps_si128(src1), xmm1); 650 | __m128 dest = _mm_blendv_ps(_mm_castsi128_ps(xmm0), _mm_castsi128_ps(_mm_set1_epi32(INT_MAX)), _mm_castsi128_ps(xmm1)); 651 | return _mm_andnot_si128(_mm_castps_si128(xmm2), _mm_castps_si128(dest)); 652 | } 653 | 654 | inline __m128i _mm_vsr(__m128i a, __m128i b) 655 | { 656 | b = _mm_srli_epi64(_mm_slli_epi64(b, 61), 61); 657 | return _mm_castps_si128(_mm_insert_ps(_mm_castsi128_ps(_mm_srl_epi64(a, b)), _mm_castsi128_ps(_mm_srl_epi64(_mm_srli_si128(a, 4), b)), 0x10)); 658 | } 659 | 660 | #endif 661 | -------------------------------------------------------------------------------- /XenonUtils/section.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | enum SectionFlags : uint8_t 6 | { 7 | SectionFlags_None = 0, 8 | SectionFlags_Data = 1, 9 | SectionFlags_Code = 2 10 | }; 11 | 12 | struct Section 13 | { 14 | std::string name{}; 15 | size_t base{}; 16 | uint32_t size{}; 17 | SectionFlags flags{}; 18 | uint8_t* data{}; 19 | 20 | bool operator<(size_t address) const 21 | { 22 | return address < base; 23 | } 24 | 25 | bool operator>(size_t address) const 26 | { 27 | return address >= (base + size); 28 | } 29 | 30 | bool operator==(size_t address) const 31 | { 32 | return address >= base && address < base + size; 33 | } 34 | }; 35 | 36 | struct SectionComparer 37 | { 38 | using is_transparent = void; 39 | 40 | bool operator()(const Section& lhs, size_t rhs) const 41 | { 42 | return lhs.base < rhs; 43 | } 44 | 45 | bool operator()(size_t lhs, const Section& rhs) const 46 | { 47 | return lhs < rhs.base; 48 | } 49 | 50 | bool operator()(const Section& lhs, const Section& rhs) const 51 | { 52 | return lhs.base < rhs.base; 53 | } 54 | }; 55 | -------------------------------------------------------------------------------- /XenonUtils/symbol.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | enum SymbolType 6 | { 7 | Symbol_None, 8 | Symbol_Section, 9 | Symbol_Function, 10 | Symbol_Comment, 11 | }; 12 | 13 | struct Symbol 14 | { 15 | mutable std::string name{}; 16 | size_t address{}; 17 | size_t size{}; 18 | mutable SymbolType type{}; 19 | 20 | Symbol() 21 | { 22 | } 23 | 24 | Symbol(std::string name, size_t address, size_t size, SymbolType type) 25 | : name(std::move(name)), address(address), size(size), type(type) 26 | { 27 | } 28 | }; 29 | 30 | struct SymbolComparer 31 | { 32 | using is_transparent = void; 33 | 34 | bool operator()(const Symbol& lhs, size_t rhs) const 35 | { 36 | return lhs.address < rhs; 37 | } 38 | 39 | bool operator()(size_t lhs, const Symbol& rhs) const 40 | { 41 | return lhs < rhs.address; 42 | } 43 | 44 | bool operator()(const Symbol& lhs, const Symbol& rhs) const 45 | { 46 | return lhs.address < rhs.address; 47 | } 48 | }; 49 | -------------------------------------------------------------------------------- /XenonUtils/symbol_table.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "symbol.h" 3 | #include 4 | 5 | class SymbolTable : public std::multiset 6 | { 7 | public: 8 | const_iterator find(size_t address) const 9 | { 10 | auto [beginIt, endIt] = equal_range(address); 11 | if (beginIt == endIt) 12 | { 13 | return end(); 14 | } 15 | 16 | size_t closest{ address - beginIt->address }; 17 | auto match = end(); 18 | for (auto it = beginIt; it != endIt; ++it) 19 | { 20 | if (address < it->address || address >= it->address + it->size) 21 | { 22 | continue; 23 | } 24 | 25 | const size_t distance = address - it->address; 26 | if (distance <= closest) 27 | { 28 | match = it; 29 | closest = distance; 30 | } 31 | } 32 | 33 | return match; 34 | } 35 | 36 | iterator find(size_t address) 37 | { 38 | auto [beginIt, endIt] = equal_range(address); 39 | if (beginIt == endIt) 40 | { 41 | return end(); 42 | } 43 | 44 | size_t closest{ address - beginIt->address }; 45 | auto match = end(); 46 | for (auto it = beginIt; it != endIt; ++it) 47 | { 48 | if (address < it->address || address >= it->address + it->size) 49 | { 50 | continue; 51 | } 52 | 53 | const size_t distance = address - it->address; 54 | if (distance <= closest) 55 | { 56 | match = it; 57 | closest = distance; 58 | } 59 | } 60 | 61 | return match; 62 | } 63 | }; 64 | -------------------------------------------------------------------------------- /XenonUtils/xbox.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "byteswap.h" 7 | 8 | #ifdef _WIN32 9 | #include 10 | #endif 11 | 12 | // real win32 handles will never use the upper bits unless something goes really wrong 13 | #define CHECK_GUEST_HANDLE(HANDLE) (((HANDLE) & 0x80000000) == 0x80000000) 14 | #define GUEST_HANDLE(HANDLE) ((HANDLE) | 0x80000000) 15 | #define HOST_HANDLE(HANDLE) ((HANDLE) & ~0x80000000) 16 | 17 | // Return true to free the associated memory 18 | typedef bool(*TypeDestructor_t)(void*); 19 | 20 | template 21 | bool DestroyObject(void* obj) 22 | { 23 | static_cast(obj)->~T(); 24 | return true; 25 | } 26 | 27 | template 28 | struct be 29 | { 30 | T value; 31 | 32 | be() : value(0) 33 | { 34 | } 35 | 36 | be(const T v) 37 | { 38 | set(v); 39 | } 40 | 41 | static T byteswap(T value) 42 | { 43 | if constexpr (std::is_same_v) 44 | { 45 | const uint64_t swapped = ByteSwap(*reinterpret_cast(&value)); 46 | return *reinterpret_cast(&swapped); 47 | } 48 | else if constexpr (std::is_same_v) 49 | { 50 | const uint32_t swapped = ByteSwap(*reinterpret_cast(&value)); 51 | return *reinterpret_cast(&swapped); 52 | } 53 | else if constexpr (std::is_enum_v) 54 | { 55 | const std::underlying_type_t swapped = ByteSwap(*reinterpret_cast*>(&value)); 56 | return *reinterpret_cast(&swapped); 57 | } 58 | else 59 | { 60 | return ByteSwap(value); 61 | } 62 | } 63 | 64 | void set(const T v) 65 | { 66 | value = byteswap(v); 67 | } 68 | 69 | T get() const 70 | { 71 | return byteswap(value); 72 | } 73 | 74 | be& operator| (T value) 75 | { 76 | set(get() | value); 77 | return *this; 78 | } 79 | 80 | be& operator& (T value) 81 | { 82 | set(get() & value); 83 | return *this; 84 | } 85 | 86 | operator T() const 87 | { 88 | return get(); 89 | } 90 | 91 | be& operator=(T v) 92 | { 93 | set(v); 94 | return *this; 95 | } 96 | }; 97 | 98 | extern "C" void* MmGetHostAddress(uint32_t ptr); 99 | template 100 | struct xpointer 101 | { 102 | be ptr; 103 | 104 | xpointer() : ptr(0) 105 | { 106 | } 107 | 108 | xpointer(T* p) : ptr(p != nullptr ? (reinterpret_cast(p) - reinterpret_cast(MmGetHostAddress(0))) : 0) 109 | { 110 | } 111 | 112 | T* get() const 113 | { 114 | if (!ptr.value) 115 | { 116 | return nullptr; 117 | } 118 | 119 | return reinterpret_cast(MmGetHostAddress(ptr)); 120 | } 121 | 122 | operator T* () const 123 | { 124 | return get(); 125 | } 126 | 127 | T* operator->() const 128 | { 129 | return get(); 130 | } 131 | }; 132 | 133 | template 134 | struct HostObject 135 | { 136 | typedef TGuest guest_type; 137 | }; 138 | 139 | struct _XLIST_ENTRY; 140 | typedef _XLIST_ENTRY XLIST_ENTRY; 141 | typedef xpointer PXLIST_ENTRY; 142 | 143 | typedef struct _IMAGE_CE_RUNTIME_FUNCTION 144 | { 145 | uint32_t BeginAddress; 146 | 147 | union 148 | { 149 | uint32_t Data; 150 | struct 151 | { 152 | uint32_t PrologLength : 8; 153 | uint32_t FunctionLength : 22; 154 | uint32_t ThirtyTwoBit : 1; 155 | uint32_t ExceptionFlag : 1; 156 | }; 157 | }; 158 | } IMAGE_CE_RUNTIME_FUNCTION; 159 | 160 | static_assert(sizeof(IMAGE_CE_RUNTIME_FUNCTION) == 8); 161 | 162 | typedef struct _XLIST_ENTRY 163 | { 164 | be Flink; 165 | be Blink; 166 | } XLIST_ENTRY; 167 | 168 | typedef struct _XDISPATCHER_HEADER 169 | { 170 | union 171 | { 172 | struct 173 | { 174 | uint8_t Type; 175 | union 176 | { 177 | uint8_t Abandoned; 178 | uint8_t Absolute; 179 | uint8_t NpxIrql; 180 | uint8_t Signalling; 181 | }; 182 | union 183 | { 184 | uint8_t Size; 185 | uint8_t Hand; 186 | }; 187 | union 188 | { 189 | uint8_t Inserted; 190 | uint8_t DebugActive; 191 | uint8_t DpcActive; 192 | }; 193 | }; 194 | be Lock; 195 | }; 196 | 197 | be SignalState; 198 | XLIST_ENTRY WaitListHead; 199 | } XDISPATCHER_HEADER, * XPDISPATCHER_HEADER; 200 | 201 | // These variables are never accessed in guest code, we can safely use them in little endian 202 | typedef struct _XRTL_CRITICAL_SECTION 203 | { 204 | XDISPATCHER_HEADER Header; 205 | int32_t LockCount; 206 | int32_t RecursionCount; 207 | uint32_t OwningThread; 208 | } XRTL_CRITICAL_SECTION; 209 | 210 | typedef struct _XANSI_STRING { 211 | be Length; 212 | be MaximumLength; 213 | xpointer Buffer; 214 | } XANSI_STRING; 215 | 216 | typedef struct _XOBJECT_ATTRIBUTES 217 | { 218 | be RootDirectory; 219 | xpointer Name; 220 | xpointer Attributes; 221 | } XOBJECT_ATTRIBUTES; 222 | 223 | typedef XDISPATCHER_HEADER XKEVENT; 224 | 225 | typedef struct _XIO_STATUS_BLOCK 226 | { 227 | union { 228 | be Status; 229 | be Pointer; 230 | }; 231 | be Information; 232 | } XIO_STATUS_BLOCK; 233 | 234 | typedef struct _XOVERLAPPED { 235 | be Internal; 236 | be InternalHigh; 237 | be Offset; 238 | be OffsetHigh; 239 | be hEvent; 240 | } XOVERLAPPED; 241 | 242 | // this name is so dumb 243 | typedef struct _XXOVERLAPPED { 244 | union 245 | { 246 | struct 247 | { 248 | be Error; 249 | be Length; 250 | }; 251 | 252 | struct 253 | { 254 | uint32_t InternalLow; 255 | uint32_t InternalHigh; 256 | }; 257 | }; 258 | uint32_t InternalContext; 259 | be hEvent; 260 | be pCompletionRoutine; 261 | be dwCompletionContext; 262 | be dwExtendedError; 263 | } XXOVERLAPPED, *PXXOVERLAPPED; 264 | 265 | static_assert(sizeof(_XXOVERLAPPED) == 0x1C); 266 | 267 | // https://learn.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-memorystatus 268 | typedef struct _XMEMORYSTATUS { 269 | be dwLength; 270 | be dwMemoryLoad; 271 | be dwTotalPhys; 272 | be dwAvailPhys; 273 | be dwTotalPageFile; 274 | be dwAvailPageFile; 275 | be dwTotalVirtual; 276 | be dwAvailVirtual; 277 | } XMEMORYSTATUS, * XLPMEMORYSTATUS; 278 | 279 | typedef struct _XVIDEO_MODE 280 | { 281 | be DisplayWidth; 282 | be DisplayHeight; 283 | be IsInterlaced; 284 | be IsWidescreen; 285 | be IsHighDefinition; 286 | be RefreshRate; 287 | be VideoStandard; 288 | be Unknown4A; 289 | be Unknown01; 290 | be reserved[3]; 291 | } XVIDEO_MODE; 292 | 293 | typedef struct _XKSEMAPHORE 294 | { 295 | XDISPATCHER_HEADER Header; 296 | be Limit; 297 | } XKSEMAPHORE; 298 | 299 | typedef struct _XUSER_SIGNIN_INFO { 300 | be xuid; 301 | be dwField08; 302 | be SigninState; 303 | be dwField10; 304 | be dwField14; 305 | char Name[16]; 306 | } XUSER_SIGNIN_INFO; 307 | 308 | typedef struct _XTIME_FIELDS 309 | { 310 | be Year; 311 | be Month; 312 | be Day; 313 | be Hour; 314 | be Minute; 315 | be Second; 316 | be Milliseconds; 317 | be Weekday; 318 | } XTIME_FIELDS, * PXTIME_FIELDS; 319 | 320 | // Content types 321 | #define XCONTENTTYPE_SAVEDATA 1 322 | #define XCONTENTTYPE_DLC 2 323 | #define XCONTENTTYPE_RESERVED 3 324 | 325 | #define XCONTENT_NEW 1 326 | #define XCONTENT_EXISTING 2 327 | 328 | #define XCONTENT_MAX_DISPLAYNAME 128 329 | #define XCONTENT_MAX_FILENAME 42 330 | #define XCONTENTDEVICE_MAX_NAME 27 331 | 332 | typedef struct _XCONTENT_DATA 333 | { 334 | be DeviceID; 335 | be dwContentType; 336 | be szDisplayName[XCONTENT_MAX_DISPLAYNAME]; 337 | char szFileName[XCONTENT_MAX_FILENAME]; 338 | } XCONTENT_DATA, * PXCONTENT_DATA; 339 | 340 | typedef struct _XHOSTCONTENT_DATA : _XCONTENT_DATA 341 | { 342 | // This is a host exclusive type so we don't care what goes on 343 | std::string szRoot{}; 344 | } XHOSTCONTENT_DATA, *PXHOSTCONTENT_DATA; 345 | 346 | 347 | #define XCONTENTDEVICETYPE_HDD 1 348 | #define XCONTENTDEVICETYPE_MU 2 349 | 350 | typedef struct _XDEVICE_DATA 351 | { 352 | be DeviceID; 353 | be DeviceType; 354 | be ulDeviceBytes; 355 | be ulDeviceFreeBytes; 356 | be wszName[XCONTENTDEVICE_MAX_NAME]; 357 | } XDEVICE_DATA, *PXDEVICE_DATA; 358 | 359 | // Direct reflection of XInput structures 360 | 361 | #define XAMINPUT_DEVTYPE_GAMEPAD 0x01 362 | #define XAMINPUT_DEVSUBTYPE_GAMEPAD 0x01 363 | 364 | #define XAMINPUT_GAMEPAD_DPAD_UP 0x0001 365 | #define XAMINPUT_GAMEPAD_DPAD_DOWN 0x0002 366 | #define XAMINPUT_GAMEPAD_DPAD_LEFT 0x0004 367 | #define XAMINPUT_GAMEPAD_DPAD_RIGHT 0x0008 368 | #define XAMINPUT_GAMEPAD_START 0x0010 369 | #define XAMINPUT_GAMEPAD_BACK 0x0020 370 | #define XAMINPUT_GAMEPAD_LEFT_THUMB 0x0040 371 | #define XAMINPUT_GAMEPAD_RIGHT_THUMB 0x0080 372 | #define XAMINPUT_GAMEPAD_LEFT_SHOULDER 0x0100 373 | #define XAMINPUT_GAMEPAD_RIGHT_SHOULDER 0x0200 374 | #define XAMINPUT_GAMEPAD_A 0x1000 375 | #define XAMINPUT_GAMEPAD_B 0x2000 376 | #define XAMINPUT_GAMEPAD_X 0x4000 377 | #define XAMINPUT_GAMEPAD_Y 0x8000 378 | 379 | typedef struct _XAMINPUT_GAMEPAD 380 | { 381 | uint16_t wButtons; 382 | uint8_t bLeftTrigger; 383 | uint8_t bRightTrigger; 384 | int16_t sThumbLX; 385 | int16_t sThumbLY; 386 | int16_t sThumbRX; 387 | int16_t sThumbRY; 388 | } XAMINPUT_GAMEPAD, *PXAMINPUT_GAMEPAD; 389 | 390 | typedef struct _XAMINPUT_VIBRATION 391 | { 392 | uint16_t wLeftMotorSpeed; 393 | uint16_t wRightMotorSpeed; 394 | } XAMINPUT_VIBRATION, * PXAMINPUT_VIBRATION; 395 | 396 | typedef struct _XAMINPUT_CAPABILITIES 397 | { 398 | uint8_t Type; 399 | uint8_t SubType; 400 | uint16_t Flags; 401 | XAMINPUT_GAMEPAD Gamepad; 402 | XAMINPUT_VIBRATION Vibration; 403 | } XAMINPUT_CAPABILITIES, * PXAMINPUT_CAPABILITIES; 404 | 405 | typedef struct _XAMINPUT_STATE 406 | { 407 | uint32_t dwPacketNumber; 408 | XAMINPUT_GAMEPAD Gamepad; 409 | } XAMINPUT_STATE, * PXAMINPUT_STATE; 410 | -------------------------------------------------------------------------------- /XenonUtils/xdbf.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define XDBF_SIGNATURE 0x58444246 6 | #define XACH_SIGNATURE 0x58414348 7 | 8 | struct XDBFHeader 9 | { 10 | be Signature; 11 | be Version; 12 | be EntryTableLength; 13 | be EntryCount; 14 | be FreeSpaceTableLength; 15 | be FreeSpaceTableEntryCount; 16 | }; 17 | 18 | enum EXDBFNamespace : uint16_t 19 | { 20 | XDBF_SPA_NAMESPACE_METADATA = 1, 21 | XDBF_SPA_NAMESPACE_IMAGE = 2, 22 | XDBF_SPA_NAMESPACE_STRING_TABLE = 3, 23 | XDBF_GPD_NAMESPACE_ACHIEVEMENT = 1, 24 | XDBF_GPD_NAMESPACE_IMAGE = 2, 25 | XDBF_GPD_NAMESPACE_SETTING = 3, 26 | XDBF_GPD_NAMESPACE_TITLE = 4, 27 | XDBF_GPD_NAMESPACE_STRING = 5, 28 | XDBF_GPD_NAMESPACE_ACHIEVEMENT_SECURITY_GFWL = 6, 29 | XDBF_GPD_NAMESPACE_AVATAR_AWARD_360 = 6 30 | }; 31 | 32 | #pragma pack(push, 1) 33 | struct XDBFEntry 34 | { 35 | be NamespaceID; 36 | be ResourceID; 37 | be Offset; 38 | be Length; 39 | }; 40 | #pragma pack(pop) 41 | 42 | struct XDBFFreeSpaceEntry 43 | { 44 | be Offset; 45 | be Length; 46 | }; 47 | 48 | enum EXDBFLanguage : uint32_t 49 | { 50 | XDBF_LANGUAGE_UNKNOWN = 0, 51 | XDBF_LANGUAGE_ENGLISH = 1, 52 | XDBF_LANGUAGE_JAPANESE = 2, 53 | XDBF_LANGUAGE_GERMAN = 3, 54 | XDBF_LANGUAGE_FRENCH = 4, 55 | XDBF_LANGUAGE_SPANISH = 5, 56 | XDBF_LANGUAGE_ITALIAN = 6, 57 | XDBF_LANGUAGE_KOREAN = 7, 58 | XDBF_LANGUAGE_CHINESE_TRAD = 8, 59 | XDBF_LANGUAGE_PORTUGUESE = 9, 60 | XDBF_LANGUAGE_CHINESE_SIMP = 10, 61 | XDBF_LANGUAGE_POLISH = 11, 62 | XDBF_LANGUAGE_RUSSIAN = 12, 63 | XDBF_LANGUAGE_MAX 64 | }; 65 | 66 | struct XSTCHeader 67 | { 68 | be Signature; 69 | be Version; 70 | be Size; 71 | be Language; 72 | }; 73 | 74 | #pragma pack(push, 1) 75 | struct XSTRHeader 76 | { 77 | be Signature; 78 | be Version; 79 | be Size; 80 | be StringCount; 81 | }; 82 | #pragma pack(pop) 83 | 84 | struct XSTREntry 85 | { 86 | be ID; 87 | be Length; 88 | }; 89 | 90 | #pragma pack(push, 1) 91 | struct XACHHeader 92 | { 93 | be Signature; 94 | be Version; 95 | be Size; 96 | be AchievementCount; 97 | }; 98 | #pragma pack(pop) 99 | 100 | enum EXACHFlags : uint32_t 101 | { 102 | XACH_TYPE_COMPLETION = 1U, 103 | XACH_TYPE_LEVELING = 2U, 104 | XACH_TYPE_UNLOCK = 3U, 105 | XACH_TYPE_EVENT = 4U, 106 | XACH_TYPE_TOURNAMENT = 5U, 107 | XACH_TYPE_CHECKPOINT = 6U, 108 | XACH_TYPE_OTHER = 7U, 109 | XACH_TYPE_MASK = 7U, 110 | XACH_STATUS_UNACHIEVED = (1U << 4), 111 | XACH_STATUS_EARNED_ONLINE = (1U << 16), 112 | XACH_STATUS_EARNED = (1U << 17), 113 | XACH_STATUS_EDITED = (1U << 20) 114 | }; 115 | 116 | struct XACHEntry 117 | { 118 | be AchievementID; 119 | be NameID; 120 | be UnlockedDescID; 121 | be LockedDescID; 122 | be ImageID; 123 | be Gamerscore; 124 | char pad0[0x02]; 125 | be Flags; 126 | char pad1[0x10]; 127 | }; 128 | 129 | union XDBFTitleID 130 | { 131 | struct 132 | { 133 | be u16; 134 | char u8[0x02]; 135 | }; 136 | 137 | be u32; 138 | }; 139 | 140 | struct XDBFTitleVersion 141 | { 142 | be Major; 143 | be Minor; 144 | be Build; 145 | be Revision; 146 | }; 147 | 148 | enum EXDBFTitleType : uint32_t 149 | { 150 | XDBF_TITLE_TYPE_SYSTEM = 0, 151 | XDBF_TITLE_TYPE_FULL = 1, 152 | XDBF_TITLE_TYPE_DEMO = 2, 153 | XDBF_TITLE_TYPE_DOWNLOAD = 3 154 | }; 155 | 156 | struct XTHDHeader 157 | { 158 | be Signature; 159 | be Version; 160 | be Size; 161 | XDBFTitleID TitleID; 162 | be Type; 163 | XDBFTitleVersion TitleVersion; 164 | char pad0[0x10]; 165 | }; 166 | 167 | #pragma pack(push, 1) 168 | struct XGAAHeader 169 | { 170 | be Signature; 171 | be Version; 172 | be Size; 173 | be Count; 174 | }; 175 | #pragma pack(pop) 176 | 177 | struct XGAAEntry 178 | { 179 | char pad0[0x04]; 180 | be AvatarAwardID; 181 | char pad1[0x06]; 182 | XDBFTitleID TitleID; 183 | be NameID; 184 | be UnlockedDescID; 185 | be LockedDescID; 186 | char pad2[0x02]; 187 | be ImageID; 188 | char pad3[0x08]; 189 | }; 190 | 191 | struct XSRCHeader 192 | { 193 | be Signature; 194 | be Version; 195 | be Size; 196 | be FileNameLength; 197 | }; 198 | 199 | struct XSRCHeader2 200 | { 201 | be UncompressedSize; 202 | be CompressedSize; 203 | }; 204 | -------------------------------------------------------------------------------- /XenonUtils/xdbf_wrapper.cpp: -------------------------------------------------------------------------------- 1 | #include "xdbf_wrapper.h" 2 | 3 | XDBFWrapper::XDBFWrapper(const uint8_t* buffer, size_t bufferSize) : pBuffer(buffer), BufferSize(bufferSize) 4 | { 5 | if (!buffer || bufferSize <= sizeof(XDBFHeader)) 6 | { 7 | pBuffer = nullptr; 8 | return; 9 | } 10 | 11 | auto seek = pBuffer; 12 | 13 | pHeader = (XDBFHeader*)seek; 14 | seek += sizeof(XDBFHeader); 15 | 16 | if (pHeader->Signature != XDBF_SIGNATURE) 17 | { 18 | pBuffer = nullptr; 19 | return; 20 | } 21 | 22 | pEntries = (XDBFEntry*)seek; 23 | seek += sizeof(XDBFEntry) * pHeader->EntryCount; 24 | 25 | pFiles = (XDBFFreeSpaceEntry*)seek; 26 | seek += sizeof(XDBFFreeSpaceEntry) * pHeader->FreeSpaceTableLength; 27 | 28 | pContent = seek; 29 | } 30 | 31 | XDBFBlock XDBFWrapper::GetResource(EXDBFNamespace ns, uint64_t id) const 32 | { 33 | for (int i = 0; i < pHeader->EntryCount; i++) 34 | { 35 | auto& entry = pEntries[i]; 36 | 37 | if (entry.NamespaceID == ns && entry.ResourceID == id) 38 | { 39 | XDBFBlock block{}; 40 | block.pBuffer = pContent + entry.Offset; 41 | block.BufferSize = entry.Length; 42 | return block; 43 | } 44 | } 45 | 46 | return { nullptr }; 47 | } 48 | 49 | std::string XDBFWrapper::GetString(EXDBFLanguage language, uint16_t id) const 50 | { 51 | auto languageBlock = GetResource(XDBF_SPA_NAMESPACE_STRING_TABLE, (uint64_t)language); 52 | 53 | if (!languageBlock) 54 | return ""; 55 | 56 | auto pHeader = (XSTRHeader*)languageBlock.pBuffer; 57 | auto seek = languageBlock.pBuffer + sizeof(XSTRHeader); 58 | 59 | for (int i = 0; i < pHeader->StringCount; i++) 60 | { 61 | auto entry = (XSTREntry*)seek; 62 | 63 | seek += sizeof(XSTREntry); 64 | 65 | if (entry->ID == id) 66 | return std::string((const char*)seek, entry->Length); 67 | 68 | seek += entry->Length; 69 | } 70 | 71 | return ""; 72 | } 73 | 74 | std::vector XDBFWrapper::GetAchievements(EXDBFLanguage language) const 75 | { 76 | std::vector result; 77 | 78 | auto achievementsBlock = GetResource(XDBF_SPA_NAMESPACE_METADATA, XACH_SIGNATURE); 79 | 80 | if (!achievementsBlock) 81 | return result; 82 | 83 | auto pHeader = (XACHHeader*)achievementsBlock.pBuffer; 84 | auto seek = achievementsBlock.pBuffer + sizeof(XACHHeader); 85 | 86 | for (int i = 0; i < pHeader->AchievementCount; i++) 87 | { 88 | auto entry = (XACHEntry*)seek; 89 | 90 | seek += sizeof(XACHEntry); 91 | 92 | Achievement achievement{}; 93 | achievement.ID = entry->AchievementID; 94 | achievement.Name = GetString(language, entry->NameID); 95 | achievement.UnlockedDesc = GetString(language, entry->UnlockedDescID); 96 | achievement.LockedDesc = GetString(language, entry->LockedDescID); 97 | achievement.Score = entry->Gamerscore; 98 | 99 | auto imageBlock = GetResource(XDBF_SPA_NAMESPACE_IMAGE, entry->ImageID); 100 | 101 | if (imageBlock) 102 | { 103 | achievement.pImageBuffer = imageBlock.pBuffer; 104 | achievement.ImageBufferSize = imageBlock.BufferSize; 105 | } 106 | 107 | result.push_back(achievement); 108 | } 109 | 110 | return result; 111 | } 112 | 113 | Achievement XDBFWrapper::GetAchievement(EXDBFLanguage language, uint16_t id) const 114 | { 115 | Achievement result{}; 116 | 117 | auto achievementsBlock = GetResource(XDBF_SPA_NAMESPACE_METADATA, 0x58414348); 118 | 119 | if (!achievementsBlock) 120 | return result; 121 | 122 | auto pHeader = (XACHHeader*)achievementsBlock.pBuffer; 123 | auto seek = achievementsBlock.pBuffer + sizeof(XACHHeader); 124 | 125 | for (int i = 0; i < pHeader->AchievementCount; i++) 126 | { 127 | auto entry = (XACHEntry*)seek; 128 | 129 | seek += sizeof(XACHEntry); 130 | 131 | if (entry->AchievementID == id) 132 | { 133 | result.ID = entry->AchievementID; 134 | result.Name = GetString(language, entry->NameID); 135 | result.UnlockedDesc = GetString(language, entry->UnlockedDescID); 136 | result.LockedDesc = GetString(language, entry->LockedDescID); 137 | result.Score = entry->Gamerscore; 138 | 139 | auto imageBlock = GetResource(XDBF_SPA_NAMESPACE_IMAGE, entry->ImageID); 140 | 141 | if (imageBlock) 142 | { 143 | result.pImageBuffer = imageBlock.pBuffer; 144 | result.ImageBufferSize = imageBlock.BufferSize; 145 | } 146 | 147 | return result; 148 | } 149 | } 150 | 151 | return result; 152 | } 153 | -------------------------------------------------------------------------------- /XenonUtils/xdbf_wrapper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "xdbf.h" 5 | 6 | struct Achievement 7 | { 8 | uint16_t ID; 9 | std::string Name; 10 | std::string UnlockedDesc; 11 | std::string LockedDesc; 12 | const uint8_t* pImageBuffer; 13 | size_t ImageBufferSize; 14 | uint16_t Score; 15 | }; 16 | 17 | struct XDBFBlock 18 | { 19 | const uint8_t* pBuffer; 20 | size_t BufferSize; 21 | 22 | operator bool() const 23 | { 24 | return pBuffer; 25 | } 26 | }; 27 | 28 | class XDBFWrapper 29 | { 30 | public: 31 | const uint8_t* pBuffer; 32 | size_t BufferSize; 33 | 34 | const uint8_t* pContent; 35 | 36 | const XDBFHeader* pHeader; 37 | const XDBFEntry* pEntries; 38 | const XDBFFreeSpaceEntry* pFiles; 39 | 40 | XDBFWrapper() {} 41 | XDBFWrapper(const uint8_t* pBuffer, size_t bufferSize); 42 | XDBFBlock GetResource(EXDBFNamespace ns, uint64_t id) const; 43 | std::string GetString(EXDBFLanguage language, uint16_t id) const; 44 | std::vector GetAchievements(EXDBFLanguage language) const; 45 | Achievement GetAchievement(EXDBFLanguage language, uint16_t id) const; 46 | }; 47 | -------------------------------------------------------------------------------- /XenonUtils/xex.cpp: -------------------------------------------------------------------------------- 1 | #include "xex.h" 2 | #include "image.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define STRINGIFY(X) #X 10 | #define XE_EXPORT(MODULE, ORDINAL, NAME, TYPE) { (ORDINAL), "__imp__" STRINGIFY(NAME) } 11 | 12 | #ifndef _WIN32 13 | 14 | typedef struct _IMAGE_DOS_HEADER { 15 | uint16_t e_magic; 16 | uint16_t e_cblp; 17 | uint16_t e_cp; 18 | uint16_t e_crlc; 19 | uint16_t e_cparhdr; 20 | uint16_t e_minalloc; 21 | uint16_t e_maxalloc; 22 | uint16_t e_ss; 23 | uint16_t e_sp; 24 | uint16_t e_csum; 25 | uint16_t e_ip; 26 | uint16_t e_cs; 27 | uint16_t e_lfarlc; 28 | uint16_t e_ovno; 29 | uint16_t e_res[4]; 30 | uint16_t e_oemid; 31 | uint16_t e_oeminfo; 32 | uint16_t e_res2[10]; 33 | uint32_t e_lfanew; 34 | } IMAGE_DOS_HEADER, * PIMAGE_DOS_HEADER; 35 | 36 | typedef struct _IMAGE_FILE_HEADER { 37 | uint16_t Machine; 38 | uint16_t NumberOfSections; 39 | uint32_t TimeDateStamp; 40 | uint32_t PointerToSymbolTable; 41 | uint32_t NumberOfSymbols; 42 | uint16_t SizeOfOptionalHeader; 43 | uint16_t Characteristics; 44 | } IMAGE_FILE_HEADER, * PIMAGE_FILE_HEADER; 45 | 46 | typedef struct _IMAGE_DATA_DIRECTORY { 47 | uint32_t VirtualAddress; 48 | uint32_t Size; 49 | } IMAGE_DATA_DIRECTORY, * PIMAGE_DATA_DIRECTORY; 50 | 51 | #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16 52 | 53 | typedef struct _IMAGE_OPTIONAL_HEADER { 54 | uint16_t Magic; 55 | uint8_t MajorLinkerVersion; 56 | uint8_t MinorLinkerVersion; 57 | uint32_t SizeOfCode; 58 | uint32_t SizeOfInitializedData; 59 | uint32_t SizeOfUninitializedData; 60 | uint32_t AddressOfEntryPoint; 61 | uint32_t BaseOfCode; 62 | uint32_t BaseOfData; 63 | uint32_t ImageBase; 64 | uint32_t SectionAlignment; 65 | uint32_t FileAlignment; 66 | uint16_t MajorOperatingSystemVersion; 67 | uint16_t MinorOperatingSystemVersion; 68 | uint16_t MajorImageVersion; 69 | uint16_t MinorImageVersion; 70 | uint16_t MajorSubsystemVersion; 71 | uint16_t MinorSubsystemVersion; 72 | uint32_t Win32VersionValue; 73 | uint32_t SizeOfImage; 74 | uint32_t SizeOfHeaders; 75 | uint32_t CheckSum; 76 | uint16_t Subsystem; 77 | uint16_t DllCharacteristics; 78 | uint32_t SizeOfStackReserve; 79 | uint32_t SizeOfStackCommit; 80 | uint32_t SizeOfHeapReserve; 81 | uint32_t SizeOfHeapCommit; 82 | uint32_t LoaderFlags; 83 | uint32_t NumberOfRvaAndSizes; 84 | IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; 85 | } IMAGE_OPTIONAL_HEADER32, * PIMAGE_OPTIONAL_HEADER32; 86 | 87 | typedef struct _IMAGE_NT_HEADERS { 88 | uint32_t Signature; 89 | IMAGE_FILE_HEADER FileHeader; 90 | IMAGE_OPTIONAL_HEADER32 OptionalHeader; 91 | } IMAGE_NT_HEADERS32, * PIMAGE_NT_HEADERS32; 92 | 93 | #define IMAGE_SIZEOF_SHORT_NAME 8 94 | 95 | typedef struct _IMAGE_SECTION_HEADER { 96 | uint8_t Name[IMAGE_SIZEOF_SHORT_NAME]; 97 | union { 98 | uint32_t PhysicalAddress; 99 | uint32_t VirtualSize; 100 | } Misc; 101 | uint32_t VirtualAddress; 102 | uint32_t SizeOfRawData; 103 | uint32_t PointerToRawData; 104 | uint32_t PointerToRelocations; 105 | uint32_t PointerToLinenumbers; 106 | uint16_t NumberOfRelocations; 107 | uint16_t NumberOfLinenumbers; 108 | uint32_t Characteristics; 109 | } IMAGE_SECTION_HEADER, * PIMAGE_SECTION_HEADER; 110 | 111 | #define IMAGE_SCN_CNT_CODE 0x00000020 112 | 113 | #endif 114 | 115 | std::unordered_map XamExports = 116 | { 117 | #include "xbox/xam_table.inc" 118 | }; 119 | 120 | std::unordered_map XboxKernelExports = 121 | { 122 | #include "xbox/xboxkrnl_table.inc" 123 | }; 124 | 125 | Image Xex2LoadImage(const uint8_t* data, size_t dataSize) 126 | { 127 | auto* header = reinterpret_cast(data); 128 | auto* security = reinterpret_cast(data + header->securityOffset); 129 | const auto* fileFormatInfo = reinterpret_cast(getOptHeaderPtr(data, XEX_HEADER_FILE_FORMAT_INFO)); 130 | 131 | Image image{}; 132 | std::unique_ptr result{}; 133 | size_t imageSize = security->imageSize; 134 | 135 | // Decompress image 136 | if (fileFormatInfo != nullptr) 137 | { 138 | assert(fileFormatInfo->compressionType <= XEX_COMPRESSION_BASIC); 139 | 140 | std::unique_ptr decryptedData; 141 | const uint8_t* srcData = nullptr; 142 | 143 | if (fileFormatInfo->encryptionType == XEX_ENCRYPTION_NORMAL) 144 | { 145 | constexpr uint32_t KeySize = 16; 146 | AES_ctx aesContext; 147 | 148 | uint8_t decryptedKey[KeySize]; 149 | memcpy(decryptedKey, security->aesKey, KeySize); 150 | AES_init_ctx_iv(&aesContext, Xex2RetailKey, AESBlankIV); 151 | AES_CBC_decrypt_buffer(&aesContext, decryptedKey, KeySize); 152 | 153 | decryptedData = std::make_unique(dataSize - header->headerSize); 154 | memcpy(decryptedData.get(), data + header->headerSize, dataSize - header->headerSize); 155 | AES_init_ctx_iv(&aesContext, decryptedKey, AESBlankIV); 156 | AES_CBC_decrypt_buffer(&aesContext, decryptedData.get(), dataSize - header->headerSize); 157 | 158 | srcData = decryptedData.get(); 159 | } 160 | else 161 | { 162 | srcData = data + header->headerSize; 163 | } 164 | 165 | if (fileFormatInfo->compressionType == XEX_COMPRESSION_NONE) 166 | { 167 | result = std::make_unique(imageSize); 168 | memcpy(result.get(), srcData, imageSize); 169 | } 170 | else if (fileFormatInfo->compressionType == XEX_COMPRESSION_BASIC) 171 | { 172 | auto* blocks = reinterpret_cast(fileFormatInfo + 1); 173 | const size_t numBlocks = (fileFormatInfo->infoSize / sizeof(Xex2FileBasicCompressionInfo)) - 1; 174 | 175 | imageSize = 0; 176 | for (size_t i = 0; i < numBlocks; i++) 177 | { 178 | imageSize += blocks[i].dataSize + blocks[i].zeroSize; 179 | } 180 | 181 | result = std::make_unique(imageSize); 182 | auto* destData = result.get(); 183 | 184 | for (size_t i = 0; i < numBlocks; i++) 185 | { 186 | memcpy(destData, srcData, blocks[i].dataSize); 187 | 188 | srcData += blocks[i].dataSize; 189 | destData += blocks[i].dataSize; 190 | 191 | memset(destData, 0, blocks[i].zeroSize); 192 | destData += blocks[i].zeroSize; 193 | } 194 | } 195 | } 196 | 197 | image.data = std::move(result); 198 | image.size = security->imageSize; 199 | 200 | // Map image 201 | const auto* dosHeader = reinterpret_cast(image.data.get()); 202 | const auto* ntHeaders = reinterpret_cast(image.data.get() + dosHeader->e_lfanew); 203 | 204 | image.base = ntHeaders->OptionalHeader.ImageBase; 205 | image.entry_point = image.base + ntHeaders->OptionalHeader.AddressOfEntryPoint; 206 | 207 | const auto numSections = ntHeaders->FileHeader.NumberOfSections; 208 | const auto* sections = reinterpret_cast(ntHeaders + 1); 209 | 210 | for (size_t i = 0; i < numSections; i++) 211 | { 212 | const auto& section = sections[i]; 213 | uint8_t flags{}; 214 | 215 | if (section.Characteristics & IMAGE_SCN_CNT_CODE) 216 | { 217 | flags |= SectionFlags_Code; 218 | } 219 | 220 | image.Map(reinterpret_cast(section.Name), section.VirtualAddress, 221 | section.Misc.VirtualSize, flags, image.data.get() + section.VirtualAddress); 222 | } 223 | 224 | auto* imports = reinterpret_cast(getOptHeaderPtr(data, XEX_HEADER_IMPORT_LIBRARIES)); 225 | if (imports != nullptr) 226 | { 227 | std::vector stringTable; 228 | auto* pStrTable = reinterpret_cast(imports + 1); 229 | 230 | for (size_t i = 0; i < imports->numImports; i++) 231 | { 232 | stringTable.emplace_back(pStrTable); 233 | pStrTable += strlen(pStrTable) + 1; 234 | } 235 | 236 | auto* library = (Xex2ImportLibrary*)(((char*)imports) + sizeof(Xex2ImportHeader) + imports->sizeOfStringTable); 237 | for (size_t i = 0; i < stringTable.size(); i++) 238 | { 239 | auto* descriptors = (Xex2ImportDescriptor*)(library + 1); 240 | static std::unordered_map DummyExports; 241 | const std::unordered_map* names = &DummyExports; 242 | 243 | if (stringTable[i] == "xam.xex") 244 | { 245 | names = &XamExports; 246 | } 247 | else if (stringTable[i] == "xboxkrnl.exe") 248 | { 249 | names = &XboxKernelExports; 250 | } 251 | 252 | for (size_t im = 0; im < library->numberOfImports; im++) 253 | { 254 | auto originalThunk = (Xex2ThunkData*)image.Find(descriptors[im].firstThunk); 255 | auto originalData = originalThunk; 256 | originalData->data = ByteSwap(originalData->data); 257 | 258 | if (originalData->originalData.type != 0) 259 | { 260 | uint32_t thunk[4] = { 0x00000060, 0x00000060, 0x00000060, 0x2000804E }; 261 | auto name = names->find(originalData->originalData.ordinal); 262 | if (name != names->end()) 263 | { 264 | image.symbols.insert({ name->second, descriptors[im].firstThunk, sizeof(thunk), Symbol_Function }); 265 | } 266 | 267 | memcpy(originalThunk, thunk, sizeof(thunk)); 268 | } 269 | } 270 | library = (Xex2ImportLibrary*)((char*)(library + 1) + library->numberOfImports * sizeof(Xex2ImportDescriptor)); 271 | } 272 | } 273 | 274 | return image; 275 | } 276 | -------------------------------------------------------------------------------- /XenonUtils/xex.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "xbox.h" 4 | 5 | inline constexpr uint8_t Xex2RetailKey[16] = { 0x20, 0xB1, 0x85, 0xA5, 0x9D, 0x28, 0xFD, 0xC3, 0x40, 0x58, 0x3F, 0xBB, 0x08, 0x96, 0xBF, 0x91 }; 6 | inline constexpr uint8_t AESBlankIV[16] = {}; 7 | 8 | enum Xex2ModuleFlags 9 | { 10 | XEX_MODULE_MODULE_PATCH = 0x10, 11 | XEX_MODULE_PATCH_FULL = 0x20, 12 | XEX_MODULE_PATCH_DELTA = 0x40, 13 | }; 14 | 15 | enum Xex2HeaderKeys 16 | { 17 | XEX_HEADER_RESOURCE_INFO = 0x000002FF, 18 | XEX_HEADER_FILE_FORMAT_INFO = 0x000003FF, 19 | XEX_HEADER_DELTA_PATCH_DESCRIPTOR = 0x000005FF, 20 | XEX_HEADER_BASE_REFERENCE = 0x00000405, 21 | XEX_HEADER_BOUNDING_PATH = 0x000080FF, 22 | XEX_HEADER_DEVICE_ID = 0x00008105, 23 | XEX_HEADER_ORIGINAL_BASE_ADDRESS = 0x00010001, 24 | XEX_HEADER_ENTRY_POINT = 0x00010100, 25 | XEX_HEADER_IMAGE_BASE_ADDRESS = 0x00010201, 26 | XEX_HEADER_IMPORT_LIBRARIES = 0x000103FF, 27 | XEX_HEADER_CHECKSUM_TIMESTAMP = 0x00018002, 28 | XEX_HEADER_ENABLED_FOR_CALLCAP = 0x00018102, 29 | XEX_HEADER_ENABLED_FOR_FASTCAP = 0x00018200, 30 | XEX_HEADER_ORIGINAL_PE_NAME = 0x000183FF, 31 | XEX_HEADER_STATIC_LIBRARIES = 0x000200FF, 32 | XEX_HEADER_TLS_INFO = 0x00020104, 33 | XEX_HEADER_DEFAULT_STACK_SIZE = 0x00020200, 34 | XEX_HEADER_DEFAULT_FILESYSTEM_CACHE_SIZE = 0x00020301, 35 | XEX_HEADER_DEFAULT_HEAP_SIZE = 0x00020401, 36 | XEX_HEADER_PAGE_HEAP_SIZE_AND_FLAGS = 0x00028002, 37 | XEX_HEADER_SYSTEM_FLAGS = 0x00030000, 38 | XEX_HEADER_EXECUTION_INFO = 0x00040006, 39 | XEX_HEADER_TITLE_WORKSPACE_SIZE = 0x00040201, 40 | XEX_HEADER_GAME_RATINGS = 0x00040310, 41 | XEX_HEADER_LAN_KEY = 0x00040404, 42 | XEX_HEADER_XBOX360_LOGO = 0x000405FF, 43 | XEX_HEADER_MULTIDISC_MEDIA_IDS = 0x000406FF, 44 | XEX_HEADER_ALTERNATE_TITLE_IDS = 0x000407FF, 45 | XEX_HEADER_ADDITIONAL_TITLE_MEMORY = 0x00040801, 46 | XEX_HEADER_EXPORTS_BY_NAME = 0x00E10402, 47 | }; 48 | 49 | enum Xex2EncryptionType 50 | { 51 | XEX_ENCRYPTION_NONE = 0, 52 | XEX_ENCRYPTION_NORMAL = 1, 53 | }; 54 | 55 | enum Xex2CompressionType 56 | { 57 | XEX_COMPRESSION_NONE = 0, 58 | XEX_COMPRESSION_BASIC = 1, 59 | XEX_COMPRESSION_NORMAL = 2, 60 | XEX_COMPRESSION_DELTA = 3, 61 | }; 62 | 63 | enum Xex2SectionType 64 | { 65 | XEX_SECTION_CODE = 1, 66 | XEX_SECTION_DATA = 2, 67 | XEX_SECTION_READONLY_DATA = 3, 68 | }; 69 | 70 | enum Xex2ThunkTypes 71 | { 72 | XEX_THUNK_VARIABLE = 0, 73 | XEX_THUNK_FUNCTION = 1, 74 | }; 75 | 76 | struct Xex2OptHeader 77 | { 78 | be key; 79 | 80 | union 81 | { 82 | be value; 83 | be offset; 84 | }; 85 | }; 86 | 87 | struct Xex2Header 88 | { 89 | be magic; 90 | be moduleFlags; 91 | be headerSize; 92 | be reserved; 93 | be securityOffset; 94 | be headerCount; 95 | }; 96 | 97 | struct Xex2PageDescriptor 98 | { 99 | union 100 | { 101 | // Must be endian-swapped before reading the bitfield. 102 | uint32_t beValue; 103 | struct 104 | { 105 | uint32_t info : 4; 106 | uint32_t pageCount : 28; 107 | }; 108 | }; 109 | 110 | char dataDigest[0x14]; 111 | }; 112 | 113 | struct Xex2SecurityInfo 114 | { 115 | be headerSize; 116 | be imageSize; 117 | char rsaSignature[0x100]; 118 | be unknown; 119 | be imageFlags; 120 | be loadAddress; 121 | char sectionDigest[0x14]; 122 | be importTableCount; 123 | char importTableDigest[0x14]; 124 | char xgd2MediaId[0x10]; 125 | char aesKey[0x10]; 126 | be exportTable; 127 | char headerDigest[0x14]; 128 | be region; 129 | be allowedMediaTypes; 130 | be pageDescriptorCount; 131 | }; 132 | 133 | struct Xex2DeltaPatch 134 | { 135 | be oldAddress; 136 | be newAddress; 137 | be uncompressedLength; 138 | be compressedLength; 139 | char patchData[1]; 140 | }; 141 | 142 | struct Xex2OptDeltaPatchDescriptor 143 | { 144 | be size; 145 | be targetVersionValue; 146 | be sourceVersionValue; 147 | uint8_t digestSource[0x14]; 148 | uint8_t imageKeySource[0x10]; 149 | be sizeOfTargetHeaders; 150 | be deltaHeadersSourceOffset; 151 | be deltaHeadersSourceSize; 152 | be deltaHeadersTargetOffset; 153 | be deltaImageSourceOffset; 154 | be deltaImageSourceSize; 155 | be deltaImageTargetOffset; 156 | Xex2DeltaPatch info; 157 | }; 158 | 159 | struct Xex2FileBasicCompressionBlock 160 | { 161 | be dataSize; 162 | be zeroSize; 163 | }; 164 | 165 | struct Xex2FileBasicCompressionInfo 166 | { 167 | Xex2FileBasicCompressionBlock firstBlock; 168 | }; 169 | 170 | struct Xex2CompressedBlockInfo 171 | { 172 | be blockSize; 173 | uint8_t blockHash[20]; 174 | }; 175 | 176 | struct Xex2FileNormalCompressionInfo 177 | { 178 | be windowSize; 179 | Xex2CompressedBlockInfo firstBlock; 180 | }; 181 | 182 | struct Xex2OptFileFormatInfo 183 | { 184 | be infoSize; 185 | be encryptionType; 186 | be compressionType; 187 | }; 188 | 189 | struct Xex2ImportHeader 190 | { 191 | be sizeOfHeader; 192 | be sizeOfStringTable; 193 | be numImports; 194 | }; 195 | 196 | struct Xex2ImportLibrary 197 | { 198 | be size; 199 | char nextImportDigest[0x14]; 200 | be id; 201 | be version; 202 | be minVersion; 203 | be name; 204 | be numberOfImports; 205 | }; 206 | 207 | struct Xex2ImportDescriptor 208 | { 209 | be firstThunk; // VA XEX_THUNK_DATA 210 | }; 211 | 212 | struct Xex2ThunkData 213 | { 214 | union 215 | { 216 | struct 217 | { 218 | uint16_t ordinal : 16; 219 | uint16_t hint : 8; 220 | uint16_t type : 8; 221 | } originalData; 222 | 223 | be ordinal; 224 | be function; 225 | be addressOfData; 226 | 227 | // For easier swapping 228 | uint32_t data; 229 | }; 230 | }; 231 | 232 | struct Xex2ResourceInfo 233 | { 234 | be sizeOfHeader; 235 | uint8_t resourceID[8]; 236 | be offset; 237 | be sizeOfData; 238 | }; 239 | 240 | inline const void* getOptHeaderPtr(const uint8_t* moduleBytes, uint32_t headerKey) 241 | { 242 | const Xex2Header* xex2Header = (const Xex2Header*)(moduleBytes); 243 | for (uint32_t i = 0; i < xex2Header->headerCount; i++) 244 | { 245 | const Xex2OptHeader& optHeader = ((const Xex2OptHeader*)(xex2Header + 1))[i]; 246 | if (optHeader.key == headerKey) 247 | { 248 | if ((headerKey & 0xFF) == 0) 249 | { 250 | return &optHeader.value; 251 | } 252 | else 253 | { 254 | return &moduleBytes[optHeader.offset]; 255 | } 256 | } 257 | } 258 | 259 | return nullptr; 260 | } 261 | 262 | struct Image; 263 | Image Xex2LoadImage(const uint8_t* data, size_t dataSize); 264 | -------------------------------------------------------------------------------- /XenonUtils/xex_patcher.cpp: -------------------------------------------------------------------------------- 1 | // Referenced from: https://github.com/xenia-canary/xenia-canary/blob/canary_experimental/src/xenia/cpu/xex_module.cc 2 | 3 | /** 4 | ****************************************************************************** 5 | * Xenia : Xbox 360 Emulator Research Project * 6 | ****************************************************************************** 7 | * Copyright 2023 Ben Vanik. All rights reserved. * 8 | * Released under the BSD license - see LICENSE in the root for more details. * 9 | ****************************************************************************** 10 | */ 11 | 12 | #include "xex_patcher.h" 13 | #include "xex.h" 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "memory_mapped_file.h" 26 | 27 | struct mspack_memory_file 28 | { 29 | mspack_system sys; 30 | void *buffer; 31 | size_t bufferSize; 32 | size_t offset; 33 | }; 34 | 35 | static mspack_memory_file *mspack_memory_open(mspack_system *sys, void *buffer, size_t bufferSize) 36 | { 37 | assert(bufferSize < INT_MAX); 38 | 39 | if (bufferSize >= INT_MAX) 40 | { 41 | return nullptr; 42 | } 43 | 44 | mspack_memory_file *memoryFile = (mspack_memory_file *)(std::calloc(1, sizeof(mspack_memory_file))); 45 | if (memoryFile == nullptr) 46 | { 47 | return memoryFile; 48 | } 49 | 50 | memoryFile->buffer = buffer; 51 | memoryFile->bufferSize = bufferSize; 52 | memoryFile->offset = 0; 53 | return memoryFile; 54 | } 55 | 56 | static void mspack_memory_close(mspack_memory_file *file) 57 | { 58 | std::free(file); 59 | } 60 | 61 | static int mspack_memory_read(mspack_file *file, void *buffer, int chars) 62 | { 63 | mspack_memory_file *memoryFile = (mspack_memory_file *)(file); 64 | const size_t remaining = memoryFile->bufferSize - memoryFile->offset; 65 | const size_t total = std::min(size_t(chars), remaining); 66 | std::memcpy(buffer, (uint8_t *)(memoryFile->buffer) + memoryFile->offset, total); 67 | memoryFile->offset += total; 68 | return int(total); 69 | } 70 | 71 | static int mspack_memory_write(mspack_file *file, void *buffer, int chars) 72 | { 73 | mspack_memory_file *memoryFile = (mspack_memory_file *)(file); 74 | const size_t remaining = memoryFile->bufferSize - memoryFile->offset; 75 | const size_t total = std::min(size_t(chars), remaining); 76 | std::memcpy((uint8_t *)(memoryFile->buffer) + memoryFile->offset, buffer, total); 77 | memoryFile->offset += total; 78 | return int(total); 79 | } 80 | 81 | static void *mspack_memory_alloc(mspack_system *sys, size_t chars) 82 | { 83 | return std::calloc(chars, 1); 84 | } 85 | 86 | static void mspack_memory_free(void *ptr) 87 | { 88 | std::free(ptr); 89 | } 90 | 91 | static void mspack_memory_copy(void *src, void *dest, size_t chars) 92 | { 93 | std::memcpy(dest, src, chars); 94 | } 95 | 96 | static mspack_system *mspack_memory_sys_create() 97 | { 98 | auto sys = (mspack_system *)(std::calloc(1, sizeof(mspack_system))); 99 | if (!sys) 100 | { 101 | return nullptr; 102 | } 103 | 104 | sys->read = mspack_memory_read; 105 | sys->write = mspack_memory_write; 106 | sys->alloc = mspack_memory_alloc; 107 | sys->free = mspack_memory_free; 108 | sys->copy = mspack_memory_copy; 109 | return sys; 110 | } 111 | 112 | static void mspack_memory_sys_destroy(struct mspack_system *sys) 113 | { 114 | free(sys); 115 | } 116 | 117 | #if defined(_WIN32) 118 | inline bool bitScanForward(uint32_t v, uint32_t *outFirstSetIndex) 119 | { 120 | return _BitScanForward((unsigned long *)(outFirstSetIndex), v) != 0; 121 | } 122 | 123 | inline bool bitScanForward(uint64_t v, uint32_t *outFirstSetIndex) 124 | { 125 | return _BitScanForward64((unsigned long *)(outFirstSetIndex), v) != 0; 126 | } 127 | 128 | #else 129 | inline bool bitScanForward(uint32_t v, uint32_t *outFirstSetIndex) 130 | { 131 | int i = ffs(v); 132 | *outFirstSetIndex = i - 1; 133 | return i != 0; 134 | } 135 | 136 | inline bool bitScanForward(uint64_t v, uint32_t *outFirstSetIndex) 137 | { 138 | int i = __builtin_ffsll(v); 139 | *outFirstSetIndex = i - 1; 140 | return i != 0; 141 | } 142 | #endif 143 | 144 | static int lzxDecompress(const void *lzxData, size_t lzxLength, void *dst, size_t dstLength, uint32_t windowSize, void *windowData, size_t windowDataLength) 145 | { 146 | int resultCode = 1; 147 | uint32_t windowBits; 148 | if (!bitScanForward(windowSize, &windowBits)) { 149 | return resultCode; 150 | } 151 | 152 | mspack_system *sys = mspack_memory_sys_create(); 153 | mspack_memory_file *lzxSrc = mspack_memory_open(sys, (void *)(lzxData), lzxLength); 154 | mspack_memory_file *lzxDst = mspack_memory_open(sys, dst, dstLength); 155 | lzxd_stream *lzxd = lzxd_init(sys, (mspack_file *)(lzxSrc), (mspack_file *)(lzxDst), windowBits, 0, 0x8000, dstLength, 0); 156 | if (lzxd != nullptr) { 157 | if (windowData != nullptr) { 158 | size_t paddingLength = windowSize - windowDataLength; 159 | std::memset(&lzxd->window[0], 0, paddingLength); 160 | std::memcpy(&lzxd->window[paddingLength], windowData, windowDataLength); 161 | lzxd->ref_data_size = windowSize; 162 | } 163 | 164 | resultCode = lzxd_decompress(lzxd, dstLength); 165 | lzxd_free(lzxd); 166 | } 167 | 168 | if (lzxSrc) { 169 | mspack_memory_close(lzxSrc); 170 | } 171 | 172 | if (lzxDst) { 173 | mspack_memory_close(lzxDst); 174 | } 175 | 176 | if (sys) { 177 | mspack_memory_sys_destroy(sys); 178 | } 179 | 180 | return resultCode; 181 | } 182 | 183 | static int lzxDeltaApplyPatch(const Xex2DeltaPatch *deltaPatch, uint32_t patchLength, uint32_t windowSize, uint8_t *dstData) 184 | { 185 | const void *patchEnd = (const uint8_t *)(deltaPatch) + patchLength; 186 | const Xex2DeltaPatch *curPatch = deltaPatch; 187 | while (patchEnd > curPatch) 188 | { 189 | int patchSize = -4; 190 | if (curPatch->compressedLength == 0 && curPatch->uncompressedLength == 0 && curPatch->newAddress == 0 && curPatch->oldAddress == 0) 191 | { 192 | // End of patch. 193 | break; 194 | } 195 | 196 | switch (curPatch->compressedLength) 197 | { 198 | case 0: 199 | // Set the data to zeroes. 200 | std::memset(&dstData[curPatch->newAddress], 0, curPatch->uncompressedLength); 201 | break; 202 | case 1: 203 | // Move the data. 204 | std::memcpy(&dstData[curPatch->newAddress], &dstData[curPatch->oldAddress], curPatch->uncompressedLength); 205 | break; 206 | default: 207 | // Decompress the data into the destination. 208 | patchSize = curPatch->compressedLength - 4; 209 | int result = lzxDecompress(curPatch->patchData, curPatch->compressedLength, &dstData[curPatch->newAddress], curPatch->uncompressedLength, windowSize, &dstData[curPatch->oldAddress], curPatch->uncompressedLength); 210 | if (result != 0) 211 | { 212 | return result; 213 | } 214 | 215 | break; 216 | } 217 | 218 | curPatch++; 219 | curPatch = (const Xex2DeltaPatch *)((const uint8_t *)(curPatch) + patchSize); 220 | } 221 | 222 | return 0; 223 | } 224 | 225 | XexPatcher::Result XexPatcher::apply(const uint8_t* xexBytes, size_t xexBytesSize, const uint8_t* patchBytes, size_t patchBytesSize, std::vector &outBytes, bool skipData) 226 | { 227 | // Validate headers. 228 | static const char Xex2Magic[] = "XEX2"; 229 | const Xex2Header *xexHeader = (const Xex2Header *)(xexBytes); 230 | if (memcmp(xexBytes, Xex2Magic, 4) != 0) 231 | { 232 | return Result::XexFileInvalid; 233 | } 234 | 235 | const Xex2Header *patchHeader = (const Xex2Header *)(patchBytes); 236 | if (memcmp(patchBytes, Xex2Magic, 4) != 0) 237 | { 238 | return Result::PatchFileInvalid; 239 | } 240 | 241 | if ((patchHeader->moduleFlags & (XEX_MODULE_MODULE_PATCH | XEX_MODULE_PATCH_DELTA | XEX_MODULE_PATCH_FULL)) == 0) 242 | { 243 | return Result::PatchFileInvalid; 244 | } 245 | 246 | // Validate patch. 247 | const Xex2OptDeltaPatchDescriptor *patchDescriptor = (const Xex2OptDeltaPatchDescriptor *)(getOptHeaderPtr(patchBytes, XEX_HEADER_DELTA_PATCH_DESCRIPTOR)); 248 | if (patchDescriptor == nullptr) 249 | { 250 | return Result::PatchFileInvalid; 251 | } 252 | 253 | const Xex2OptFileFormatInfo *patchFileFormatInfo = (const Xex2OptFileFormatInfo *)(getOptHeaderPtr(patchBytes, XEX_HEADER_FILE_FORMAT_INFO)); 254 | if (patchFileFormatInfo == nullptr) 255 | { 256 | return Result::PatchFileInvalid; 257 | } 258 | 259 | if (patchFileFormatInfo->compressionType != XEX_COMPRESSION_DELTA) 260 | { 261 | return Result::PatchFileInvalid; 262 | } 263 | 264 | if (patchDescriptor->deltaHeadersSourceOffset > xexHeader->headerSize) 265 | { 266 | return Result::PatchIncompatible; 267 | } 268 | 269 | if (patchDescriptor->deltaHeadersSourceSize > (xexHeader->headerSize - patchDescriptor->deltaHeadersSourceOffset)) 270 | { 271 | return Result::PatchIncompatible; 272 | } 273 | 274 | if (patchDescriptor->deltaHeadersTargetOffset > patchDescriptor->sizeOfTargetHeaders) 275 | { 276 | return Result::PatchIncompatible; 277 | } 278 | 279 | uint32_t deltaTargetSize = patchDescriptor->sizeOfTargetHeaders - patchDescriptor->deltaHeadersTargetOffset; 280 | if (patchDescriptor->deltaHeadersSourceSize > deltaTargetSize) 281 | { 282 | return Result::PatchIncompatible; 283 | } 284 | 285 | // Apply patch. 286 | uint32_t headerTargetSize = patchDescriptor->sizeOfTargetHeaders; 287 | if (headerTargetSize == 0) 288 | { 289 | headerTargetSize = patchDescriptor->deltaHeadersTargetOffset + patchDescriptor->deltaHeadersSourceSize; 290 | } 291 | 292 | // Create the bytes for the new XEX header. Copy over the existing data. 293 | uint32_t newXexHeaderSize = std::max(headerTargetSize, xexHeader->headerSize.get()); 294 | outBytes.resize(newXexHeaderSize); 295 | memset(outBytes.data(), 0, newXexHeaderSize); 296 | memcpy(outBytes.data(), xexBytes, headerTargetSize); 297 | 298 | Xex2Header *newXexHeader = (Xex2Header *)(outBytes.data()); 299 | if (patchDescriptor->deltaHeadersSourceOffset > 0) 300 | { 301 | memcpy(&outBytes[patchDescriptor->deltaHeadersTargetOffset], &outBytes[patchDescriptor->deltaHeadersSourceOffset], patchDescriptor->deltaHeadersSourceSize); 302 | } 303 | 304 | int resultCode = lzxDeltaApplyPatch(&patchDescriptor->info, patchDescriptor->size, ((const Xex2FileNormalCompressionInfo*)(patchFileFormatInfo + 1))->windowSize, outBytes.data()); 305 | if (resultCode != 0) 306 | { 307 | return Result::PatchFailed; 308 | } 309 | 310 | // Make the header the specified size by the patch. 311 | outBytes.resize(headerTargetSize); 312 | newXexHeader = (Xex2Header *)(outBytes.data()); 313 | 314 | // Copy the rest of the data. 315 | const Xex2SecurityInfo *newSecurityInfo = (const Xex2SecurityInfo *)(&outBytes[newXexHeader->securityOffset]); 316 | outBytes.resize(outBytes.size() + newSecurityInfo->imageSize); 317 | memset(&outBytes[headerTargetSize], 0, outBytes.size() - headerTargetSize); 318 | memcpy(&outBytes[headerTargetSize], &xexBytes[xexHeader->headerSize], xexBytesSize - xexHeader->headerSize); 319 | newXexHeader = (Xex2Header *)(outBytes.data()); 320 | newSecurityInfo = (const Xex2SecurityInfo *)(&outBytes[newXexHeader->securityOffset]); 321 | 322 | // Decrypt the keys and validate that the patch is compatible with the base file. 323 | constexpr uint32_t KeySize = 16; 324 | const Xex2SecurityInfo *originalSecurityInfo = (const Xex2SecurityInfo *)(&xexBytes[xexHeader->securityOffset]); 325 | const Xex2SecurityInfo *patchSecurityInfo = (const Xex2SecurityInfo *)(&patchBytes[patchHeader->securityOffset]); 326 | uint8_t decryptedOriginalKey[KeySize]; 327 | uint8_t decryptedNewKey[KeySize]; 328 | uint8_t decryptedPatchKey[KeySize]; 329 | uint8_t decrpytedImageKeySource[KeySize]; 330 | memcpy(decryptedOriginalKey, originalSecurityInfo->aesKey, KeySize); 331 | memcpy(decryptedNewKey, newSecurityInfo->aesKey, KeySize); 332 | memcpy(decryptedPatchKey, patchSecurityInfo->aesKey, KeySize); 333 | memcpy(decrpytedImageKeySource, patchDescriptor->imageKeySource, KeySize); 334 | 335 | AES_ctx aesContext; 336 | AES_init_ctx_iv(&aesContext, Xex2RetailKey, AESBlankIV); 337 | AES_CBC_decrypt_buffer(&aesContext, decryptedOriginalKey, KeySize); 338 | 339 | AES_ctx_set_iv(&aesContext, AESBlankIV); 340 | AES_CBC_decrypt_buffer(&aesContext, decryptedNewKey, KeySize); 341 | 342 | AES_init_ctx_iv(&aesContext, decryptedNewKey, AESBlankIV); 343 | AES_CBC_decrypt_buffer(&aesContext, decryptedPatchKey, KeySize); 344 | 345 | AES_ctx_set_iv(&aesContext, AESBlankIV); 346 | AES_CBC_decrypt_buffer(&aesContext, decrpytedImageKeySource, KeySize); 347 | 348 | // Validate the patch's key matches the one from the original XEX. 349 | if (memcmp(decrpytedImageKeySource, decryptedOriginalKey, KeySize) != 0) 350 | { 351 | return Result::PatchIncompatible; 352 | } 353 | 354 | // Don't process the rest of the patch. 355 | if (skipData) 356 | { 357 | return Result::Success; 358 | } 359 | 360 | // Decrypt base XEX if necessary. 361 | const Xex2OptFileFormatInfo *fileFormatInfo = (const Xex2OptFileFormatInfo *)(getOptHeaderPtr(xexBytes, XEX_HEADER_FILE_FORMAT_INFO)); 362 | if (fileFormatInfo == nullptr) 363 | { 364 | return Result::XexFileInvalid; 365 | } 366 | 367 | if (fileFormatInfo->encryptionType == XEX_ENCRYPTION_NORMAL) 368 | { 369 | AES_init_ctx_iv(&aesContext, decryptedOriginalKey, AESBlankIV); 370 | AES_CBC_decrypt_buffer(&aesContext, &outBytes[headerTargetSize], xexBytesSize - xexHeader->headerSize); 371 | } 372 | else if (fileFormatInfo->encryptionType != XEX_ENCRYPTION_NONE) 373 | { 374 | return Result::XexFileInvalid; 375 | } 376 | 377 | // Decompress base XEX if necessary. 378 | if (fileFormatInfo->compressionType == XEX_COMPRESSION_BASIC) 379 | { 380 | const Xex2FileBasicCompressionBlock *blocks = &((const Xex2FileBasicCompressionInfo*)(fileFormatInfo + 1))->firstBlock; 381 | int32_t numBlocks = (fileFormatInfo->infoSize / sizeof(Xex2FileBasicCompressionBlock)) - 1; 382 | int32_t baseCompressedSize = 0; 383 | int32_t baseImageSize = 0; 384 | for (int32_t i = 0; i < numBlocks; i++) { 385 | baseCompressedSize += blocks[i].dataSize; 386 | baseImageSize += blocks[i].dataSize + blocks[i].zeroSize; 387 | } 388 | 389 | if (outBytes.size() < (headerTargetSize + baseImageSize)) 390 | { 391 | return Result::XexFileInvalid; 392 | } 393 | 394 | // Reverse iteration allows to perform this decompression in place. 395 | uint8_t *srcDataCursor = outBytes.data() + headerTargetSize + baseCompressedSize; 396 | uint8_t *outDataCursor = outBytes.data() + headerTargetSize + baseImageSize; 397 | for (int32_t i = numBlocks - 1; i >= 0; i--) 398 | { 399 | outDataCursor -= blocks[i].zeroSize; 400 | memset(outDataCursor, 0, blocks[i].zeroSize); 401 | outDataCursor -= blocks[i].dataSize; 402 | srcDataCursor -= blocks[i].dataSize; 403 | memmove(outDataCursor, srcDataCursor, blocks[i].dataSize); 404 | } 405 | } 406 | else if (fileFormatInfo->compressionType == XEX_COMPRESSION_NORMAL || fileFormatInfo->compressionType == XEX_COMPRESSION_DELTA) 407 | { 408 | return Result::XexFileUnsupported; 409 | } 410 | else if (fileFormatInfo->compressionType != XEX_COMPRESSION_NONE) 411 | { 412 | return Result::XexFileInvalid; 413 | } 414 | 415 | Xex2OptFileFormatInfo *newFileFormatInfo = (Xex2OptFileFormatInfo *)(getOptHeaderPtr(outBytes.data(), XEX_HEADER_FILE_FORMAT_INFO)); 416 | if (newFileFormatInfo == nullptr) 417 | { 418 | return Result::PatchFailed; 419 | } 420 | 421 | // Update the header to indicate no encryption or compression is used. 422 | newFileFormatInfo->encryptionType = XEX_ENCRYPTION_NONE; 423 | newFileFormatInfo->compressionType = XEX_COMPRESSION_NONE; 424 | 425 | // Copy and decrypt patch data if necessary. 426 | std::vector patchData; 427 | patchData.resize(patchBytesSize - patchHeader->headerSize); 428 | memcpy(patchData.data(), &patchBytes[patchHeader->headerSize], patchData.size()); 429 | 430 | if (patchFileFormatInfo->encryptionType == XEX_ENCRYPTION_NORMAL) 431 | { 432 | AES_init_ctx_iv(&aesContext, decryptedPatchKey, AESBlankIV); 433 | AES_CBC_decrypt_buffer(&aesContext, patchData.data(), patchData.size()); 434 | } 435 | else if (patchFileFormatInfo->encryptionType != XEX_ENCRYPTION_NONE) 436 | { 437 | return Result::PatchFileInvalid; 438 | } 439 | 440 | const Xex2CompressedBlockInfo *currentBlock = &((const Xex2FileNormalCompressionInfo*)(patchFileFormatInfo + 1))->firstBlock; 441 | uint8_t *outExe = &outBytes[newXexHeader->headerSize]; 442 | if (patchDescriptor->deltaImageSourceOffset > 0) 443 | { 444 | memcpy(&outExe[patchDescriptor->deltaImageTargetOffset], &outExe[patchDescriptor->deltaImageSourceOffset], patchDescriptor->deltaImageSourceSize); 445 | } 446 | 447 | static const uint32_t DigestSize = 20; 448 | uint8_t sha1Digest[DigestSize]; 449 | sha1::SHA1 sha1Context; 450 | uint8_t *patchDataCursor = patchData.data(); 451 | while (currentBlock->blockSize > 0) 452 | { 453 | const Xex2CompressedBlockInfo *nextBlock = (const Xex2CompressedBlockInfo *)(patchDataCursor); 454 | 455 | // Hash and validate the block. 456 | sha1Context.reset(); 457 | sha1Context.processBytes(patchDataCursor, currentBlock->blockSize); 458 | sha1Context.finalize(sha1Digest); 459 | if (memcmp(sha1Digest, currentBlock->blockHash, DigestSize) != 0) 460 | { 461 | return Result::PatchFailed; 462 | } 463 | 464 | patchDataCursor += 24; 465 | 466 | // Apply the block's patch data. 467 | uint32_t blockDataSize = currentBlock->blockSize - 24; 468 | if (lzxDeltaApplyPatch((const Xex2DeltaPatch *)(patchDataCursor), blockDataSize, ((const Xex2FileNormalCompressionInfo*)(patchFileFormatInfo + 1))->windowSize, outExe) != 0) 469 | { 470 | return Result::PatchFailed; 471 | } 472 | 473 | patchDataCursor += blockDataSize; 474 | currentBlock = nextBlock; 475 | } 476 | 477 | return Result::Success; 478 | } 479 | 480 | XexPatcher::Result XexPatcher::apply(const std::filesystem::path &baseXexPath, const std::filesystem::path &patchXexPath, const std::filesystem::path &newXexPath) 481 | { 482 | MemoryMappedFile baseXexFile(baseXexPath); 483 | MemoryMappedFile patchFile(patchXexPath); 484 | if (!baseXexFile.isOpen() || !patchFile.isOpen()) 485 | { 486 | return Result::FileOpenFailed; 487 | } 488 | 489 | std::vector newXexBytes; 490 | Result result = apply(baseXexFile.data(), baseXexFile.size(), patchFile.data(), patchFile.size(), newXexBytes, false); 491 | if (result != Result::Success) 492 | { 493 | return result; 494 | } 495 | 496 | std::ofstream newXexFile(newXexPath, std::ios::binary); 497 | if (!newXexFile.is_open()) 498 | { 499 | return Result::FileOpenFailed; 500 | } 501 | 502 | newXexFile.write((const char *)(newXexBytes.data()), newXexBytes.size()); 503 | newXexFile.close(); 504 | 505 | if (newXexFile.bad()) 506 | { 507 | std::filesystem::remove(newXexPath); 508 | return Result::FileWriteFailed; 509 | } 510 | 511 | return Result::Success; 512 | } 513 | -------------------------------------------------------------------------------- /XenonUtils/xex_patcher.h: -------------------------------------------------------------------------------- 1 | // Referenced from: https://github.com/xenia-canary/xenia-canary/blob/canary_experimental/src/xenia/cpu/xex_module.cc 2 | 3 | /** 4 | ****************************************************************************** 5 | * Xenia : Xbox 360 Emulator Research Project * 6 | ****************************************************************************** 7 | * Copyright 2023 Ben Vanik. All rights reserved. * 8 | * Released under the BSD license - see LICENSE in the root for more details. * 9 | ****************************************************************************** 10 | */ 11 | 12 | #pragma once 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | struct XexPatcher 20 | { 21 | enum class Result { 22 | Success, 23 | FileOpenFailed, 24 | FileWriteFailed, 25 | XexFileUnsupported, 26 | XexFileInvalid, 27 | PatchFileInvalid, 28 | PatchIncompatible, 29 | PatchFailed, 30 | PatchUnsupported 31 | }; 32 | 33 | static Result apply(const uint8_t* xexBytes, size_t xexBytesSize, const uint8_t* patchBytes, size_t patchBytesSize, std::vector &outBytes, bool skipData); 34 | static Result apply(const std::filesystem::path &baseXexPath, const std::filesystem::path &patchXexPath, const std::filesystem::path &newXexPath); 35 | }; 36 | -------------------------------------------------------------------------------- /thirdparty/.gitignore: -------------------------------------------------------------------------------- 1 | !* 2 | 3 | # Visual Studio 2015/2017 cache/options directory 4 | .vs/ 5 | # The packages folder can be ignored because of Package Restore 6 | **/[Pp]ackages/* 7 | # except build/, which is used as an MSBuild target. 8 | !**/[Pp]ackages/build/ 9 | -------------------------------------------------------------------------------- /thirdparty/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(${THIRDPARTY_ROOT}/disasm) 2 | 3 | if (NOT TARGET fmt::fmt) 4 | add_subdirectory(${THIRDPARTY_ROOT}/fmt) 5 | endif() 6 | 7 | if (NOT TARGET tomlplusplus::tomlplusplus) 8 | add_subdirectory(${THIRDPARTY_ROOT}/tomlplusplus) 9 | endif() 10 | 11 | if (NOT TARGET xxHash::xxhash) 12 | add_subdirectory(${THIRDPARTY_ROOT}/xxHash/cmake_unofficial) 13 | endif() 14 | -------------------------------------------------------------------------------- /thirdparty/TinySHA1/TinySHA1.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * TinySHA1 - a header only implementation of the SHA1 algorithm in C++. Based 4 | * on the implementation in boost::uuid::details. 5 | * 6 | * SHA1 Wikipedia Page: http://en.wikipedia.org/wiki/SHA-1 7 | * 8 | * Copyright (c) 2012-22 SAURAV MOHAPATRA 9 | * 10 | * Permission to use, copy, modify, and distribute this software for any 11 | * purpose with or without fee is hereby granted, provided that the above 12 | * copyright notice and this permission notice appear in all copies. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 15 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 16 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 17 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 18 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 19 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 20 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 21 | * 22 | * Taken from https://github.com/mohaps/TinySHA1 23 | * Modified for use by Xenia 24 | */ 25 | #ifndef _TINY_SHA1_HPP_ 26 | #define _TINY_SHA1_HPP_ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | namespace sha1 { 34 | class SHA1 { 35 | public: 36 | typedef uint32_t digest32_t[5]; 37 | typedef uint8_t digest8_t[20]; 38 | inline static uint32_t LeftRotate(uint32_t value, size_t count) { 39 | return (value << count) ^ (value >> (32 - count)); 40 | } 41 | SHA1() { reset(); } 42 | virtual ~SHA1() {} 43 | SHA1(const SHA1& s) { *this = s; } 44 | const SHA1& operator=(const SHA1& s) { 45 | memcpy(m_digest, s.m_digest, 5 * sizeof(uint32_t)); 46 | memcpy(m_block, s.m_block, 64); 47 | m_blockByteIndex = s.m_blockByteIndex; 48 | m_byteCount = s.m_byteCount; 49 | 50 | return *this; 51 | } 52 | 53 | SHA1& init(const uint32_t digest[5], const uint8_t block[64], 54 | uint32_t count) { 55 | std::memcpy(m_digest, digest, 20); 56 | std::memcpy(m_block, block, count % 64); 57 | m_byteCount = count; 58 | m_blockByteIndex = count % 64; 59 | 60 | return *this; 61 | } 62 | 63 | const uint32_t* getDigest() const { return m_digest; } 64 | const uint8_t* getBlock() const { return m_block; } 65 | size_t getBlockByteIndex() const { return m_blockByteIndex; } 66 | size_t getByteCount() const { return m_byteCount; } 67 | 68 | SHA1& reset() { 69 | m_digest[0] = 0x67452301; 70 | m_digest[1] = 0xEFCDAB89; 71 | m_digest[2] = 0x98BADCFE; 72 | m_digest[3] = 0x10325476; 73 | m_digest[4] = 0xC3D2E1F0; 74 | m_blockByteIndex = 0; 75 | m_byteCount = 0; 76 | return *this; 77 | } 78 | 79 | SHA1& processByte(uint8_t octet) { 80 | this->m_block[this->m_blockByteIndex++] = octet; 81 | ++this->m_byteCount; 82 | if (m_blockByteIndex == 64) { 83 | this->m_blockByteIndex = 0; 84 | processBlock(); 85 | } 86 | 87 | return *this; 88 | } 89 | 90 | SHA1& processBlock(const void* const start, const void* const end) { 91 | const uint8_t* begin = static_cast(start); 92 | const uint8_t* finish = static_cast(end); 93 | while (begin != finish) { 94 | processByte(*begin); 95 | begin++; 96 | } 97 | return *this; 98 | } 99 | 100 | SHA1& processBytes(const void* const data, size_t len) { 101 | const uint8_t* block = static_cast(data); 102 | processBlock(block, block + len); 103 | return *this; 104 | } 105 | 106 | const uint32_t* finalize(digest32_t digest) { 107 | size_t bitCount = this->m_byteCount * 8; 108 | processByte(0x80); 109 | if (this->m_blockByteIndex > 56) { 110 | while (m_blockByteIndex != 0) { 111 | processByte(0); 112 | } 113 | while (m_blockByteIndex < 56) { 114 | processByte(0); 115 | } 116 | } else { 117 | while (m_blockByteIndex < 56) { 118 | processByte(0); 119 | } 120 | } 121 | processByte(0); 122 | processByte(0); 123 | processByte(0); 124 | processByte(0); 125 | processByte(static_cast((bitCount >> 24) & 0xFF)); 126 | processByte(static_cast((bitCount >> 16) & 0xFF)); 127 | processByte(static_cast((bitCount >> 8) & 0xFF)); 128 | processByte(static_cast((bitCount)&0xFF)); 129 | 130 | memcpy(digest, m_digest, 5 * sizeof(uint32_t)); 131 | return digest; 132 | } 133 | 134 | const uint8_t* finalize(digest8_t digest) { 135 | digest32_t d32; 136 | finalize(d32); 137 | size_t di = 0; 138 | digest[di++] = ((d32[0] >> 24) & 0xFF); 139 | digest[di++] = ((d32[0] >> 16) & 0xFF); 140 | digest[di++] = ((d32[0] >> 8) & 0xFF); 141 | digest[di++] = ((d32[0]) & 0xFF); 142 | 143 | digest[di++] = ((d32[1] >> 24) & 0xFF); 144 | digest[di++] = ((d32[1] >> 16) & 0xFF); 145 | digest[di++] = ((d32[1] >> 8) & 0xFF); 146 | digest[di++] = ((d32[1]) & 0xFF); 147 | 148 | digest[di++] = ((d32[2] >> 24) & 0xFF); 149 | digest[di++] = ((d32[2] >> 16) & 0xFF); 150 | digest[di++] = ((d32[2] >> 8) & 0xFF); 151 | digest[di++] = ((d32[2]) & 0xFF); 152 | 153 | digest[di++] = ((d32[3] >> 24) & 0xFF); 154 | digest[di++] = ((d32[3] >> 16) & 0xFF); 155 | digest[di++] = ((d32[3] >> 8) & 0xFF); 156 | digest[di++] = ((d32[3]) & 0xFF); 157 | 158 | digest[di++] = ((d32[4] >> 24) & 0xFF); 159 | digest[di++] = ((d32[4] >> 16) & 0xFF); 160 | digest[di++] = ((d32[4] >> 8) & 0xFF); 161 | digest[di++] = ((d32[4]) & 0xFF); 162 | return digest; 163 | } 164 | 165 | protected: 166 | void processBlock() { 167 | uint32_t w[80]; 168 | for (size_t i = 0; i < 16; i++) { 169 | w[i] = (m_block[i * 4 + 0] << 24); 170 | w[i] |= (m_block[i * 4 + 1] << 16); 171 | w[i] |= (m_block[i * 4 + 2] << 8); 172 | w[i] |= (m_block[i * 4 + 3]); 173 | } 174 | for (size_t i = 16; i < 80; i++) { 175 | w[i] = LeftRotate((w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]), 1); 176 | } 177 | 178 | uint32_t a = m_digest[0]; 179 | uint32_t b = m_digest[1]; 180 | uint32_t c = m_digest[2]; 181 | uint32_t d = m_digest[3]; 182 | uint32_t e = m_digest[4]; 183 | 184 | for (std::size_t i = 0; i < 80; ++i) { 185 | uint32_t f = 0; 186 | uint32_t k = 0; 187 | 188 | if (i < 20) { 189 | f = (b & c) | (~b & d); 190 | k = 0x5A827999; 191 | } else if (i < 40) { 192 | f = b ^ c ^ d; 193 | k = 0x6ED9EBA1; 194 | } else if (i < 60) { 195 | f = (b & c) | (b & d) | (c & d); 196 | k = 0x8F1BBCDC; 197 | } else { 198 | f = b ^ c ^ d; 199 | k = 0xCA62C1D6; 200 | } 201 | uint32_t temp = LeftRotate(a, 5) + f + e + k + w[i]; 202 | e = d; 203 | d = c; 204 | c = LeftRotate(b, 30); 205 | b = a; 206 | a = temp; 207 | } 208 | 209 | m_digest[0] += a; 210 | m_digest[1] += b; 211 | m_digest[2] += c; 212 | m_digest[3] += d; 213 | m_digest[4] += e; 214 | } 215 | 216 | private: 217 | digest32_t m_digest; 218 | uint8_t m_block[64]; 219 | size_t m_blockByteIndex; 220 | size_t m_byteCount; 221 | }; 222 | } 223 | #endif 224 | -------------------------------------------------------------------------------- /thirdparty/disasm/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project("disasm") 2 | 3 | add_library(disasm "ppc-dis.c" "disasm.c" "ppc.h") 4 | target_include_directories(disasm PUBLIC .) 5 | -------------------------------------------------------------------------------- /thirdparty/disasm/dis-asm.h: -------------------------------------------------------------------------------- 1 | /* Interface between the opcode library and its callers. 2 | Written by Cygnus Support, 1993. 3 | 4 | The opcode library (libopcodes.a) provides instruction decoders for 5 | a large variety of instruction sets, callable with an identical 6 | interface, for making instruction-processing programs more independent 7 | of the instruction set being processed. */ 8 | 9 | #ifndef DIS_ASM_H 10 | #define DIS_ASM_H 11 | 12 | #if defined __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #if defined __GNUC__ 23 | # if (__GNUC__ < 4) || \ 24 | defined(__GNUC_MINOR__) && (__GNUC__ == 4) && (__GNUC_MINOR__ < 4) 25 | /* gcc versions before 4.4.x don't support gnu_printf, so use printf. */ 26 | # define GCC_ATTR __attribute__((__unused__, format(printf, 1, 2))) 27 | # define GCC_FMT_ATTR(n, m) __attribute__((format(printf, n, m))) 28 | # else 29 | /* Use gnu_printf when supported (qemu uses standard format strings). */ 30 | # define GCC_ATTR __attribute__((__unused__, format(gnu_printf, 1, 2))) 31 | # define GCC_FMT_ATTR(n, m) __attribute__((format(gnu_printf, n, m))) 32 | # endif 33 | #else 34 | #define GCC_ATTR /**/ 35 | #define GCC_FMT_ATTR(n, m) 36 | #endif 37 | 38 | typedef int (*fprintf_function)(FILE* f, const char* fmt, ...) GCC_FMT_ATTR(2, 3); 39 | 40 | typedef void *PTR; 41 | typedef uint64_t bfd_vma; 42 | typedef int64_t bfd_signed_vma; 43 | typedef uint8_t bfd_byte; 44 | #define sprintf_vma(s,x) sprintf (s, "%0" PRIx64, x) 45 | #define snprintf_vma(s,ss,x) snprintf (s, ss, "%0" PRIx64, x) 46 | 47 | #define BFD64 48 | 49 | enum bfd_flavour { 50 | bfd_target_unknown_flavour, 51 | bfd_target_aout_flavour, 52 | bfd_target_coff_flavour, 53 | bfd_target_ecoff_flavour, 54 | bfd_target_elf_flavour, 55 | bfd_target_ieee_flavour, 56 | bfd_target_nlm_flavour, 57 | bfd_target_oasys_flavour, 58 | bfd_target_tekhex_flavour, 59 | bfd_target_srec_flavour, 60 | bfd_target_ihex_flavour, 61 | bfd_target_som_flavour, 62 | bfd_target_os9k_flavour, 63 | bfd_target_versados_flavour, 64 | bfd_target_msdos_flavour, 65 | bfd_target_evax_flavour 66 | }; 67 | 68 | enum bfd_endian { BFD_ENDIAN_BIG, BFD_ENDIAN_LITTLE, BFD_ENDIAN_UNKNOWN }; 69 | 70 | enum bfd_architecture 71 | { 72 | bfd_arch_unknown, /* File arch not known */ 73 | bfd_arch_obscure, /* Arch known, not one of these */ 74 | bfd_arch_m68k, /* Motorola 68xxx */ 75 | #define bfd_mach_m68000 1 76 | #define bfd_mach_m68008 2 77 | #define bfd_mach_m68010 3 78 | #define bfd_mach_m68020 4 79 | #define bfd_mach_m68030 5 80 | #define bfd_mach_m68040 6 81 | #define bfd_mach_m68060 7 82 | #define bfd_mach_cpu32 8 83 | #define bfd_mach_mcf5200 9 84 | #define bfd_mach_mcf5206e 10 85 | #define bfd_mach_mcf5307 11 86 | #define bfd_mach_mcf5407 12 87 | #define bfd_mach_mcf528x 13 88 | #define bfd_mach_mcfv4e 14 89 | #define bfd_mach_mcf521x 15 90 | #define bfd_mach_mcf5249 16 91 | #define bfd_mach_mcf547x 17 92 | #define bfd_mach_mcf548x 18 93 | bfd_arch_vax, /* DEC Vax */ 94 | bfd_arch_i960, /* Intel 960 */ 95 | /* The order of the following is important. 96 | lower number indicates a machine type that 97 | only accepts a subset of the instructions 98 | available to machines with higher numbers. 99 | The exception is the "ca", which is 100 | incompatible with all other machines except 101 | "core". */ 102 | 103 | #define bfd_mach_i960_core 1 104 | #define bfd_mach_i960_ka_sa 2 105 | #define bfd_mach_i960_kb_sb 3 106 | #define bfd_mach_i960_mc 4 107 | #define bfd_mach_i960_xa 5 108 | #define bfd_mach_i960_ca 6 109 | #define bfd_mach_i960_jx 7 110 | #define bfd_mach_i960_hx 8 111 | 112 | bfd_arch_a29k, /* AMD 29000 */ 113 | bfd_arch_sparc, /* SPARC */ 114 | #define bfd_mach_sparc 1 115 | /* The difference between v8plus and v9 is that v9 is a true 64 bit env. */ 116 | #define bfd_mach_sparc_sparclet 2 117 | #define bfd_mach_sparc_sparclite 3 118 | #define bfd_mach_sparc_v8plus 4 119 | #define bfd_mach_sparc_v8plusa 5 /* with ultrasparc add'ns. */ 120 | #define bfd_mach_sparc_sparclite_le 6 121 | #define bfd_mach_sparc_v9 7 122 | #define bfd_mach_sparc_v9a 8 /* with ultrasparc add'ns. */ 123 | #define bfd_mach_sparc_v8plusb 9 /* with cheetah add'ns. */ 124 | #define bfd_mach_sparc_v9b 10 /* with cheetah add'ns. */ 125 | /* Nonzero if MACH has the v9 instruction set. */ 126 | #define bfd_mach_sparc_v9_p(mach) \ 127 | ((mach) >= bfd_mach_sparc_v8plus && (mach) <= bfd_mach_sparc_v9b \ 128 | && (mach) != bfd_mach_sparc_sparclite_le) 129 | bfd_arch_mips, /* MIPS Rxxxx */ 130 | #define bfd_mach_mips3000 3000 131 | #define bfd_mach_mips3900 3900 132 | #define bfd_mach_mips4000 4000 133 | #define bfd_mach_mips4010 4010 134 | #define bfd_mach_mips4100 4100 135 | #define bfd_mach_mips4300 4300 136 | #define bfd_mach_mips4400 4400 137 | #define bfd_mach_mips4600 4600 138 | #define bfd_mach_mips4650 4650 139 | #define bfd_mach_mips5000 5000 140 | #define bfd_mach_mips6000 6000 141 | #define bfd_mach_mips8000 8000 142 | #define bfd_mach_mips10000 10000 143 | #define bfd_mach_mips16 16 144 | bfd_arch_i386, /* Intel 386 */ 145 | #define bfd_mach_i386_i386 0 146 | #define bfd_mach_i386_i8086 1 147 | #define bfd_mach_i386_i386_intel_syntax 2 148 | #define bfd_mach_x86_64 3 149 | #define bfd_mach_x86_64_intel_syntax 4 150 | bfd_arch_we32k, /* AT&T WE32xxx */ 151 | bfd_arch_tahoe, /* CCI/Harris Tahoe */ 152 | bfd_arch_i860, /* Intel 860 */ 153 | bfd_arch_romp, /* IBM ROMP PC/RT */ 154 | bfd_arch_alliant, /* Alliant */ 155 | bfd_arch_convex, /* Convex */ 156 | bfd_arch_m88k, /* Motorola 88xxx */ 157 | bfd_arch_pyramid, /* Pyramid Technology */ 158 | bfd_arch_h8300, /* Hitachi H8/300 */ 159 | #define bfd_mach_h8300 1 160 | #define bfd_mach_h8300h 2 161 | #define bfd_mach_h8300s 3 162 | bfd_arch_powerpc, /* PowerPC */ 163 | #define bfd_mach_ppc 0 164 | #define bfd_mach_ppc64 1 165 | #define bfd_mach_ppc_403 403 166 | #define bfd_mach_ppc_403gc 4030 167 | #define bfd_mach_ppc_e500 500 168 | #define bfd_mach_ppc_505 505 169 | #define bfd_mach_ppc_601 601 170 | #define bfd_mach_ppc_602 602 171 | #define bfd_mach_ppc_603 603 172 | #define bfd_mach_ppc_ec603e 6031 173 | #define bfd_mach_ppc_604 604 174 | #define bfd_mach_ppc_620 620 175 | #define bfd_mach_ppc_630 630 176 | #define bfd_mach_ppc_750 750 177 | #define bfd_mach_ppc_860 860 178 | #define bfd_mach_ppc_a35 35 179 | #define bfd_mach_ppc_rs64ii 642 180 | #define bfd_mach_ppc_rs64iii 643 181 | #define bfd_mach_ppc_7400 7400 182 | bfd_arch_rs6000, /* IBM RS/6000 */ 183 | bfd_arch_hppa, /* HP PA RISC */ 184 | #define bfd_mach_hppa10 10 185 | #define bfd_mach_hppa11 11 186 | #define bfd_mach_hppa20 20 187 | #define bfd_mach_hppa20w 25 188 | bfd_arch_d10v, /* Mitsubishi D10V */ 189 | bfd_arch_z8k, /* Zilog Z8000 */ 190 | #define bfd_mach_z8001 1 191 | #define bfd_mach_z8002 2 192 | bfd_arch_h8500, /* Hitachi H8/500 */ 193 | bfd_arch_sh, /* Hitachi SH */ 194 | #define bfd_mach_sh 1 195 | #define bfd_mach_sh2 0x20 196 | #define bfd_mach_sh_dsp 0x2d 197 | #define bfd_mach_sh2a 0x2a 198 | #define bfd_mach_sh2a_nofpu 0x2b 199 | #define bfd_mach_sh2e 0x2e 200 | #define bfd_mach_sh3 0x30 201 | #define bfd_mach_sh3_nommu 0x31 202 | #define bfd_mach_sh3_dsp 0x3d 203 | #define bfd_mach_sh3e 0x3e 204 | #define bfd_mach_sh4 0x40 205 | #define bfd_mach_sh4_nofpu 0x41 206 | #define bfd_mach_sh4_nommu_nofpu 0x42 207 | #define bfd_mach_sh4a 0x4a 208 | #define bfd_mach_sh4a_nofpu 0x4b 209 | #define bfd_mach_sh4al_dsp 0x4d 210 | #define bfd_mach_sh5 0x50 211 | bfd_arch_alpha, /* Dec Alpha */ 212 | #define bfd_mach_alpha 1 213 | #define bfd_mach_alpha_ev4 0x10 214 | #define bfd_mach_alpha_ev5 0x20 215 | #define bfd_mach_alpha_ev6 0x30 216 | bfd_arch_arm, /* Advanced Risc Machines ARM */ 217 | #define bfd_mach_arm_unknown 0 218 | #define bfd_mach_arm_2 1 219 | #define bfd_mach_arm_2a 2 220 | #define bfd_mach_arm_3 3 221 | #define bfd_mach_arm_3M 4 222 | #define bfd_mach_arm_4 5 223 | #define bfd_mach_arm_4T 6 224 | #define bfd_mach_arm_5 7 225 | #define bfd_mach_arm_5T 8 226 | #define bfd_mach_arm_5TE 9 227 | #define bfd_mach_arm_XScale 10 228 | #define bfd_mach_arm_ep9312 11 229 | #define bfd_mach_arm_iWMMXt 12 230 | #define bfd_mach_arm_iWMMXt2 13 231 | bfd_arch_ns32k, /* National Semiconductors ns32000 */ 232 | bfd_arch_w65, /* WDC 65816 */ 233 | bfd_arch_tic30, /* Texas Instruments TMS320C30 */ 234 | bfd_arch_v850, /* NEC V850 */ 235 | #define bfd_mach_v850 0 236 | bfd_arch_arc, /* Argonaut RISC Core */ 237 | #define bfd_mach_arc_base 0 238 | bfd_arch_m32r, /* Mitsubishi M32R/D */ 239 | #define bfd_mach_m32r 0 /* backwards compatibility */ 240 | bfd_arch_mn10200, /* Matsushita MN10200 */ 241 | bfd_arch_mn10300, /* Matsushita MN10300 */ 242 | bfd_arch_cris, /* Axis CRIS */ 243 | #define bfd_mach_cris_v0_v10 255 244 | #define bfd_mach_cris_v32 32 245 | #define bfd_mach_cris_v10_v32 1032 246 | bfd_arch_microblaze, /* Xilinx MicroBlaze. */ 247 | bfd_arch_ia64, /* HP/Intel ia64 */ 248 | #define bfd_mach_ia64_elf64 64 249 | #define bfd_mach_ia64_elf32 32 250 | bfd_arch_last 251 | }; 252 | #define bfd_mach_s390_31 31 253 | #define bfd_mach_s390_64 64 254 | 255 | typedef struct symbol_cache_entry 256 | { 257 | const char *name; 258 | union 259 | { 260 | PTR p; 261 | bfd_vma i; 262 | } udata; 263 | } asymbol; 264 | 265 | enum dis_insn_type { 266 | dis_noninsn, /* Not a valid instruction */ 267 | dis_nonbranch, /* Not a branch instruction */ 268 | dis_branch, /* Unconditional branch */ 269 | dis_condbranch, /* Conditional branch */ 270 | dis_jsr, /* Jump to subroutine */ 271 | dis_condjsr, /* Conditional jump to subroutine */ 272 | dis_dref, /* Data reference instruction */ 273 | dis_dref2 /* Two data references in instruction */ 274 | }; 275 | 276 | /* This struct is passed into the instruction decoding routine, 277 | and is passed back out into each callback. The various fields are used 278 | for conveying information from your main routine into your callbacks, 279 | for passing information into the instruction decoders (such as the 280 | addresses of the callback functions), or for passing information 281 | back from the instruction decoders to their callers. 282 | 283 | It must be initialized before it is first passed; this can be done 284 | by hand, or using one of the initialization macros below. */ 285 | 286 | typedef struct disassemble_info { 287 | fprintf_function fprintf_func; 288 | FILE *stream; 289 | PTR application_data; 290 | 291 | /* Target description. We could replace this with a pointer to the bfd, 292 | but that would require one. There currently isn't any such requirement 293 | so to avoid introducing one we record these explicitly. */ 294 | /* The bfd_flavour. This can be bfd_target_unknown_flavour. */ 295 | enum bfd_flavour flavour; 296 | /* The bfd_arch value. */ 297 | enum bfd_architecture arch; 298 | /* The bfd_mach value. */ 299 | unsigned long mach; 300 | /* Endianness (for bi-endian cpus). Mono-endian cpus can ignore this. */ 301 | enum bfd_endian endian; 302 | 303 | /* An array of pointers to symbols either at the location being disassembled 304 | or at the start of the function being disassembled. The array is sorted 305 | so that the first symbol is intended to be the one used. The others are 306 | present for any misc. purposes. This is not set reliably, but if it is 307 | not NULL, it is correct. */ 308 | asymbol **symbols; 309 | /* Number of symbols in array. */ 310 | int num_symbols; 311 | 312 | /* For use by the disassembler. 313 | The top 16 bits are reserved for public use (and are documented here). 314 | The bottom 16 bits are for the internal use of the disassembler. */ 315 | unsigned long flags; 316 | #define INSN_HAS_RELOC 0x80000000 317 | PTR private_data; 318 | 319 | /* Function used to get bytes to disassemble. MEMADDR is the 320 | address of the stuff to be disassembled, MYADDR is the address to 321 | put the bytes in, and LENGTH is the number of bytes to read. 322 | INFO is a pointer to this struct. 323 | Returns an errno value or 0 for success. */ 324 | int (*read_memory_func) 325 | (bfd_vma memaddr, bfd_byte *myaddr, int length, 326 | struct disassemble_info *info); 327 | 328 | /* Function which should be called if we get an error that we can't 329 | recover from. STATUS is the errno value from read_memory_func and 330 | MEMADDR is the address that we were trying to read. INFO is a 331 | pointer to this struct. */ 332 | void (*memory_error_func) 333 | (int status, bfd_vma memaddr, struct disassemble_info *info); 334 | 335 | /* Function called to print ADDR. */ 336 | void (*print_address_func) 337 | (bfd_vma addr, struct disassemble_info *info); 338 | 339 | /* Function called to determine if there is a symbol at the given ADDR. 340 | If there is, the function returns 1, otherwise it returns 0. 341 | This is used by ports which support an overlay manager where 342 | the overlay number is held in the top part of an address. In 343 | some circumstances we want to include the overlay number in the 344 | address, (normally because there is a symbol associated with 345 | that address), but sometimes we want to mask out the overlay bits. */ 346 | int (* symbol_at_address_func) 347 | (bfd_vma addr, struct disassemble_info * info); 348 | 349 | /* These are for buffer_read_memory. */ 350 | const bfd_byte *buffer; 351 | bfd_vma buffer_vma; 352 | int buffer_length; 353 | 354 | /* This variable may be set by the instruction decoder. It suggests 355 | the number of bytes objdump should display on a single line. If 356 | the instruction decoder sets this, it should always set it to 357 | the same value in order to get reasonable looking output. */ 358 | int bytes_per_line; 359 | 360 | /* the next two variables control the way objdump displays the raw data */ 361 | /* For example, if bytes_per_line is 8 and bytes_per_chunk is 4, the */ 362 | /* output will look like this: 363 | 00: 00000000 00000000 364 | with the chunks displayed according to "display_endian". */ 365 | int bytes_per_chunk; 366 | enum bfd_endian display_endian; 367 | 368 | /* Results from instruction decoders. Not all decoders yet support 369 | this information. This info is set each time an instruction is 370 | decoded, and is only valid for the last such instruction. 371 | 372 | To determine whether this decoder supports this information, set 373 | insn_info_valid to 0, decode an instruction, then check it. */ 374 | 375 | char insn_info_valid; /* Branch info has been set. */ 376 | char branch_delay_insns; /* How many sequential insn's will run before 377 | a branch takes effect. (0 = normal) */ 378 | char data_size; /* Size of data reference in insn, in bytes */ 379 | enum dis_insn_type insn_type; /* Type of instruction */ 380 | bfd_vma target; /* Target address of branch or dref, if known; 381 | zero if unknown. */ 382 | bfd_vma target2; /* Second target address for dref2 */ 383 | 384 | /* Command line options specific to the target disassembler. */ 385 | const char * disassembler_options; 386 | 387 | } disassemble_info; 388 | 389 | typedef struct powerpc_opcode 390 | { 391 | /* The opcode name. */ 392 | const char* name; 393 | 394 | /* The opcode itself. Those bits which will be filled in with 395 | operands are zeroes. */ 396 | unsigned long opcode; 397 | 398 | /* The opcode mask. This is used by the disassembler. This is a 399 | mask containing ones indicating those bits which must match the 400 | opcode field, and zeroes indicating those bits which need not 401 | match (and are presumably filled in by operands). */ 402 | unsigned long mask; 403 | 404 | /* One bit flags for the opcode. These are used to indicate which 405 | specific processors support the instructions. The defined values 406 | are listed below. */ 407 | unsigned long flags; 408 | 409 | /* An array of operand codes. Each code is an index into the 410 | operand table. They appear in the order which the operands must 411 | appear in assembly code, and are terminated by a zero. */ 412 | unsigned char operands[8]; 413 | 414 | /* The opcode ID. */ 415 | int id; 416 | } powerpc_opcode; 417 | 418 | typedef struct ppc_insn 419 | { 420 | const powerpc_opcode* opcode; 421 | uint32_t instruction; 422 | uint32_t operands[8]; 423 | char op_str[160]; 424 | } ppc_insn; 425 | 426 | /* Standard disassemblers. Disassemble one instruction at the given 427 | target address. Return number of bytes processed. */ 428 | typedef int (*disassembler_ftype) (bfd_vma, disassemble_info *); 429 | 430 | int print_insn_big_mips (bfd_vma, disassemble_info*); 431 | int print_insn_little_mips (bfd_vma, disassemble_info*); 432 | int print_insn_i386 (bfd_vma, disassemble_info*); 433 | int print_insn_m68k (bfd_vma, disassemble_info*); 434 | int print_insn_z8001 (bfd_vma, disassemble_info*); 435 | int print_insn_z8002 (bfd_vma, disassemble_info*); 436 | int print_insn_h8300 (bfd_vma, disassemble_info*); 437 | int print_insn_h8300h (bfd_vma, disassemble_info*); 438 | int print_insn_h8300s (bfd_vma, disassemble_info*); 439 | int print_insn_h8500 (bfd_vma, disassemble_info*); 440 | int print_insn_alpha (bfd_vma, disassemble_info*); 441 | disassembler_ftype arc_get_disassembler (int, int); 442 | int print_insn_arm (bfd_vma, disassemble_info*); 443 | int print_insn_sparc (bfd_vma, disassemble_info*); 444 | int print_insn_big_a29k (bfd_vma, disassemble_info*); 445 | int print_insn_little_a29k (bfd_vma, disassemble_info*); 446 | int print_insn_i960 (bfd_vma, disassemble_info*); 447 | int print_insn_sh (bfd_vma, disassemble_info*); 448 | int print_insn_shl (bfd_vma, disassemble_info*); 449 | int print_insn_hppa (bfd_vma, disassemble_info*); 450 | int print_insn_m32r (bfd_vma, disassemble_info*); 451 | int print_insn_m88k (bfd_vma, disassemble_info*); 452 | int print_insn_mn10200 (bfd_vma, disassemble_info*); 453 | int print_insn_mn10300 (bfd_vma, disassemble_info*); 454 | int print_insn_ns32k (bfd_vma, disassemble_info*); 455 | int print_insn_big_powerpc (bfd_vma, disassemble_info*); 456 | int print_insn_little_powerpc (bfd_vma, disassemble_info*); 457 | int print_insn_rs6000 (bfd_vma, disassemble_info*); 458 | int print_insn_w65 (bfd_vma, disassemble_info*); 459 | int print_insn_d10v (bfd_vma, disassemble_info*); 460 | int print_insn_v850 (bfd_vma, disassemble_info*); 461 | int print_insn_tic30 (bfd_vma, disassemble_info*); 462 | int print_insn_ppc (bfd_vma, disassemble_info*); 463 | int print_insn_s390 (bfd_vma, disassemble_info*); 464 | int print_insn_crisv32 (bfd_vma, disassemble_info*); 465 | int print_insn_crisv10 (bfd_vma, disassemble_info*); 466 | int print_insn_microblaze (bfd_vma, disassemble_info*); 467 | int print_insn_ia64 (bfd_vma, disassemble_info*); 468 | 469 | int decode_insn_ppc(bfd_vma, disassemble_info*, ppc_insn*); 470 | 471 | #if 0 472 | /* Fetch the disassembler for a given BFD, if that support is available. */ 473 | disassembler_ftype disassembler(bfd *); 474 | #endif 475 | 476 | /* This block of definitions is for particular callers who read instructions 477 | into a buffer before calling the instruction decoder. */ 478 | 479 | /* Here is a function which callers may wish to use for read_memory_func. 480 | It gets bytes from a buffer. */ 481 | int buffer_read_memory(bfd_vma, bfd_byte *, int, struct disassemble_info *); 482 | 483 | /* This function goes with buffer_read_memory. 484 | It prints a message using info->fprintf_func and info->stream. */ 485 | void perror_memory(int, bfd_vma, struct disassemble_info *); 486 | 487 | 488 | /* Just print the address in hex. This is included for completeness even 489 | though both GDB and objdump provide their own (to print symbolic 490 | addresses). */ 491 | void generic_print_address(bfd_vma, struct disassemble_info *); 492 | 493 | /* Always true. */ 494 | int generic_symbol_at_address(bfd_vma, struct disassemble_info *); 495 | 496 | /* Macro to initialize a disassemble_info struct. This should be called 497 | by all applications creating such a struct. */ 498 | #define INIT_DISASSEMBLE_INFO(INFO, STREAM, FPRINTF_FUNC) \ 499 | (INFO).flavour = bfd_target_unknown_flavour, \ 500 | (INFO).arch = bfd_arch_unknown, \ 501 | (INFO).mach = 0, \ 502 | (INFO).endian = BFD_ENDIAN_UNKNOWN, \ 503 | INIT_DISASSEMBLE_INFO_NO_ARCH(INFO, STREAM, FPRINTF_FUNC) 504 | 505 | /* Call this macro to initialize only the internal variables for the 506 | disassembler. Architecture dependent things such as byte order, or machine 507 | variant are not touched by this macro. This makes things much easier for 508 | GDB which must initialize these things separately. */ 509 | 510 | #define INIT_DISASSEMBLE_INFO_NO_ARCH(INFO, STREAM, FPRINTF_FUNC) \ 511 | (INFO).fprintf_func = (FPRINTF_FUNC), \ 512 | (INFO).stream = (STREAM), \ 513 | (INFO).symbols = NULL, \ 514 | (INFO).num_symbols = 0, \ 515 | (INFO).private_data = NULL, \ 516 | (INFO).buffer = NULL, \ 517 | (INFO).buffer_vma = 0, \ 518 | (INFO).buffer_length = 0, \ 519 | (INFO).read_memory_func = buffer_read_memory, \ 520 | (INFO).memory_error_func = perror_memory, \ 521 | (INFO).print_address_func = generic_print_address, \ 522 | (INFO).symbol_at_address_func = generic_symbol_at_address, \ 523 | (INFO).flags = 0, \ 524 | (INFO).bytes_per_line = 0, \ 525 | (INFO).bytes_per_chunk = 0, \ 526 | (INFO).display_endian = BFD_ENDIAN_UNKNOWN, \ 527 | (INFO).disassembler_options = NULL, \ 528 | (INFO).insn_info_valid = 0 529 | 530 | #define _(x) x 531 | 532 | #if defined __GNUC__ 533 | #define ATTRIBUTE_UNUSED __attribute__((unused)) 534 | #else 535 | #define ATTRIBUTE_UNUSED 536 | #endif 537 | /* from libbfd */ 538 | 539 | bfd_vma bfd_getl64 (const bfd_byte *addr); 540 | bfd_vma bfd_getl32 (const bfd_byte *addr); 541 | bfd_vma bfd_getb32 (const bfd_byte *addr); 542 | bfd_vma bfd_getl16 (const bfd_byte *addr); 543 | bfd_vma bfd_getb16 (const bfd_byte *addr); 544 | typedef bool bfd_boolean; 545 | 546 | #if defined __cplusplus 547 | } 548 | #endif 549 | 550 | #endif /* ! defined (DIS_ASM_H) */ 551 | -------------------------------------------------------------------------------- /thirdparty/disasm/disasm.c: -------------------------------------------------------------------------------- 1 | #include "dis-asm.h" 2 | 3 | #ifndef EIO 4 | #define EIO 5 5 | #endif 6 | 7 | /* Get LENGTH bytes from info's buffer, at target address memaddr. 8 | Transfer them to myaddr. */ 9 | int 10 | buffer_read_memory(bfd_vma memaddr, bfd_byte* myaddr, int length, 11 | struct disassemble_info* info) 12 | { 13 | if (memaddr < info->buffer_vma 14 | || memaddr + length > info->buffer_vma + info->buffer_length) 15 | /* Out of bounds. Use EIO because GDB uses it. */ 16 | return EIO; 17 | memcpy(myaddr, info->buffer + (memaddr - info->buffer_vma), length); 18 | return 0; 19 | } 20 | 21 | /* Print an error message. We can assume that this is in response to 22 | an error return from buffer_read_memory. */ 23 | void 24 | perror_memory(int status, bfd_vma memaddr, struct disassemble_info* info) 25 | { 26 | if (status != EIO) 27 | /* Can't happen. */ 28 | (*info->fprintf_func) (info->stream, "Unknown error %d\n", status); 29 | else 30 | /* Actually, address between memaddr and memaddr + len was 31 | out of bounds. */ 32 | (*info->fprintf_func) (info->stream, 33 | "Address 0x%" PRIx64 " is out of bounds.\n", memaddr); 34 | } 35 | 36 | /* This could be in a separate file, to save miniscule amounts of space 37 | in statically linked executables. */ 38 | 39 | /* Just print the address is hex. This is included for completeness even 40 | though both GDB and objdump provide their own (to print symbolic 41 | addresses). */ 42 | 43 | void 44 | generic_print_address(bfd_vma addr, struct disassemble_info* info) 45 | { 46 | (*info->fprintf_func) (info->stream, "0x%" PRIx64, addr); 47 | } 48 | 49 | /* Just return the given address. */ 50 | 51 | int 52 | generic_symbol_at_address(bfd_vma addr, struct disassemble_info* info) 53 | { 54 | return 1; 55 | } 56 | 57 | bfd_vma bfd_getl64(const bfd_byte* addr) 58 | { 59 | unsigned long long v; 60 | 61 | v = (unsigned long long) addr[0]; 62 | v |= (unsigned long long) addr[1] << 8; 63 | v |= (unsigned long long) addr[2] << 16; 64 | v |= (unsigned long long) addr[3] << 24; 65 | v |= (unsigned long long) addr[4] << 32; 66 | v |= (unsigned long long) addr[5] << 40; 67 | v |= (unsigned long long) addr[6] << 48; 68 | v |= (unsigned long long) addr[7] << 56; 69 | return (bfd_vma)v; 70 | } 71 | 72 | bfd_vma bfd_getl32(const bfd_byte* addr) 73 | { 74 | unsigned long v; 75 | 76 | v = (unsigned long)addr[0]; 77 | v |= (unsigned long)addr[1] << 8; 78 | v |= (unsigned long)addr[2] << 16; 79 | v |= (unsigned long)addr[3] << 24; 80 | return (bfd_vma)v; 81 | } 82 | 83 | bfd_vma bfd_getb32(const bfd_byte* addr) 84 | { 85 | unsigned long v; 86 | 87 | v = (unsigned long)addr[0] << 24; 88 | v |= (unsigned long)addr[1] << 16; 89 | v |= (unsigned long)addr[2] << 8; 90 | v |= (unsigned long)addr[3]; 91 | return (bfd_vma)v; 92 | } 93 | 94 | bfd_vma bfd_getl16(const bfd_byte* addr) 95 | { 96 | unsigned long v; 97 | 98 | v = (unsigned long)addr[0]; 99 | v |= (unsigned long)addr[1] << 8; 100 | return (bfd_vma)v; 101 | } 102 | 103 | bfd_vma bfd_getb16(const bfd_byte* addr) 104 | { 105 | unsigned long v; 106 | 107 | v = (unsigned long)addr[0] << 24; 108 | v |= (unsigned long)addr[1] << 16; 109 | return (bfd_vma)v; 110 | } 111 | -------------------------------------------------------------------------------- /thirdparty/disasm/ppc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "ppc-inst.h" 3 | 4 | /* A macro to extract the major opcode from an instruction. */ 5 | #define PPC_OP(i) (((i) >> 26) & 0x3f) 6 | 7 | /* A macro to extract the extended opcode from an instruction. */ 8 | #define PPC_XOP(i) (((i) >> 1) & 0x3ff) 9 | 10 | /* A macro to extract the branch destination from a conditional instruction. */ 11 | #define PPC_BD(i) ((signed int)((((i) & 0xFFFC) ^ 0x8000) - 0x8000)) 12 | 13 | /* A macro to extract the branch destination from an immediate instruction. */ 14 | #define PPC_BI(i) ((signed int)((((i) & 0x3FFFFFC) ^ 0x2000000) - 0x2000000)) 15 | 16 | /* A macro to extract whether the branch is absolute. */ 17 | #define PPC_BA(i) (!!((i) & 2)) 18 | 19 | /* A macro to extract whether the branch is a link. */ 20 | #define PPC_BL(i) (!!((i) & 1)) 21 | 22 | /* A macro to extract the branch operation of an instruction. */ 23 | #define PPC_BO(i) (((i) >> 21) & 0x1F) 24 | 25 | #define PPC_OP_TDI 2 26 | #define PPC_OP_TWI 3 27 | #define PPC_OP_MULLI 7 28 | #define PPC_OP_SUBFIC 8 29 | #define PPC_OP_CMPLI 0xA 30 | #define PPC_OP_CMPI 0xB 31 | #define PPC_OP_ADDIC 0xC // addic 32 | #define PPC_OP_ADDICR 0xD // addic. 33 | #define PPC_OP_ADDI 0xE 34 | #define PPC_OP_ADDIS 0xF 35 | #define PPC_OP_BC 0x10 36 | #define PPC_OP_SC 0x11 37 | #define PPC_OP_B 0x12 38 | #define PPC_OP_CTR 0x13 39 | --------------------------------------------------------------------------------