feat: add menu + stage music support (#386)

* Add support for Rust in CMake via Corrosion

* Tweak .gitignore to handle Rust target dir

* It's 3AM and I have no desire to deal with this

* Initial scaffolding for loading Rust library in Dolphin

* Expose the Dolphin logging mechanisms to Rust via some callback trickery

* Attempt patching Core.vcxproj to support building Rust dylib

* Attempt patching Core.vcxproj to support building Rust dylib, take 2 OR

* Instruct the build to actually use the steps

* Attempt the older style vcxproj definition format

* Push build targets to end

* Just don't use va_args when calling through from Rust

* Enable Windows project linking, move to generic project structure, start moving out of HW

* Add note to README about Rust requirement

* Update slippi-rust-extensions to have proper EXI Device skeleton

* Attach shadow EXI device to cpp EXI device, add feature flag for playback-specific code, rewrite README for rust extensions slightly

* Remove unused log file

* Ongoing logging work

* Tweak Dolphin LogContainer to auto forward enabled/level status over to Rust, expose more methods on Rust side for control, rebuild logger layer subscriber

* Remove debug flag for release mode but add note about it

* Reorganize module definitions, pass in sampler handler fn for SoundStream

* Fix log target

* Rename to SlippiRustExtensions, separate out into cargo workspace, rename General Rust log

* Tweak logging layer so that we don't double-allocate strings on all log messages, properly surface log locations

* cargo fmt

* initial port of slippi-jukebox code

* minor cleanup and leverage channels rather than atomic bool for stopping
threads

* Add config option for enabling/disabling Jukebox

* Invert shutdown order for Memory and ExpansionInterface so we avoid a null ptr race in the Jukebox, as we need Memory to still be valid at Jukebox shutdown

* update dolphin additional include dirs to facilitate new slippi config pane changes

* Ensure Core is running before trying to find an EXI device, as ExpansionInterface isn't initialized unless Core is running - fixes a crash on bad access

* Expose streaming sampler to jukebox, disable DVDInterface streaming sampler pushes to avoid null data being pushed in

* Expose setters for streaming sample rate and streaming volume to Rust

* jukebox: renaming variables for clarity

* try to use new jukebox sample functions

* jukebox: add support for all star rest area

* jukebox: add support for adventure mode field stages

* jukebox: continuously send chunks of pcm data to dolphin's audio mixer

* Audio somewhat coming through now, albeit with pops and pitch issues...

* Force-log samples to wav

* jukebox: switch from dolphin mixer back to rodio for music playback

* Mark doc example as notest

* Attempt an initial CI pass

* Tinkering with CI

* Specify CI working directory for cargo fmt

* Specify CI working directory for cargo fmt

* Specify CI working directory for cargo fmt

* Specify CI working directory for cargo fmt

* Specify CI working directory for cargo fmt

* Specify CI working directory for cargo fmt

* [CI] More permissive for compiling code

* jukebox: replace rand with fastrand

* jukebox: dont use static memory for menu & tournament tracks

* Attempt to resolve Windows playback lib loading oddity

* Add a method for grabbing the current volume level

* Have Dolphin pass over the iso path and a getter for the volume level

* Cargo fmt pass

* Include rustfmt.toml

* Remove the Jukebox config option from playback builds entirely, do not start the jukebox if we're in WASAPI Exclusive mode

* Ix-nay the bad check I copied by accident, lol

* SlippiRustExtensions: add build instructions for windows

* jukebox: make proper use of dolphin's volume + remove unused dependencies

* SlippiRustExtensions(readme): simplify suggested out-of-band build command

* jukebox: remove 'anyhow' dependency

* jukebox: simplify read_dolphin_state fn

* jukebox: reduce hps fingerprint size for track matching

* jukebox: remove 'bus' dependency and improve comments

* jukebox: add readme

* jukebox: add description field to Cargo.toml

* README cleanup, extra writeup for logcontainer creation

* Cleanup unused imports

* Rename DolphinState to DolphinGameState

* Pin to Rust 1.70.0

* Re-enable SFX, stop force-dumping WAV DTK audiograph logs

* Revert attempted change of disabling DTK reads

* Revise documentation surrounding logging infrastructure changes

* Ensure the Volume getter is outside of a C++ namespace, just to try and be careful with ABI oddities

* Add an extra LogContainer.

- A generic Rust dependencies log container that we can use if we need
  to ever inspect dependency tracing logs.
- Moves the AudioCommon::GetVolume call out of the namespace for now
  since I want someone (or me, when I get more time) to verify that
  being inside a C++ namespace is safe.
- Remove once_cell as a dependency now that this is pinned to `1.70.0`.

* Fix the typo, because C++

* Additional README contexts

* ifndef PLAYBACK for GUI toggles

* Always publish an artifact for macOS even if we're not signing and notarizing

* Try changing into the directory - why is there so little documentation about what is supported in these files...?

* Pin toolchain in CI to 1.70.0, update Visual Studio to change into working directory for pre-build Rust step so that toolchain file is actually detected

* Opt for a root symlink to the toolchain config per Nikki's idea, comment out forced 1.70.0 in CI build flow

* Revert symlink toolchain to see if there's a CI bug for Linux

* jukebox: reduce music volume by 20%

* jukebox: link to hps_decode crate in the docs

* ci: sed out rust version from rust-toolchain.toml

* ci: add id field for rust_ver

* jukebox: stop blocking the main thread when scanning iso for tracks

* jukebox: respect melee's volume setting when restarting jukebox during emulation

* jukebox: improve code readability

* jukebox: leverage disc filesystem table to locate .hps files

* jukebox: minor fst parsing code improvements

* remove '.unwrap()' from Jukebox destructor

---------

Co-authored-by: Ryan McGrath <ryan@rymc.io>
Co-authored-by: Nikhil Narayana <nikhil.narayana@live.com>
This commit is contained in:
Daryl Pinto
2023-07-27 23:22:41 -04:00
committed by GitHub
parent f68f6f6cbf
commit e29fe70188
60 changed files with 9461 additions and 1251 deletions

View File

@@ -13,6 +13,25 @@ on:
- "**.md"
jobs:
slprs_cargo_fmt:
name: (SlippiRustExtensions) Cargo Format
runs-on: ubuntu-latest
outputs:
rust_ver: ${{ steps.rust_ver.outputs.rust_ver }}
steps:
- uses: actions/checkout@v3
- id: rust_ver
name: Grab Rust Version
run: echo "rust_ver=$(sed -rn 's/^channel = "(.*)"/\1/p' ./Externals/SlippiRustExtensions/rust-toolchain.toml)" >> "$GITHUB_OUTPUT"
- uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: ${{ steps.rust_ver.outputs.rust_ver }}
components: rustfmt
- name: Rustfmt Check
uses: actions-rust-lang/rustfmt@v1
with:
manifest-path: ./Externals/SlippiRustExtensions/Cargo.toml
skip_duplicates:
name: Skip Duplicate Runs
runs-on: ubuntu-latest
@@ -26,7 +45,7 @@ jobs:
do_not_skip: '["push", "workflow_dispatch", "schedule"]'
windows:
needs: skip_duplicates
needs: [skip_duplicates, slprs_cargo_fmt]
if: ${{ needs.skip_duplicates.outputs.should_skip != 'true' }}
strategy:
fail-fast: false
@@ -47,6 +66,13 @@ jobs:
steps:
- name: "Checkout"
uses: actions/checkout@v2
- name: "Install Rust"
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: ${{ needs.slprs_cargo_fmt.outputs.rust_ver }} # Pin to our specific Rust version.
rustflags: "" # Disable default injection of warnings = errors.
- name: "(SlippiRustExtensions) Run cargo test"
run: cargo test --all --all-features --manifest-path=Externals/SlippiRustExtensions/Cargo.toml
- name: "Remove Redistributable"
shell: cmd
run: |
@@ -121,7 +147,7 @@ jobs:
path: "./artifact/"
linux:
needs: skip_duplicates
needs: [skip_duplicates, slprs_cargo_fmt]
if: ${{ needs.skip_duplicates.outputs.should_skip != 'true' }}
strategy:
fail-fast: false
@@ -191,6 +217,13 @@ jobs:
qtbase5-private-dev \
libxxf86vm-dev \
x11proto-xinerama-dev
- name: "Install Rust"
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: ${{ needs.slprs_cargo_fmt.outputs.rust_ver }} # Pin to our specific Rust version.
rustflags: "" # Disable default injection of warnings = errors.
- name: "(SlippiRustExtensions) Run cargo test"
run: cargo test --all --all-features --manifest-path=Externals/SlippiRustExtensions/Cargo.toml
- name: "Build ${{ matrix.build_type }} Dolphin"
if: success()
working-directory: ${{ github.workspace }}
@@ -222,7 +255,7 @@ jobs:
path: "./artifact/"
macOS:
needs: skip_duplicates
needs: [skip_duplicates, slprs_cargo_fmt]
if: ${{ needs.skip_duplicates.outputs.should_skip != 'true' }}
strategy:
fail-fast: false
@@ -274,6 +307,13 @@ jobs:
sound-touch \
hidapi \
python@3.10
- name: "Install Rust"
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: ${{ needs.slprs_cargo_fmt.outputs.rust_ver }} # Pin to our specific Rust version.
rustflags: "" # Disable default injection of warnings = errors.
- name: "(SlippiRustExtensions) Run cargo test"
run: cargo test --all --all-features --manifest-path=Externals/SlippiRustExtensions/Cargo.toml
- name: "Build ${{ matrix.build_type }} Dolphin"
if: success()
shell: bash
@@ -298,14 +338,10 @@ jobs:
mkdir -p ~/private_keys/
echo '${{ secrets.APPLE_CONNECT_API_KEY }}' > ~/private_keys/AuthKey_${{ secrets.APPLE_API_KEY_ID }}.p8
/usr/bin/codesign -f -s "${{ secrets.APPLE_IDENTITY_HASH }}" --deep --options runtime --entitlements Source/Core/DolphinWX/Entitlements.plist ./build/Binaries/Slippi\ Dolphin.app
- name: "Package, Sign and Notarize Netplay Release DMG"
if: success() && env.CERTIFICATE_MACOS_APPLICATION != null
- name: "Package ${{ matrix.build_type }} Release DMG"
if: success()
shell: bash
working-directory: ${{ github.workspace }}
env:
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_ISSUER_ID: ${{ secrets.APPLE_ISSUER_ID }}
CERTIFICATE_MACOS_APPLICATION: ${{ secrets.CERTIFICATE_MACOS_APPLICATION }}
run: |
rm build/Binaries/traversal_server
chmod +x Tools/create-dmg/run.sh
@@ -323,6 +359,15 @@ jobs:
"${{ env.FILE_NAME }}.dmg" \
"./build/Binaries/"
mv "${{ env.FILE_NAME }}.dmg" artifact/
- name: "Sign and Notarize ${{ matrix.build_type }} Release DMG"
if: success() && env.CERTIFICATE_MACOS_APPLICATION != null
shell: bash
working-directory: ${{ github.workspace }}
env:
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_ISSUER_ID: ${{ secrets.APPLE_ISSUER_ID }}
CERTIFICATE_MACOS_APPLICATION: ${{ secrets.CERTIFICATE_MACOS_APPLICATION }}
run: |
/usr/bin/codesign -f -s "${{ secrets.APPLE_IDENTITY_HASH }}" --deep --options runtime ./artifact/${{ env.FILE_NAME }}.dmg
chmod +x Tools/notarize_netplay.sh && ./Tools/notarize_netplay.sh ./artifact/${{ env.FILE_NAME }}.dmg
- name: "Publish"

4
.gitignore vendored
View File

@@ -47,6 +47,10 @@ Externals/SlippiLib/CMakeCache.txt
Externals/SlippiLib/CMakeFiles/
Externals/SlippiLib/Makefile
Externals/SlippiLib/cmake_install.cmake
# The rust build directory, just ignore it
Externals/SlippiRustExtensions/target
Ishiiruka.pro
Source/.idea/
Source/enc_temp_folder/

View File

@@ -586,6 +586,9 @@ if(ENCODE_FRAMEDUMPS)
endif()
message("Using static Corrosion from Externals for Rust linking support")
add_subdirectory(Externals/corrosion)
if(NOT ANDROID)
set(PORTAUDIO_FOUND TRUE)
add_definitions(-DHAVE_PORTAUDIO=1)

1588
Externals/SlippiRustExtensions/Cargo.lock generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,19 @@
[workspace]
resolver = "2"
# Always build the ffi interface as the default project.
default-members = ["ffi"]
members = [
"exi",
"ffi",
"jukebox",
"logger"
]
[workspace.dependencies]
time = { version = "0.3.20", default-features = false, features = ["std", "local-offset"] }
# We disable the "attributes" feature as we don't currently need it and it brings
# in extra dependencies.
tracing = { version = "0.1", default-features = false, features = ["std"] }

View File

@@ -0,0 +1,64 @@
# Slippi Rust Extensions
This external module houses various Slippi-specific bits of functionality and is ultimately linked into the Dolphin executable and instrumented via the C FFI. This is an evolving workspace area and may be subject to changes.
This workspace currently targets Rust `1.70.0`. As long as you have Rust installed, `cargo` should automatically configure this when building.
> You may opt to add other components (e.g `clippy`) as the toolchain file currently targets what the CI builders need.
- [Project Module Structure Overview](#project-module-structure-overview)
- [Adding a new workspace module](#adding-a-new-workspace-module)
- [Feature Flags](#feature-flags)
- [Building out of band](#building-out-of-band)
## Project Module Structure Overview
| Module | Description |
|----------|--------------------------------------------------------------------------------|
| `ffi` | The core library. Exposes C FFI functions for Dolphin to call. |
| `exi` | An EXI device that receives forwarded calls from the Dolphin EXI (C++) device. |
| `logger` | A library that connects Dolphin `LogContainer`s and `tracing`. |
| `jukebox`| Melee music player library. See the [Slippi Jukebox README](jukebox/README.md) for more info. |
Some important aspects of the project structure to understand:
- The build script in this crate automatically generates C bindings that get output to `ffi/includes/SlippiRustExtensions.h`, and the Dolphin CMake and Visual Studio projects are pre-configured to find this header and link the necessary libraries and dlls.
- The **entry point** of this is the `ffi` crate. (e.g, `Dolphin (C++) -> ffi (C) -> (EXI) (Rust)`)
- As Rust is not the _host_ language, it cannot import anything from the C++ side of things. This means that when you go to bridge things, you'll need to thread calls and data through from the C++ side of things to the Rust side of things. In practice, this shouldn't be too bad - much of the Slippi-specific logic is contained within the `EXI_DeviceSlippi.cpp` class.
- The aforementioned `EXI_DeviceSlippi.cpp` class *owns* the Rust EXI device. When the EXI device is created, the Rust EXI device is created. When the EXI device is torn down, the Rust device is torn down. Do not alter this lifecycle pattern; it keeps things predictable regarding ownership and fits well with the Rust approach.
- If you add a new FFI function, please prefix it with `slprs_`. This helps keep the Dolphin codebase well segmented for "Slippi-Rust"-specific functionality.
> Be careful when trying to pass C++ types! _C_ has a stable ABI, _C++_ does not - you **always go through C**.
## Adding a new workspace module
If you're adding a new workspace module, simply create it (`cargo new --lib my_module`), add it to the root `Cargo.toml`, and setup/link it/call it elsewhere accordingly.
If this is code that Dolphin needs to call (via the C FFI), then it belongs in the `ffi` module. Your exposed method in the `ffi` module can/should forward on to wherever your code actually lives.
## Feature Flags
### The `playback` feature
There is an optional feature flag for this repository for playback-specific functionality. This will automatically be set via either CMake or Visual Studio if you're building a Playback-enabled project, but if you're building and testing out of band you may need to enable this flag, e.g:
```
cargo build --release --features playback
```
## Building out of band
#### Windows
To compile changes made to the Rust extensions without having to rebuild the entire Dolphin project, you can compile _just_ the Rust codebase in the target area. This can work as the Rust code is linked to the Dolphin side of things at runtime; if you alter the Dolphin codebase, _or if you alter the exposed C FFI_, you will need a full project rebuild.
In the `SlippiRustExtensions` directory, run the following:
```
cargo build --release --target=x86_64-pc-windows-msvc
```
> This is necessary on Windows to allow for situations where developers may need to run a VM to test across OS instances. The default `release` path can conflict on the two when mounted on the same filesystem, and we need the Visual Studio builder to know where to reliably look.
#### macOS/Linux/etc
Simply rebuild with the release flag.
```
cargo build --release
```

View File

@@ -0,0 +1,15 @@
[package]
name = "slippi-exi-device"
description = "Implements a shadow EXI device."
version = "0.1.0"
authors = [
"Slippi Team",
"Ryan McGrath <ryan@rymc.io>"
]
edition = "2021"
publish = false
[dependencies]
dolphin-logger = { path = "../logger" }
slippi-jukebox = { path = "../jukebox" }
tracing = { workspace = true }

View File

@@ -0,0 +1,6 @@
# Slippi Rust EXI Device
This module acts as a "shadow" EXI device for the _actual_ C++ EXI Device ([`EXI_DeviceSlippi.cpp`](../../../Source/Core/Core/HW/EXI_DeviceSlippi.cpp)) over on the Dolphin side of things. The C++ class _owns_ this EXI device and acts as the general entry point to everything.
If you're building something on the Rust side, you probably - unless it's a standalone function or you really know it needs to be global - want to just hook it here. Doing so will ensure you get the automatic lifecycle management and can avoid worrying about any Rust cleanup pieces (just implement `Drop`).
For an example of this pattern, check out how the `Jukebox` is orchestrated in this crate.

View File

@@ -0,0 +1,60 @@
//! This module houses the `SlippiEXIDevice`, which is in effect a "shadow subclass" of the C++
//! Slippi EXI device.
//!
//! What this means is that the Slippi EXI Device (C++) holds a pointer to the Rust
//! `SlippiEXIDevice` and forwards calls over the C FFI. This has a fairly clean mapping to "when
//! Slippi stuff is happening" and enables us to let the Rust side live in its own world.
use dolphin_logger::Log;
use slippi_jukebox::Jukebox;
/// An EXI Device subclass specific to managing and interacting with the game itself.
#[derive(Debug)]
pub struct SlippiEXIDevice {
jukebox: Option<Jukebox>,
}
impl SlippiEXIDevice {
/// Creates and returns a new `SlippiEXIDevice` with default values.
///
/// At the moment you should never need to call this yourself.
pub fn new() -> Self {
tracing::info!(target: Log::EXI, "Starting SlippiEXIDevice");
Self { jukebox: None }
}
/// Stubbed for now, but this would get called by the C++ EXI device on DMAWrite.
pub fn dma_write(&mut self, _address: usize, _size: usize) {}
/// Stubbed for now, but this would get called by the C++ EXI device on DMARead.
pub fn dma_read(&mut self, _address: usize, _size: usize) {}
/// Configures a new Jukebox, or ensures an existing one is dropped if it's being disabled.
pub fn configure_jukebox(
&mut self,
is_enabled: bool,
m_p_ram: *const u8,
iso_path: String,
get_dolphin_volume_fn: slippi_jukebox::ForeignGetVolumeFn,
) {
if !is_enabled {
self.jukebox = None;
return;
}
match Jukebox::new(m_p_ram, iso_path, get_dolphin_volume_fn) {
Ok(jukebox) => {
self.jukebox = Some(jukebox);
},
Err(e) => {
tracing::error!(
target: Log::EXI,
error = ?e,
"Failed to start Jukebox"
);
},
}
}
}

View File

@@ -0,0 +1,26 @@
[package]
name = "slippi_rust_extensions"
description = "An internal library that exposes entry points via the C FFI."
version = "0.1.0"
authors = [
"Slippi Team",
"Ryan McGrath <ryan@rymc.io>"
]
repository = ""
edition = "2021"
publish = false
[lib]
crate-type = ["cdylib"]
[build-dependencies]
cbindgen = "0.24.3"
[features]
default = []
playback = []
[dependencies]
dolphin-logger = { path = "../logger" }
slippi-exi-device = { path = "../exi" }
tracing = { workspace = true }

View File

@@ -0,0 +1,8 @@
# Slippi Rust FFI
This crate contains the external API that Dolphin can see and interact with. The API itself is a small set of C functions that handle creating/destroying/updating various components (EXI, Logger, Jukebox, etc) on the Rust side of things.
The included `build.rs` will automatically generate a C header set on each build, and the CMake and Visual Studio projects are configured to locate it from the `includes` folder.
When adding a new method to this crate, prefix it with `slprs_` so that the Dolphin codebase keeps a clear delineation of where Rust code is being used. If (or when) you use `unsafe`, please add a comment explaining _why_ we can guarantee some aspect of safety.
See the method headers for more information.

View File

@@ -0,0 +1,15 @@
//! This build script simply generates C FFI bindings for the freestanding
//! functions in `lib.rs` and dumps them into a header that the Dolphin
//! project is pre-configured to find.
use std::env;
fn main() {
let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
cbindgen::Builder::new()
.with_crate(crate_dir)
.generate()
.expect("Unable to generate bindings")
.write_to_file("includes/SlippiRustExtensions.h");
}

View File

@@ -0,0 +1,73 @@
#include <cstdarg>
#include <cstdint>
#include <cstdlib>
#include <ostream>
#include <new>
extern "C" {
/// Creates and leaks a shadow EXI device.
///
/// The C++ (Dolphin) side of things should call this and pass the appropriate arguments. At
/// that point, everything on the Rust side is its own universe, and should be told to shut
/// down (at whatever point) via the corresponding `slprs_exi_device_destroy` function.
///
/// The returned pointer from this should *not* be used after calling `slprs_exi_device_destroy`.
uintptr_t slprs_exi_device_create();
/// The C++ (Dolphin) side of things should call this to notify the Rust side that it
/// can safely shut down and clean up.
void slprs_exi_device_destroy(uintptr_t exi_device_instance_ptr);
/// This method should be called from the EXI device subclass shim that's registered on
/// the Dolphin side, corresponding to:
///
/// `virtual void DMAWrite(u32 _uAddr, u32 _uSize);`
void slprs_exi_device_dma_write(uintptr_t exi_device_instance_ptr,
const uint8_t *address,
const uint8_t *size);
/// This method should be called from the EXI device subclass shim that's registered on
/// the Dolphin side, corresponding to:
///
/// `virtual void DMARead(u32 _uAddr, u32 _uSize);`
void slprs_exi_device_dma_read(uintptr_t exi_device_instance_ptr,
const uint8_t *address,
const uint8_t *size);
/// Configures the Jukebox process. This needs to be called after the EXI device is created
/// in order for certain pieces of Dolphin to be properly initalized; this may change down
/// the road though and is not set in stone.
void slprs_exi_device_configure_jukebox(uintptr_t exi_device_instance_ptr,
bool is_enabled,
const uint8_t *m_p_ram,
const char *iso_path,
int (*get_dolphin_volume_fn)());
/// This should be called from the Dolphin LogManager initialization to ensure that
/// all logging needs on the Rust side are configured appropriately.
///
/// For more information, consult `dolphin_logger::init`.
///
/// Note that `logger_fn` cannot be type-aliased here, otherwise cbindgen will
/// mess up the header output. That said, the function type represents:
///
/// ```
/// void Log(level, log_type, msg);
/// ```
void slprs_logging_init(void (*logger_fn)(int, int, const char*));
/// Registers a log container, which mirrors a Dolphin `LogContainer` (`RustLogContainer`).
///
/// See `dolphin_logger::register_container` for more information.
void slprs_logging_register_container(const char *kind,
int log_type,
bool is_enabled,
int default_log_level);
/// Updates the configuration for a registered logging container.
///
/// For more information, see `dolphin_logger::update_container`.
void slprs_logging_update_container(const char *kind, bool enabled, int level);
} // extern "C"

View File

@@ -0,0 +1,153 @@
//! This library is the core interface for the Rust side of things, and consists
//! predominantly of C FFI bridging functions that can be called from the Dolphin
//! side of things.
//!
//! This library auto-generates C headers on build, and Slippi Dolphin is pre-configured
//! to locate these headers and link the entire dylib.
use std::ffi::{c_char, c_int, CStr};
use dolphin_logger::Log;
use slippi_exi_device::SlippiEXIDevice;
/// Creates and leaks a shadow EXI device.
///
/// The C++ (Dolphin) side of things should call this and pass the appropriate arguments. At
/// that point, everything on the Rust side is its own universe, and should be told to shut
/// down (at whatever point) via the corresponding `slprs_exi_device_destroy` function.
///
/// The returned pointer from this should *not* be used after calling `slprs_exi_device_destroy`.
#[no_mangle]
pub extern "C" fn slprs_exi_device_create() -> usize {
let exi_device = Box::new(SlippiEXIDevice::new());
let exi_device_instance_ptr = Box::into_raw(exi_device) as usize;
tracing::warn!(target: Log::EXI, ptr = exi_device_instance_ptr, "Creating Device");
exi_device_instance_ptr
}
/// The C++ (Dolphin) side of things should call this to notify the Rust side that it
/// can safely shut down and clean up.
#[no_mangle]
pub extern "C" fn slprs_exi_device_destroy(exi_device_instance_ptr: usize) {
tracing::warn!(target: Log::EXI, ptr = exi_device_instance_ptr, "Destroying Device");
// Coerce the instance from the pointer. This is theoretically safe since we control
// the C++ side and can guarantee that the `exi_device_instance_ptr` is only owned
// by the C++ EXI device, and is created/destroyed with the corresponding lifetimes.
unsafe {
// Coerce ownership back, then let standard Drop semantics apply
let _device = Box::from_raw(exi_device_instance_ptr as *mut SlippiEXIDevice);
}
}
/// This method should be called from the EXI device subclass shim that's registered on
/// the Dolphin side, corresponding to:
///
/// `virtual void DMAWrite(u32 _uAddr, u32 _uSize);`
#[no_mangle]
pub extern "C" fn slprs_exi_device_dma_write(exi_device_instance_ptr: usize, address: *const u8, size: *const u8) {
// Coerce the instance back from the pointer. This is theoretically safe since we control
// the C++ side and can guarantee that the `exi_device_instance_ptr` pointer is only owned
// by the C++ EXI device, and is created/destroyed with the corresponding lifetimes.
let mut device = unsafe { Box::from_raw(exi_device_instance_ptr as *mut SlippiEXIDevice) };
device.dma_write(address as usize, size as usize);
// Fall back into a raw pointer so Rust doesn't obliterate the object
let _leak = Box::into_raw(device);
}
/// This method should be called from the EXI device subclass shim that's registered on
/// the Dolphin side, corresponding to:
///
/// `virtual void DMARead(u32 _uAddr, u32 _uSize);`
#[no_mangle]
pub extern "C" fn slprs_exi_device_dma_read(exi_device_instance_ptr: usize, address: *const u8, size: *const u8) {
// Coerce the instance from the pointer. This is theoretically safe since we control
// the C++ side and can guarantee that the `exi_device_instance_ptr` pointer is only owned
// by the C++ EXI device, and is created/destroyed with the corresponding lifetimes.
let mut device = unsafe { Box::from_raw(exi_device_instance_ptr as *mut SlippiEXIDevice) };
device.dma_read(address as usize, size as usize);
// Fall back into a raw pointer so Rust doesn't obliterate the object.
let _leak = Box::into_raw(device);
}
/// Configures the Jukebox process. This needs to be called after the EXI device is created
/// in order for certain pieces of Dolphin to be properly initalized; this may change down
/// the road though and is not set in stone.
#[no_mangle]
pub extern "C" fn slprs_exi_device_configure_jukebox(
exi_device_instance_ptr: usize,
is_enabled: bool,
m_p_ram: *const u8,
iso_path: *const c_char,
get_dolphin_volume_fn: unsafe extern "C" fn() -> c_int,
) {
// Convert the provided ISO path to an owned Rust string.
// This is theoretically safe since we control the C++ side and can can mostly guarantee
// the validity of what is being passed in.
let slice = unsafe { CStr::from_ptr(iso_path) };
// What we *can't* guarantee is that it's proper UTF-8 etc. If we can't parse it into a
// Rust String, then we'll just avoid running the Jukebox entirely and log an error message
// for people to debug with.
let iso_path = match slice.to_str() {
Ok(path) => path.to_string(),
Err(e) => {
tracing::error!(error = ?e, "Failed to bridge iso_path, jukebox not initializing");
return;
},
};
// Coerce the instance from the pointer. This is theoretically safe since we control
// the C++ side and can guarantee that the `exi_device_instance_ptr` is only owned
// by the C++ EXI device, and is created/destroyed with the corresponding lifetimes.
let mut device = unsafe { Box::from_raw(exi_device_instance_ptr as *mut SlippiEXIDevice) };
device.configure_jukebox(is_enabled, m_p_ram, iso_path, get_dolphin_volume_fn);
// Fall back into a raw pointer so Rust doesn't obliterate the object.
let _leak = Box::into_raw(device);
}
/// This should be called from the Dolphin LogManager initialization to ensure that
/// all logging needs on the Rust side are configured appropriately.
///
/// For more information, consult `dolphin_logger::init`.
///
/// Note that `logger_fn` cannot be type-aliased here, otherwise cbindgen will
/// mess up the header output. That said, the function type represents:
///
/// ```
/// void Log(level, log_type, msg);
/// ```
#[no_mangle]
pub extern "C" fn slprs_logging_init(logger_fn: unsafe extern "C" fn(c_int, c_int, *const c_char)) {
dolphin_logger::init(logger_fn);
}
/// Registers a log container, which mirrors a Dolphin `LogContainer` (`RustLogContainer`).
///
/// See `dolphin_logger::register_container` for more information.
#[no_mangle]
pub extern "C" fn slprs_logging_register_container(
kind: *const c_char,
log_type: c_int,
is_enabled: bool,
default_log_level: c_int,
) {
dolphin_logger::register_container(kind, log_type, is_enabled, default_log_level);
}
/// Updates the configuration for a registered logging container.
///
/// For more information, see `dolphin_logger::update_container`.
#[no_mangle]
pub extern "C" fn slprs_logging_update_container(kind: *const c_char, enabled: bool, level: c_int) {
dolphin_logger::update_container(kind, enabled, level);
}

View File

@@ -0,0 +1,19 @@
[package]
name = "slippi-jukebox"
description = "A built-in solution to play melee's OST in a manner that's effectively detached from emulation."
version = "0.1.0"
authors = [
"Slippi Team",
"Daryl Pinto <daryl.j.pinto@gmail.com>"
]
edition = "2021"
publish = false
[dependencies]
anyhow = "1.0.71"
dolphin-logger = { path = "../logger" }
fastrand = "2.0.0"
hps_decode = "0.1.1"
process-memory = "0.5.0"
rodio = "0.17.1"
tracing = { workspace = true }

View File

@@ -0,0 +1,40 @@
# Slippi Jukebox
Slippi Jukebox is a built-in solution to play Melee's OST in a manner that's effectively detached from emulation.
## Features
- Menu music
- All stage music
- Alternate stage music<sup>1</sup>
- "Versus" splash screen jingle
- Ranked stage striking music
- Auto-ducking music volume when pausing the game
- _Break the Targets_ + _Home Run Contest_ music
- Controlling volume with the in-game options and Dolphin's audio volume slider
- Lottery menu music
- Single player mode<sup>2</sup>
1. _Alternate stage music has a 12.5% chance of playing. Holding triggers to force alternate tracks to play is not supported._
2. _In some 1P modes, stage music will differ from vs mode. This behavior is not supported by jukebox. Additionally, the following songs will not play:_
<ul>
<li>Classic "Stage Clear" Jingle</li>
<li>Classic "Continue?" and "Game Over" Jingles</li>
<li>Credits music</li>
<li>Post-credits congratulations fmv music</li>
</ul>
## How it works
When a `Jukebox` instance is created (generally from an EXI Device), it scans the iso for music files and stores their locations + file sizes in a hashmap.
Two child threads are immediately spawned: `JukeboxMessageDispatcher` and `JukeboxMusicPlayer`. Together these threads form an event loop.
The message dispatcher continuously reads from dolphin's game memory and dispatches relevant events. The music player thread listens to the events and handles them by decoding the appropriate music files and playing them with the default audio device.
When the `Jukebox` instance is dropped, the child threads are instructed to terminate, the event loop breaks, and the music stops.
## Decoding Melee's Music
The logic for decoding Melee's music has been split out into a public library. See the [`hps_decode`](https://crates.io/crates/hps_decode) crate for more. For general information about the `.hps` file format, [see here.](https://github.com/DarylPinto/hps_decode/blob/main/HPS-LAYOUT.md)

View File

@@ -0,0 +1,424 @@
mod scenes;
mod tracks;
mod utils;
use anyhow::{Context, Result};
use dolphin_logger::Log;
use hps_decode::{hps::Hps, pcm_iterator::PcmIterator};
use process_memory::LocalMember;
use process_memory::Memory;
use rodio::{OutputStream, Sink};
use scenes::scene_ids::*;
use std::convert::TryInto;
use std::ops::ControlFlow::{self, Break, Continue};
use std::sync::mpsc::{channel, Receiver, Sender};
use std::{thread::sleep, time::Duration};
use tracks::TrackId;
/// Represents a foreign method from the Dolphin side for grabbing the current volume.
/// Dolphin represents this as a number from 0 - 100; 0 being mute.
pub type ForeignGetVolumeFn = unsafe extern "C" fn() -> std::ffi::c_int;
const THREAD_LOOP_SLEEP_TIME_MS: u64 = 30;
const CHILD_THREAD_COUNT: usize = 2;
/// By default Slippi Jukebox plays music slightly louder than vanilla melee
/// does. This reduces the overall music volume output to 80%. Not totally sure
/// if that's the correct amount, but it sounds about right.
const VOLUME_REDUCTION_MULTIPLIER: f32 = 0.8;
#[derive(Debug, PartialEq)]
struct DolphinGameState {
in_game: bool,
in_menus: bool,
scene_major: u8,
scene_minor: u8,
stage_id: u8,
volume: f32,
is_paused: bool,
match_info: u8,
}
impl Default for DolphinGameState {
fn default() -> Self {
Self {
in_game: false,
in_menus: false,
scene_major: SCENE_MAIN_MENU,
scene_minor: 0,
stage_id: 0,
volume: 0.0,
is_paused: false,
match_info: 0,
}
}
}
#[derive(Debug)]
enum MeleeEvent {
TitleScreenEntered,
MenuEntered,
LotteryEntered,
GameStart(u8), // stage id
GameEnd,
RankedStageStrikeEntered,
VsOnlineOpponent,
Pause,
Unpause,
SetVolume(f32),
NoOp,
}
#[derive(Debug, Clone)]
enum JukeboxEvent {
Dropped,
}
#[derive(Debug)]
pub struct Jukebox {
channel_senders: [Sender<JukeboxEvent>; CHILD_THREAD_COUNT],
}
impl Jukebox {
/// Returns a Jukebox instance that will immediately spawn two child threads
/// to try and read game memory and play music. When the returned instance is
/// dropped, the child threads will terminate and the music will stop.
pub fn new(m_p_ram: *const u8, iso_path: String, get_dolphin_volume_fn: ForeignGetVolumeFn) -> Result<Self> {
tracing::info!(target: Log::Jukebox, "Initializing Slippi Jukebox");
// We are implicitly trusting that these pointers will outlive the jukebox instance
let m_p_ram = m_p_ram as usize;
let get_dolphin_volume = move || unsafe { get_dolphin_volume_fn() } as f32 / 100.0;
// This channel is used for the `JukeboxMessageDispatcher` thread to send
// messages to the `JukeboxMusicPlayer` thread
let (melee_event_tx, melee_event_rx) = channel::<MeleeEvent>();
// These channels allow the jukebox instance to notify both child
// threads when something important happens. Currently its only purpose
// is to notify them that the instance is about to be dropped so they
// should terminate
let (message_dispatcher_thread_tx, message_dispatcher_thread_rx) = channel::<JukeboxEvent>();
let (music_thread_tx, music_thread_rx) = channel::<JukeboxEvent>();
std::thread::Builder::new()
.name("JukeboxMessageDispatcher".to_string())
.spawn(move || Self::dispatch_messages(m_p_ram, get_dolphin_volume, message_dispatcher_thread_rx, melee_event_tx))?;
std::thread::Builder::new()
.name("JukeboxMusicPlayer".to_string())
.spawn(move || Self::play_music(m_p_ram, &iso_path, get_dolphin_volume, music_thread_rx, melee_event_rx))?;
Ok(Self {
channel_senders: [message_dispatcher_thread_tx, music_thread_tx],
})
}
/// This thread continuously reads select values from game memory as well
/// as the current `volume` value in the dolphin configuration. If it
/// notices anything change, it will dispatch a message to the
/// `JukeboxMusicPlayer` thread.
fn dispatch_messages(
m_p_ram: usize,
get_dolphin_volume: impl Fn() -> f32,
message_dispatcher_thread_rx: Receiver<JukeboxEvent>,
melee_event_tx: Sender<MeleeEvent>,
) -> Result<()> {
// Initial "dolphin state" that will get updated over time
let mut prev_state = DolphinGameState::default();
loop {
// Stop the thread if the jukebox instance will be been dropped
if let Ok(event) = message_dispatcher_thread_rx.try_recv() {
if matches!(event, JukeboxEvent::Dropped) {
return Ok(());
}
}
// Continuously check if the dolphin state has changed
let state = Self::read_dolphin_game_state(&m_p_ram, get_dolphin_volume())?;
// If the state has changed,
if prev_state != state {
// dispatch a message to the music player thread
let event = Self::produce_melee_event(&prev_state, &state);
tracing::info!(target: Log::Jukebox, "{:?}", event);
melee_event_tx.send(event)?;
prev_state = state;
}
sleep(Duration::from_millis(THREAD_LOOP_SLEEP_TIME_MS));
}
}
/// This thread listens for incoming messages from the
/// `JukeboxMessageDispatcher` thread and handles music playback
/// accordingly.
fn play_music(
m_p_ram: usize,
iso_path: &str,
get_dolphin_volume: impl Fn() -> f32,
music_thread_rx: Receiver<JukeboxEvent>,
melee_event_rx: Receiver<MeleeEvent>,
) -> Result<()> {
let mut iso = std::fs::File::open(iso_path)?;
tracing::info!(target: Log::Jukebox, "Loading track metadata...");
let tracks = utils::create_track_map(&mut iso)?;
tracing::info!(target: Log::Jukebox, "Loaded metadata for {} tracks!", tracks.len());
let (_stream, stream_handle) = OutputStream::try_default()?;
let sink = Sink::try_new(&stream_handle)?;
// The menu track and tournament-mode track are randomly selected
// one time, and will be used for the rest of the session
let random_menu_tracks = utils::get_random_menu_tracks();
// Initial music volume and track id. These values will get
// updated by the `handle_melee_event` fn whenever a message is
// received from the other thread.
let initial_state = Self::read_dolphin_game_state(&m_p_ram, get_dolphin_volume())?;
let mut volume = initial_state.volume;
let mut track_id: Option<TrackId> = None;
loop {
if let Some(track_id) = track_id {
// Lookup the current track_id in the `tracks` hashmap,
// and if it's present, then play it. If not, there will
// be silence until a new track_id is set
let track = tracks.get(&track_id);
if let Some(&(offset, size)) = track {
let offset = offset as u64;
let size = size as usize;
// Parse data from the ISO into pcm samples
let hps: Hps = utils::read_from_file(&mut iso, offset, size)?
.try_into()
.with_context(|| format!("The {size} bytes at offset 0x{offset:x?} could not be decoded into an Hps"))?;
let padding_length = hps.channel_count * hps.sample_rate / 4;
let audio_source = HpsAudioSource {
pcm: hps.into(),
padding_length,
};
// Play song
sink.append(audio_source);
sink.play();
sink.set_volume(volume);
}
}
// Continue to play the song indefinitely while regularly checking
// for new messages from the `JukeboxMessageDispatcher` thread
loop {
// Stop the thread if the jukebox instance will be been dropped
if let Ok(event) = music_thread_rx.try_recv() {
if matches!(event, JukeboxEvent::Dropped) {
return Ok(());
}
}
// When we receive an event, handle it. This can include
// changing the volume or updating the track and breaking
// the inner loop such that the next track starts to play
if let Ok(event) = melee_event_rx.try_recv() {
if let Break(_) = Self::handle_melee_event(event, &sink, &mut track_id, &mut volume, &random_menu_tracks) {
break;
}
}
sleep(Duration::from_millis(THREAD_LOOP_SLEEP_TIME_MS));
}
sink.stop();
}
}
/// Handle a events received in the audio playback thread, by changing tracks,
/// adjusting volume etc.
fn handle_melee_event(
event: MeleeEvent,
sink: &Sink,
track_id: &mut Option<TrackId>,
volume: &mut f32,
random_menu_tracks: &(TrackId, TrackId),
) -> ControlFlow<()> {
use self::MeleeEvent::*;
// TODO:
// - Intro movie
//
// - classic vs screen
// - classic victory screen
// - classic game over screen
// - classic credits
// - classic "congratulations movie"
// - Adventure mode field intro music
let (menu_track, tournament_track) = random_menu_tracks;
match event {
TitleScreenEntered | GameEnd => {
*track_id = None;
},
MenuEntered => {
*track_id = Some(*menu_track);
},
LotteryEntered => {
*track_id = Some(tracks::TrackId::Lottery);
},
VsOnlineOpponent => {
*track_id = Some(tracks::TrackId::VsOpponent);
},
RankedStageStrikeEntered => {
*track_id = Some(*tournament_track);
},
GameStart(stage_id) => {
*track_id = tracks::get_stage_track_id(stage_id);
},
Pause => {
sink.set_volume(*volume * 0.2);
return Continue(());
},
Unpause => {
sink.set_volume(*volume);
return Continue(());
},
SetVolume(received_volume) => {
sink.set_volume(received_volume);
*volume = received_volume;
return Continue(());
},
NoOp => {
return Continue(());
},
};
Break(())
}
/// Given the previous dolphin state and current dolphin state, produce an event
fn produce_melee_event(prev_state: &DolphinGameState, state: &DolphinGameState) -> MeleeEvent {
let vs_screen_1 = state.scene_major == SCENE_VS_ONLINE
&& prev_state.scene_minor != SCENE_VS_ONLINE_VERSUS
&& state.scene_minor == SCENE_VS_ONLINE_VERSUS;
let vs_screen_2 = prev_state.scene_minor == SCENE_VS_ONLINE_VERSUS && state.stage_id == 0;
let entered_vs_online_opponent_screen = vs_screen_1 || vs_screen_2;
if state.scene_major == SCENE_VS_ONLINE
&& prev_state.scene_minor != SCENE_VS_ONLINE_RANKED
&& state.scene_minor == SCENE_VS_ONLINE_RANKED
{
MeleeEvent::RankedStageStrikeEntered
} else if !prev_state.in_menus && state.in_menus {
MeleeEvent::MenuEntered
} else if prev_state.scene_major != SCENE_TITLE_SCREEN && state.scene_major == SCENE_TITLE_SCREEN {
MeleeEvent::TitleScreenEntered
} else if entered_vs_online_opponent_screen {
MeleeEvent::VsOnlineOpponent
} else if prev_state.scene_major != SCENE_TROPHY_LOTTERY && state.scene_major == SCENE_TROPHY_LOTTERY {
MeleeEvent::LotteryEntered
} else if (!prev_state.in_game && state.in_game) || prev_state.stage_id != state.stage_id {
MeleeEvent::GameStart(state.stage_id)
} else if prev_state.in_game && state.in_game && state.match_info == 1 {
MeleeEvent::GameEnd
} else if prev_state.volume != state.volume {
MeleeEvent::SetVolume(state.volume)
} else if !prev_state.is_paused && state.is_paused {
MeleeEvent::Pause
} else if prev_state.is_paused && !state.is_paused {
MeleeEvent::Unpause
} else {
MeleeEvent::NoOp
}
}
/// Create a `DolphinGameState` by reading Dolphin's memory
fn read_dolphin_game_state(m_p_ram: &usize, dolphin_volume_percent: f32) -> Result<DolphinGameState> {
#[inline(always)]
fn read<T: Copy>(offset: usize) -> Result<T> {
Ok(unsafe { LocalMember::<T>::new_offset(vec![offset]).read()? })
}
// https://github.com/bkacjios/m-overlay/blob/d8c629d/source/modules/games/GALE01-2.lua#L8
let melee_volume_percent = ((read::<i8>(m_p_ram + 0x45C384)? as f32 - 100.0) * -1.0) / 100.0;
// https://github.com/bkacjios/m-overlay/blob/d8c629d/source/modules/games/GALE01-2.lua#L16
let scene_major = read::<u8>(m_p_ram + 0x479D30)?;
// https://github.com/bkacjios/m-overlay/blob/d8c629d/source/modules/games/GALE01-2.lua#L19
let scene_minor = read::<u8>(m_p_ram + 0x479D33)?;
// https://github.com/bkacjios/m-overlay/blob/d8c629d/source/modules/games/GALE01-2.lua#L357
let stage_id = read::<u8>(m_p_ram + 0x49E753)?;
// https://github.com/bkacjios/m-overlay/blob/d8c629d/source/modules/games/GALE01-2.lua#L248
// 0 = in game, 1 = GAME! screen, 2 = Stage clear in 1p mode? (maybe also victory screen), 3 = menu
let match_info = read::<u8>(m_p_ram + 0x46B6A0)?;
// https://github.com/bkacjios/m-overlay/blob/d8c629d/source/modules/games/GALE01-2.lua#L353
let is_paused = read::<u8>(m_p_ram + 0x4D640F)? == 1;
Ok(DolphinGameState {
in_game: utils::is_in_game(scene_major, scene_minor),
in_menus: utils::is_in_menus(scene_major, scene_minor),
scene_major,
scene_minor,
volume: dolphin_volume_percent * melee_volume_percent * VOLUME_REDUCTION_MULTIPLIER,
stage_id,
is_paused,
match_info,
})
}
}
impl Drop for Jukebox {
fn drop(&mut self) {
tracing::info!(target: Log::Jukebox, "Dropping Slippi Jukebox");
for sender in &self.channel_senders {
if let Err(e) = sender.send(JukeboxEvent::Dropped) {
tracing::error!(
target: Log::Jukebox,
"Failed to notify child thread that Jukebox is dropping: {e}"
);
}
}
}
}
// This wrapper allows us to implement `rodio::Source`
struct HpsAudioSource {
pcm: PcmIterator,
padding_length: u32,
}
impl Iterator for HpsAudioSource {
type Item = i16;
fn next(&mut self) -> Option<Self::Item> {
// We need to pad the start of the music playback with a quarter second
// of silence so when two tracks are loaded in quick succession, we
// don't hear a quick "blip" from the first track. This happens in
// practice because scene_minor tells us we're in-game before stage_id
// has a chance to update from the previously played stage.
//
// Return 0s (silence) for the length of the padding
if self.padding_length > 0 {
self.padding_length -= 1;
return Some(0);
}
// Then start iterating on the actual samples
self.pcm.next()
}
}
impl rodio::Source for HpsAudioSource {
fn current_frame_len(&self) -> Option<usize> {
None
}
fn channels(&self) -> u16 {
self.pcm.channel_count as u16
}
fn sample_rate(&self) -> u32 {
self.pcm.sample_rate
}
fn total_duration(&self) -> Option<std::time::Duration> {
None
}
}

View File

@@ -0,0 +1,396 @@
/// Sourced from M'OVerlay: https://github.com/bkacjios/m-overlay/blob/d8c629d/source/melee.lua
#[rustfmt::skip]
#[allow(dead_code)]
pub(crate) mod scene_ids {
pub(crate) const MATCH_NO_RESULT: u8 = 0x00;
pub(crate) const MATCH_GAME: u8 = 0x02;
pub(crate) const MATCH_STAGE_CLEAR: u8 = 0x03;
pub(crate) const MATCH_STAGE_FAILURE: u8 = 0x04;
pub(crate) const MATCH_STAGE_CLEAR3: u8 = 0x05;
pub(crate) const MATCH_NEW_RECORD: u8 = 0x06;
pub(crate) const MATCH_NO_CONTEST: u8 = 0x07;
pub(crate) const MATCH_RETRY: u8 = 0x08;
pub(crate) const MATCH_GAME_CLEAR: u8 = 0x09;
// MAJOR FLAGS
pub(crate) const SCENE_TITLE_SCREEN: u8 = 0x00;
pub(crate) const SCENE_MAIN_MENU: u8 = 0x01;
// MENU FLAGS
pub(crate) const MENU_MAIN: u8 = 0x00;
pub(crate) const SELECT_MAIN_1P: u8 = 0x00;
pub(crate) const SELECT_MAIN_VS: u8 = 0x01;
pub(crate) const SELECT_MAIN_TROPHY: u8 = 0x02;
pub(crate) const SELECT_MAIN_OPTIONS: u8 = 0x03;
pub(crate) const SELECT_MAIN_DATA: u8 = 0x04;
pub(crate) const MENU_1P: u8 = 0x01;
pub(crate) const SELECT_1P_REGULAR: u8 = 0x00;
pub(crate) const SELECT_1P_EVENT: u8 = 0x01;
pub(crate) const SELECT_1P_ONLINE: u8 = 0x2;
pub(crate) const SELECT_1P_STADIUM: u8 = 0x03;
pub(crate) const SELECT_1P_TRAINING: u8 = 0x04;
pub(crate) const MENU_VS: u8 = 0x02;
pub(crate) const SELECT_VS_MELEE: u8 = 0x00;
pub(crate) const SELECT_VS_TOURNAMENT: u8 = 0x01;
pub(crate) const SELECT_VS_SPECIAL: u8 = 0x02;
pub(crate) const SELECT_VS_CUSTOM: u8 = 0x03;
pub(crate) const SELECT_VS_NAMEENTRY: u8 = 0x04;
pub(crate) const MENU_TROPHIES: u8 = 0x03;
pub(crate) const SELECT_TROPHIES_GALLERY: u8 = 0x00;
pub(crate) const SELECT_TROPHIES_LOTTERY: u8 = 0x01;
pub(crate) const SELECT_TROPHIES_COLLECTION: u8 = 0x02;
pub(crate) const MENU_OPTIONS: u8 = 0x04;
pub(crate) const SELECT_OPTIONS_RUMBLE: u8 = 0x00;
pub(crate) const SELECT_OPTIONS_SOUND: u8 = 0x01;
pub(crate) const SELECT_OPTIONS_DISPLAY: u8 = 0x02;
pub(crate) const SELECT_OPTIONS_UNKNOWN: u8 = 0x03;
pub(crate) const SELECT_OPTIONS_LANGUAGE: u8 = 0x04;
pub(crate) const SELECT_OPTIONS_ERASE_DATA: u8 = 0x05;
pub(crate) const MENU_ONLINE: u8 = 0x08;
pub(crate) const SELECT_ONLINE_RANKED: u8 = 0x00;
pub(crate) const SELECT_ONLINE_UNRANKED: u8 = 0x01;
pub(crate) const SELECT_ONLINE_DIRECT: u8 = 0x02;
pub(crate) const SELECT_ONLINE_TEAMS: u8 = 0x03;
pub(crate) const SELECT_ONLINE_LOGOUT: u8 = 0x05;
pub(crate) const MENU_STADIUM: u8 = 0x09;
pub(crate) const SELECT_STADIUM_TARGET_TEST: u8 = 0x00;
pub(crate) const SELECT_STADIUM_HOMERUN_CONTEST: u8 = 0x01;
pub(crate) const SELECT_STADIUM_MULTIMAN_MELEE: u8 = 0x02;
pub(crate) const MENU_RUMBLE: u8 = 0x13;
pub(crate) const MENU_SOUND: u8 = 0x14;
pub(crate) const MENU_DISPLAY: u8 = 0x15;
pub(crate) const MENU_UNKNOWN1: u8 = 0x16;
pub(crate) const MENU_LANGUAGE: u8 = 0x17;
pub(crate) const SCENE_VS_MODE: u8 = 0x02;
// MINOR FLAGS
pub(crate) const SCENE_VS_CSS: u8 = 0x0;
pub(crate) const SCENE_VS_SSS: u8 = 0x1;
pub(crate) const SCENE_VS_INGAME: u8 = 0x2;
pub(crate) const SCENE_VS_POSTGAME: u8 = 0x4;
pub(crate) const SCENE_CLASSIC_MODE: u8 = 0x03;
pub(crate) const SCENE_CLASSIC_LEVEL_1_VS: u8 = 0x00;
pub(crate) const SCENE_CLASSIC_LEVEL_1: u8 = 0x01;
pub(crate) const SCENE_CLASSIC_LEVEL_2_VS: u8 = 0x02;
pub(crate) const SCENE_CLASSIC_LEVEL_2: u8 = 0x03;
pub(crate) const SCENE_CLASSIC_LEVEL_3_VS: u8 = 0x04;
pub(crate) const SCENE_CLASSIC_LEVEL_3: u8 = 0x05;
pub(crate) const SCENE_CLASSIC_LEVEL_4_VS: u8 = 0x06;
pub(crate) const SCENE_CLASSIC_LEVEL_4: u8 = 0x07;
// pub(crate) const SCENE_CLASSIC_LEVEL_5_VS: u8 = 0x08;
// pub(crate) const SCENE_CLASSIC_LEVEL_5: u8 = 0x09;
pub(crate) const SCENE_CLASSIC_LEVEL_5_VS: u8 = 0x10;
pub(crate) const SCENE_CLASSIC_LEVEL_5: u8 = 0x09;
pub(crate) const SCENE_CLASSIC_LEVEL_16: u8 = 0x20;
pub(crate) const SCENE_CLASSIC_LEVEL_16_VS: u8 = 0x21;
pub(crate) const SCENE_CLASSIC_LEVEL_24: u8 = 0x30;
pub(crate) const SCENE_CLASSIC_LEVEL_24_VS: u8 = 0x31;
pub(crate) const SCENE_CLASSIC_BREAK_THE_TARGETS_INTRO: u8 = 0x16;
pub(crate) const SCENE_CLASSIC_BREAK_THE_TARGETS: u8 = 0x17;
pub(crate) const SCENE_CLASSIC_TROPHY_STAGE_INTRO: u8 = 0x28;
pub(crate) const SCENE_CLASSIC_TROPHY_STAGE_TARGETS: u8 = 0x29;
pub(crate) const SCENE_CLASSIC_RACE_TO_FINISH_INTRO: u8 = 0x40;
pub(crate) const SCENE_CLASSIC_RACE_TO_FINISH_TARGETS: u8 = 0x41;
pub(crate) const SCENE_CLASSIC_LEVEL_56: u8 = 0x38;
pub(crate) const SCENE_CLASSIC_LEVEL_56_VS: u8 = 0x39;
pub(crate) const SCENE_CLASSIC_MASTER_HAND: u8 = 0x51;
pub(crate) const SCENE_CLASSIC_CONTINUE: u8 = 0x69;
pub(crate) const SCENE_CLASSIC_CSS: u8 = 0x70;
pub(crate) const SCENE_ADVENTURE_MODE: u8 = 0x04;
pub(crate) const SCENE_ADVENTURE_MUSHROOM_KINGDOM_INTRO: u8 = 0x00;
pub(crate) const SCENE_ADVENTURE_MUSHROOM_KINGDOM: u8 = 0x01;
pub(crate) const SCENE_ADVENTURE_MUSHROOM_KINGDOM_LUIGI: u8 = 0x02;
pub(crate) const SCENE_ADVENTURE_MUSHROOM_KINGDOM_BATTLE: u8 = 0x03;
pub(crate) const SCENE_ADVENTURE_MUSHROOM_KONGO_JUNGLE_INTRO: u8 = 0x08;
pub(crate) const SCENE_ADVENTURE_MUSHROOM_KONGO_JUNGLE_TINY_BATTLE: u8 = 0x09;
pub(crate) const SCENE_ADVENTURE_MUSHROOM_KONGO_JUNGLE_GIANT_BATTLE: u8 = 0x0A;
pub(crate) const SCENE_ADVENTURE_UNDERGROUND_MAZE_INTRO: u8 = 0x10;
pub(crate) const SCENE_ADVENTURE_UNDERGROUND_MAZE: u8 = 0x11;
pub(crate) const SCENE_ADVENTURE_HYRULE_TEMPLE_BATTLE: u8 = 0x12;
pub(crate) const SCENE_ADVENTURE_BRINSTAR_INTRO: u8 = 0x18;
pub(crate) const SCENE_ADVENTURE_BRINSTAR: u8 = 0x19;
pub(crate) const SCENE_ADVENTURE_ESCAPE_ZEBES_INTRO: u8 = 0x1A;
pub(crate) const SCENE_ADVENTURE_ESCAPE_ZEBES: u8 = 0x1B;
pub(crate) const SCENE_ADVENTURE_ESCAPE_ZEBES_ESCAPE: u8 = 0x1C;
pub(crate) const SCENE_ADVENTURE_GREEN_GREENS_INTRO: u8 = 0x20;
pub(crate) const SCENE_ADVENTURE_GREEN_GREENS_KIRBY_BATTLE: u8 = 0x21;
pub(crate) const SCENE_ADVENTURE_GREEN_GREENS_KIRBY_TEAM_INTRO: u8 = 0x22;
pub(crate) const SCENE_ADVENTURE_GREEN_GREENS_KIRBY_TEAM_BATTLE: u8 = 0x23;
pub(crate) const SCENE_ADVENTURE_GREEN_GREENS_GIANT_KIRBY_INTRO: u8 = 0x24;
pub(crate) const SCENE_ADVENTURE_GREEN_GREENS_GIANT_KIRBY_BATTLE: u8 = 0x25;
pub(crate) const SCENE_ADVENTURE_CORNERIA_INTRO: u8 = 0x28;
pub(crate) const SCENE_ADVENTURE_CORNERIA_BATTLE_1: u8 = 0x29;
pub(crate) const SCENE_ADVENTURE_CORNERIA_RAID: u8 = 0x2A;
pub(crate) const SCENE_ADVENTURE_CORNERIA_BATTLE_2: u8 = 0x2B;
pub(crate) const SCENE_ADVENTURE_CORNERIA_BATTLE_3: u8 = 0x2C;
pub(crate) const SCENE_ADVENTURE_POKEMON_STADIUM_INTRO: u8 = 0x30;
pub(crate) const SCENE_ADVENTURE_POKEMON_STADIUM_BATTLE: u8 = 0x31;
pub(crate) const SCENE_ADVENTURE_FZERO_GRAND_PRIX_CARS: u8 = 0x38;
pub(crate) const SCENE_ADVENTURE_FZERO_GRAND_PRIX_INTRO: u8 = 0x39;
pub(crate) const SCENE_ADVENTURE_FZERO_GRAND_PRIX_RACE: u8 = 0x3A;
pub(crate) const SCENE_ADVENTURE_FZERO_GRAND_PRIX_BATTLE: u8 = 0x3B;
pub(crate) const SCENE_ADVENTURE_ONETT_INTRO: u8 = 0x40;
pub(crate) const SCENE_ADVENTURE_ONETT_BATTLE: u8 = 0x41;
pub(crate) const SCENE_ADVENTURE_ICICLE_MOUNTAIN_INTRO: u8 = 0x48;
pub(crate) const SCENE_ADVENTURE_ICICLE_MOUNTAIN_CLIMB: u8 = 0x49;
pub(crate) const SCENE_ADVENTURE_BATTLEFIELD_INTRO: u8 = 0x50;
pub(crate) const SCENE_ADVENTURE_BATTLEFIELD_BATTLE: u8 = 0x51;
pub(crate) const SCENE_ADVENTURE_BATTLEFIELD_METAL_INTRO: u8 = 0x52;
pub(crate) const SCENE_ADVENTURE_BATTLEFIELD_METAL_BATTLE: u8 = 0x53;
pub(crate) const SCENE_ADVENTURE_FINAL_DESTINATION_INTRO: u8 = 0x58;
pub(crate) const SCENE_ADVENTURE_FINAL_DESTINATION_BATTLE: u8 = 0x59;
pub(crate) const SCENE_ADVENTURE_FINAL_DESTINATION_POSE: u8 = 0x5A;
pub(crate) const SCENE_ADVENTURE_FINAL_DESTINATION_WINNER: u8 = 0x5B;
pub(crate) const SCENE_ADVENTURE_CSS: u8 = 0x70;
pub(crate) const SCENE_ALL_STAR_MODE: u8 = 0x05;
pub(crate) const SCENE_ALL_STAR_LEVEL_1: u8 = 0x00;
pub(crate) const SCENE_ALL_STAR_REST_AREA_1: u8 = 0x01;
pub(crate) const SCENE_ALL_STAR_LEVEL_2: u8 = 0x02;
pub(crate) const SCENE_ALL_STAR_REST_AREA_2: u8 = 0x03;
pub(crate) const SCENE_ALL_STAR_LEVEL_3: u8 = 0x04;
pub(crate) const SCENE_ALL_STAR_REST_AREA_3: u8 = 0x05;
pub(crate) const SCENE_ALL_STAR_LEVEL_4: u8 = 0x06;
pub(crate) const SCENE_ALL_STAR_REST_AREA_4: u8 = 0x07;
pub(crate) const SCENE_ALL_STAR_LEVEL_5: u8 = 0x08;
pub(crate) const SCENE_ALL_STAR_REST_AREA_5: u8 = 0x09;
pub(crate) const SCENE_ALL_STAR_LEVEL_6: u8 = 0x10;
pub(crate) const SCENE_ALL_STAR_REST_AREA_6: u8 = 0x11;
pub(crate) const SCENE_ALL_STAR_LEVEL_7: u8 = 0x12;
pub(crate) const SCENE_ALL_STAR_REST_AREA_7: u8 = 0x13;
pub(crate) const SCENE_ALL_STAR_LEVEL_8: u8 = 0x14;
pub(crate) const SCENE_ALL_STAR_REST_AREA_8: u8 = 0x15;
pub(crate) const SCENE_ALL_STAR_LEVEL_9: u8 = 0x16;
pub(crate) const SCENE_ALL_STAR_REST_AREA_9: u8 = 0x17;
pub(crate) const SCENE_ALL_STAR_LEVEL_10: u8 = 0x18;
pub(crate) const SCENE_ALL_STAR_REST_AREA_10: u8 = 0x19;
pub(crate) const SCENE_ALL_STAR_LEVEL_11: u8 = 0x20;
pub(crate) const SCENE_ALL_STAR_REST_AREA_11: u8 = 0x21;
pub(crate) const SCENE_ALL_STAR_LEVEL_12: u8 = 0x22;
pub(crate) const SCENE_ALL_STAR_REST_AREA_12: u8 = 0x23;
pub(crate) const SCENE_ALL_STAR_LEVEL_13: u8 = 0x24;
pub(crate) const SCENE_ALL_STAR_REST_AREA_13: u8 = 0x25;
pub(crate) const SCENE_ALL_STAR_LEVEL_14: u8 = 0x26;
pub(crate) const SCENE_ALL_STAR_REST_AREA_14: u8 = 0x27;
pub(crate) const SCENE_ALL_STAR_LEVEL_15: u8 = 0x28;
pub(crate) const SCENE_ALL_STAR_REST_AREA_15: u8 = 0x29;
pub(crate) const SCENE_ALL_STAR_LEVEL_16: u8 = 0x30;
pub(crate) const SCENE_ALL_STAR_REST_AREA_16: u8 = 0x31;
pub(crate) const SCENE_ALL_STAR_LEVEL_17: u8 = 0x32;
pub(crate) const SCENE_ALL_STAR_REST_AREA_17: u8 = 0x33;
pub(crate) const SCENE_ALL_STAR_LEVEL_18: u8 = 0x34;
pub(crate) const SCENE_ALL_STAR_REST_AREA_18: u8 = 0x35;
pub(crate) const SCENE_ALL_STAR_LEVEL_19: u8 = 0x36;
pub(crate) const SCENE_ALL_STAR_REST_AREA_19: u8 = 0x37;
pub(crate) const SCENE_ALL_STAR_LEVEL_20: u8 = 0x38;
pub(crate) const SCENE_ALL_STAR_REST_AREA_20: u8 = 0x39;
pub(crate) const SCENE_ALL_STAR_LEVEL_21: u8 = 0x40;
pub(crate) const SCENE_ALL_STAR_REST_AREA_21: u8 = 0x41;
pub(crate) const SCENE_ALL_STAR_LEVEL_22: u8 = 0x42;
pub(crate) const SCENE_ALL_STAR_REST_AREA_22: u8 = 0x43;
pub(crate) const SCENE_ALL_STAR_LEVEL_23: u8 = 0x44;
pub(crate) const SCENE_ALL_STAR_REST_AREA_23: u8 = 0x45;
pub(crate) const SCENE_ALL_STAR_LEVEL_24: u8 = 0x46;
pub(crate) const SCENE_ALL_STAR_REST_AREA_24: u8 = 0x47;
pub(crate) const SCENE_ALL_STAR_LEVEL_25: u8 = 0x48;
pub(crate) const SCENE_ALL_STAR_REST_AREA_25: u8 = 0x49;
pub(crate) const SCENE_ALL_STAR_LEVEL_26: u8 = 0x50;
pub(crate) const SCENE_ALL_STAR_REST_AREA_26: u8 = 0x51;
pub(crate) const SCENE_ALL_STAR_LEVEL_27: u8 = 0x52;
pub(crate) const SCENE_ALL_STAR_REST_AREA_28: u8 = 0x53;
pub(crate) const SCENE_ALL_STAR_LEVEL_29: u8 = 0x54;
pub(crate) const SCENE_ALL_STAR_REST_AREA_29: u8 = 0x55;
pub(crate) const SCENE_ALL_STAR_LEVEL_30: u8 = 0x56;
pub(crate) const SCENE_ALL_STAR_REST_AREA_30: u8 = 0x57;
pub(crate) const SCENE_ALL_STAR_LEVEL_31: u8 = 0x58;
pub(crate) const SCENE_ALL_STAR_REST_AREA_31: u8 = 0x59;
pub(crate) const SCENE_ALL_STAR_LEVEL_32: u8 = 0x60;
pub(crate) const SCENE_ALL_STAR_REST_AREA_32: u8 = 0x61;
pub(crate) const SCENE_ALL_STAR_CSS: u8 = 0x70;
pub(crate) const SCENE_DEBUG: u8 = 0x06;
pub(crate) const SCENE_SOUND_TEST: u8 = 0x07;
pub(crate) const SCENE_VS_ONLINE: u8 = 0x08; // SLIPPI ONLINE
pub(crate) const SCENE_VS_ONLINE_CSS: u8 = 0x00;
pub(crate) const SCENE_VS_ONLINE_SSS: u8 = 0x01;
pub(crate) const SCENE_VS_ONLINE_INGAME: u8 = 0x02;
pub(crate) const SCENE_VS_ONLINE_VERSUS: u8 = 0x04;
pub(crate) const SCENE_VS_ONLINE_RANKED: u8 = 0x05;
pub(crate) const SCENE_UNKOWN_1: u8 = 0x09;
pub(crate) const SCENE_CAMERA_MODE: u8 = 0x0A;
pub(crate) const SCENE_TROPHY_GALLERY: u8 = 0x0B;
pub(crate) const SCENE_TROPHY_LOTTERY: u8 = 0x0C;
pub(crate) const SCENE_TROPHY_COLLECTION: u8 = 0x0D;
pub(crate) const SCENE_START_MATCH: u8 = 0x0E; // Slippi Replays
pub(crate) const SCENE_START_MATCH_INGAME: u8 = 0x01; // Set when the replay is actually playing out
pub(crate) const SCENE_START_MATCH_UNKNOWN: u8 = 0x03; // Seems to be set right before the match loads
pub(crate) const SCENE_TARGET_TEST: u8 = 0x0F;
pub(crate) const SCENE_TARGET_TEST_CSS: u8 = 0x00;
pub(crate) const SCENE_TARGET_TEST_INGAME: u8 = 0x1;
pub(crate) const SCENE_SUPER_SUDDEN_DEATH: u8 = 0x10;
pub(crate) const SCENE_SSD_CSS: u8 = 0x00;
pub(crate) const SCENE_SSD_SSS: u8 = 0x01;
pub(crate) const SCENE_SSD_INGAME: u8 = 0x02;
pub(crate) const SCENE_SSD_POSTGAME: u8 = 0x04;
pub(crate) const MENU_INVISIBLE_MELEE: u8 = 0x11;
pub(crate) const MENU_INVISIBLE_MELEE_CSS: u8 = 0x00;
pub(crate) const MENU_INVISIBLE_MELEE_SSS: u8 = 0x01;
pub(crate) const MENU_INVISIBLE_MELEE_INGAME: u8 = 0x02;
pub(crate) const MENU_INVISIBLE_MELEE_POSTGAME: u8 = 0x04;
pub(crate) const MENU_SLOW_MO_MELEE: u8 = 0x12;
pub(crate) const MENU_SLOW_MO_MELEE_CSS: u8 = 0x00;
pub(crate) const MENU_SLOW_MO_MELEE_SSS: u8 = 0x01;
pub(crate) const MENU_SLOW_MO_MELEE_INGAME: u8 = 0x02;
pub(crate) const MENU_SLOW_MO_MELEE_POSTGAME: u8 = 0x04;
pub(crate) const MENU_LIGHTNING_MELEE: u8 = 0x13;
pub(crate) const MENU_LIGHTNING_MELEE_CSS: u8 = 0x00;
pub(crate) const MENU_LIGHTNING_MELEE_SSS: u8 = 0x01;
pub(crate) const MENU_LIGHTNING_MELEE_INGAME: u8 = 0x02;
pub(crate) const MENU_LIGHTNING_MELEE_POSTGAME: u8 = 0x04;
pub(crate) const SCENE_CHARACTER_APPROACHING: u8 = 0x14;
pub(crate) const SCENE_CLASSIC_MODE_COMPLETE: u8 = 0x15;
pub(crate) const SCENE_CLASSIC_MODE_TROPHY: u8 = 0x00;
pub(crate) const SCENE_CLASSIC_MODE_CREDITS: u8 = 0x01;
pub(crate) const SCENE_CLASSIC_MODE_CHARACTER_VIDEO: u8 = 0x02;
pub(crate) const SCENE_CLASSIC_MODE_CONGRATS: u8 = 0x03;
pub(crate) const SCENE_ADVENTURE_MODE_COMPLETE: u8 = 0x16;
pub(crate) const SCENE_ADVENTURE_MODE_TROPHY: u8 = 0x00;
pub(crate) const SCENE_ADVENTURE_MODE_CREDITS: u8 = 0x01;
pub(crate) const SCENE_ADVENTURE_MODE_CHARACTER_VIDEO: u8 = 0x02;
pub(crate) const SCENE_ADVENTURE_MODE_CONGRATS: u8 = 0x03;
pub(crate) const SCENE_ALL_STAR_COMPLETE: u8 = 0x17;
pub(crate) const SCENE_ALL_STAR_TROPHY: u8 = 0x00;
pub(crate) const SCENE_ALL_STAR_CREDITS: u8 = 0x01;
pub(crate) const SCENE_ALL_STAR_CHARACTER_VIDEO: u8 = 0x02;
pub(crate) const SCENE_ALL_STAR_CONGRATS: u8 = 0x03;
pub(crate) const SCENE_TITLE_SCREEN_IDLE: u8 = 0x18;
pub(crate) const SCENE_TITLE_SCREEN_IDLE_INTRO_VIDEO: u8 = 0x0;
pub(crate) const SCENE_TITLE_SCREEN_IDLE_FIGHT_1: u8 = 0x1;
pub(crate) const SCENE_TITLE_SCREEN_IDLE_BETWEEN_FIGHTS: u8 = 0x2;
pub(crate) const SCENE_TITLE_SCREEN_IDLE_FIGHT_2: u8 = 0x3;
pub(crate) const SCENE_TITLE_SCREEN_IDLE_HOW_TO_PLAY: u8 = 0x4;
pub(crate) const SCENE_ADVENTURE_MODE_CINEMEATIC: u8 = 0x19;
pub(crate) const SCENE_CHARACTER_UNLOCKED: u8 = 0x1A;
pub(crate) const SCENE_TOURNAMENT: u8 = 0x1B;
pub(crate) const SCENE_TOURNAMENT_CSS: u8 = 0x0;
pub(crate) const SCENE_TOURNAMENT_BRACKET: u8 = 0x1;
pub(crate) const SCENE_TOURNAMENT_INGAME: u8 = 0x4;
pub(crate) const SCENE_TOURNAMENT_POSTGAME: u8 = 0x6;
pub(crate) const SCENE_TRAINING_MODE: u8 = 0x1C;
pub(crate) const SCENE_TRAINING_CSS: u8 = 0x0;
pub(crate) const SCENE_TRAINING_SSS: u8 = 0x1;
pub(crate) const SCENE_TRAINING_INGAME: u8 = 0x2;
pub(crate) const SCENE_TINY_MELEE: u8 = 0x1D;
pub(crate) const SCENE_TINY_MELEE_CSS: u8 = 0x0;
pub(crate) const SCENE_TINY_MELEE_SSS: u8 = 0x1;
pub(crate) const SCENE_TINY_MELEE_INGAME: u8 = 0x2;
pub(crate) const SCENE_TINY_MELEE_POSTGAME: u8 = 0x4;
pub(crate) const SCENE_GIANT_MELEE: u8 = 0x1E;
pub(crate) const SCENE_GIANT_MELEE_CSS: u8 = 0x0;
pub(crate) const SCENE_GIANT_MELEE_SSS: u8 = 0x1;
pub(crate) const SCENE_GIANT_MELEE_INGAME: u8 = 0x2;
pub(crate) const SCENE_GIANT_MELEE_POSTGAME: u8 = 0x4;
pub(crate) const SCENE_STAMINA_MODE: u8 = 0x1F;
pub(crate) const SCENE_STAMINA_MODE_CSS: u8 = 0x0;
pub(crate) const SCENE_STAMINA_MODE_SSS: u8 = 0x1;
pub(crate) const SCENE_STAMINA_MODE_INGAME: u8 = 0x2;
pub(crate) const SCENE_STAMINA_MODE_POSTGAME: u8 = 0x4;
pub(crate) const SCENE_HOME_RUN_CONTEST: u8 = 0x20;
pub(crate) const SCENE_HOME_RUN_CONTEST_CSS: u8 = 0x0;
pub(crate) const SCENE_HOME_RUN_CONTEST_INGAME: u8 = 0x1;
pub(crate) const SCENE_10_MAN_MELEE: u8 = 0x21;
pub(crate) const SCENE_10_MAN_MELEE_CSS: u8 = 0x00;
pub(crate) const SCENE_10_MAN_MELEE_INGAME: u8 = 0x01;
pub(crate) const SCENE_100_MAN_MELEE: u8 = 0x22;
pub(crate) const SCENE_100_MAN_MELEE_CSS: u8 = 0x00;
pub(crate) const SCENE_100_MAN_MELEE_INGAME: u8 = 0x01;
pub(crate) const SCENE_3_MINUTE_MELEE: u8 = 0x23;
pub(crate) const SCENE_3_MINUTE_MELEE_CSS: u8 = 0x00;
pub(crate) const SCENE_3_MINUTE_MELEE_INGAME: u8 = 0x01;
pub(crate) const SCENE_15_MINUTE_MELEE: u8 = 0x24;
pub(crate) const SCENE_15_MINUTE_MELEE_CSS: u8 = 0x00;
pub(crate) const SCENE_15_MINUTE_MELEE_INGAME: u8 = 0x01;
pub(crate) const SCENE_ENDLESS_MELEE: u8 = 0x25;
pub(crate) const SCENE_ENDLESS_MELEE_CSS: u8 = 0x00;
pub(crate) const SCENE_ENDLESS_MELEE_INGAME: u8 = 0x01;
pub(crate) const SCENE_CRUEL_MELEE: u8 = 0x26;
pub(crate) const SCENE_CRUEL_MELEE_CSS: u8 = 0x00;
pub(crate) const SCENE_CRUEL_MELEE_INGAME: u8 = 0x01;
pub(crate) const SCENE_PROGRESSIVE_SCAN: u8 = 0x27;
pub(crate) const SCENE_PLAY_INTRO_VIDEO: u8 = 0x28;
pub(crate) const SCENE_MEMORY_CARD_OVERWRITE: u8 = 0x29;
pub(crate) const SCENE_FIXED_CAMERA_MODE: u8 = 0x2A;
pub(crate) const SCENE_FIXED_CAMERA_MODE_CSS: u8 = 0x0;
pub(crate) const SCENE_FIXED_CAMERA_MODE_SSS: u8 = 0x1;
pub(crate) const SCENE_FIXED_CAMERA_MODE_INGAME: u8 = 0x2;
pub(crate) const SCENE_FIXED_CAMERA_MODE_POSTGAME: u8 = 0x4;
pub(crate) const SCENE_EVENT_MATCH: u8 = 0x2B;
pub(crate) const SCENE_EVENT_MATCH_SELECT: u8 = 0x0;
pub(crate) const SCENE_EVENT_MATCH_INGAME: u8 = 0x1;
pub(crate) const SCENE_SINGLE_BUTTON_MODE: u8 = 0x2C;
pub(crate) const SCENE_SINGLE_BUTTON_MODE_CSS: u8 = 0x0;
pub(crate) const SCENE_SINGLE_BUTTON_MODE_SSS: u8 = 0x1;
pub(crate) const SCENE_SINGLE_BUTTON_MODE_INGAME: u8 = 0x2;
}

View File

@@ -0,0 +1,171 @@
/// IDs for all the songs that Slippi Jukebox can play. Any track that
/// exists in vanilla can be added
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) enum TrackId {
Menu1,
Menu2,
Lottery,
TournamentMode1,
TournamentMode2,
VsOpponent,
PeachsCastle,
RainbowCruise,
KongoJungle,
JungleJapes,
GreatBay,
Saria,
Temple,
FireEmblem,
Brinstar,
BrinstarDepths,
YoshisStory,
YoshisIsland,
SuperMario3,
FountainOfDreams,
GreenGreens,
Corneria,
Venom,
PokemonStadium,
PokemonBattle,
Pokefloats,
MuteCity,
BigBlue,
MachRider,
Onett,
Mother2,
Fourside,
IcicleMountain,
BalloonFighter,
MushroomKingdom,
DrMario,
MushroomKingdomII,
FlatZone,
DreamLand64,
YoshisIsland64,
KongoJungle64,
Battlefield,
MultimanMelee,
FinalDestination,
MultimanMelee2,
BreakTheTargets,
BrinstarEscape,
AllStarRestArea,
}
/// Given the filename of a melee music track, return the associated track ID
pub(crate) fn get_track_id_by_filename(track_filename: &str) -> Option<TrackId> {
use self::TrackId::*;
match track_filename {
"menu01.hps" => Some(Menu1),
"menu3.hps" => Some(Menu2),
"menu02.hps" => Some(Lottery),
"vs_hyou1.hps" => Some(TournamentMode1),
"vs_hyou2.hps" => Some(TournamentMode2),
"intro_es.hps" => Some(VsOpponent),
"castle.hps" => Some(PeachsCastle),
"rcruise.hps" => Some(RainbowCruise),
"garden.hps" => Some(KongoJungle),
"kongo.hps" => Some(JungleJapes),
"greatbay.hps" => Some(GreatBay),
"saria.hps" => Some(Saria),
"shrine.hps" => Some(Temple),
"akaneia.hps" => Some(FireEmblem),
"zebes.hps" => Some(Brinstar),
"kraid.hps" => Some(BrinstarDepths),
"ystory.hps" => Some(YoshisStory),
"yorster.hps" => Some(YoshisIsland),
"smari3.hps" => Some(SuperMario3),
"izumi.hps" => Some(FountainOfDreams),
"greens.hps" => Some(GreenGreens),
"corneria.hps" => Some(Corneria),
"venom.hps" => Some(Venom),
"pstadium.hps" => Some(PokemonStadium),
"pokesta.hps" => Some(PokemonBattle),
"pura.hps" => Some(Pokefloats),
"mutecity.hps" => Some(MuteCity),
"bigblue.hps" => Some(BigBlue),
"mrider.hps" => Some(MachRider),
"onetto.hps" => Some(Onett),
"onetto2.hps" => Some(Mother2),
"fourside.hps" => Some(Fourside),
"icemt.hps" => Some(IcicleMountain),
"baloon.hps" => Some(BalloonFighter),
"inis1_01.hps" => Some(MushroomKingdom),
"docmari.hps" => Some(DrMario),
"inis2_01.hps" => Some(MushroomKingdomII),
"flatzone.hps" => Some(FlatZone),
"old_kb.hps" => Some(DreamLand64),
"old_ys.hps" => Some(YoshisIsland64),
"old_dk.hps" => Some(KongoJungle64),
"sp_zako.hps" => Some(Battlefield),
"hyaku.hps" => Some(MultimanMelee),
"sp_end.hps" => Some(FinalDestination),
"hyaku2.hps" => Some(MultimanMelee2),
"target.hps" => Some(BreakTheTargets),
"siren.hps" => Some(BrinstarEscape),
"1p_qk.hps" => Some(AllStarRestArea),
_ => None,
}
}
/// Given a stage ID, retrieve the ID of the track that should play
pub(crate) fn get_stage_track_id(stage_id: u8) -> Option<TrackId> {
use self::TrackId::*;
// Stage IDs and their associated tracks
let track_ids: Option<(TrackId, Option<TrackId>)> = match stage_id {
0x02 | 0x1F => Some((PeachsCastle, None)),
0x03 => Some((RainbowCruise, None)),
0x04 => Some((KongoJungle, None)),
0x05 => Some((JungleJapes, None)),
0x06 => Some((GreatBay, Some(Saria))),
0x07 => Some((Temple, Some(FireEmblem))),
0x08 => Some((Brinstar, None)),
0x09 => Some((BrinstarDepths, None)),
0x0A => Some((YoshisStory, None)),
0x0B => Some((YoshisIsland, Some(SuperMario3))),
0x0C => Some((FountainOfDreams, None)),
0x0D => Some((GreenGreens, None)),
0x0E => Some((Corneria, None)),
0x0F => Some((Venom, None)),
0x10 => Some((PokemonStadium, Some(PokemonBattle))),
0x11 => Some((Pokefloats, None)),
0x12 => Some((MuteCity, None)),
0x13 => Some((BigBlue, Some(MachRider))),
0x14 => Some((Onett, Some(Mother2))),
0x15 => Some((Fourside, None)),
0x16 => Some((IcicleMountain, Some(BalloonFighter))),
0x18 => Some((MushroomKingdom, Some(DrMario))),
0x19 => Some((MushroomKingdomII, Some(DrMario))),
0x1B => Some((FlatZone, None)),
0x1C => Some((DreamLand64, None)),
0x1D => Some((YoshisIsland64, None)),
0x1E => Some((KongoJungle64, None)),
0x24 => Some((Battlefield, Some(MultimanMelee))),
0x25 => Some((FinalDestination, Some(MultimanMelee2))),
// Snag trophies
0x26 => Some((Lottery, None)),
// Race to the Finish
0x27 => Some((Battlefield, None)),
// Adventure Mode Field Stages
0x20 => Some((Temple, None)),
0x21 => Some((BrinstarEscape, None)),
0x22 => Some((BigBlue, None)),
// All-Star Rest Area
0x42 => Some((AllStarRestArea, None)),
// Break the Targets + Home Run Contest
0x2C | 0x28 | 0x43 | 0x33 | 0x31 | 0x37 | 0x3D | 0x2B | 0x29 | 0x41 | 0x2D | 0x2E | 0x36 | 0x2F | 0x30 | 0x3B | 0x3E
| 0x32 | 0x2A | 0x38 | 0x39 | 0x3A | 0x35 | 0x3F | 0x34 | 0x40 => Some((BreakTheTargets, None)),
_ => None,
};
// If the stage has an alternate track associated, there's a 12.5% chance it will be selected
match track_ids {
Some(track_ids) => match track_ids {
(_, Some(id)) if fastrand::u8(0..8) == 0 => Some(id),
(id, _) => Some(id),
},
None => None,
}
}

View File

@@ -0,0 +1,192 @@
use crate::scenes::scene_ids::*;
use crate::tracks::{get_track_id_by_filename, TrackId};
use crate::Result;
use anyhow::anyhow;
use std::collections::HashMap;
use std::ffi::CStr;
use std::io::{Read, Seek};
/// Get an unsigned 24 bit integer from a byte slice
fn read_u24(bytes: &[u8], offset: usize) -> u32 {
let size = 3;
let end = offset + size;
let mut padded_bytes = [0; 4];
padded_bytes[1..4].copy_from_slice(&bytes[offset..end]);
u32::from_be_bytes(padded_bytes)
}
/// Get an unsigned 32 bit integer from a byte slice
fn read_u32(bytes: &[u8], offset: usize) -> u32 {
let size = (u32::BITS / 8) as usize;
let end: usize = offset + size;
u32::from_be_bytes(
bytes[offset..end]
.try_into()
.unwrap_or_else(|_| unreachable!("u32::BITS / 8 is always 4")),
)
}
/// Get a copy of the `size` bytes in `file` at `offset`
pub(crate) fn read_from_file(file: &mut std::fs::File, offset: u64, size: usize) -> Result<Vec<u8>> {
file.seek(std::io::SeekFrom::Start(offset))?;
let mut bytes = vec![0; size];
file.read_exact(&mut bytes)?;
Ok(bytes)
}
/// Produces a hashmap containing offsets and sizes of .hps files contained within the iso
/// These can be looked up by TrackId
pub(crate) fn create_track_map(iso: &mut std::fs::File) -> Result<HashMap<TrackId, (u32, u32)>> {
const FST_LOCATION_OFFSET: u64 = 0x424;
const FST_SIZE_OFFSET: u64 = 0x0428;
const FST_ENTRY_SIZE: usize = 0xC;
// Filesystem Table (FST)
let fst_location = u32::from_be_bytes(
read_from_file(iso, FST_LOCATION_OFFSET, 0x4)?
.try_into()
.map_err(|_| anyhow!("Unable to read FST offset as u32"))?,
);
let fst_size = u32::from_be_bytes(
read_from_file(iso, FST_SIZE_OFFSET, 0x4)?
.try_into()
.map_err(|_| anyhow!("Unable to read FST size as u32"))?,
);
let fst = read_from_file(iso, fst_location as u64, fst_size as usize)?;
// FST String Table
let str_table_offset = read_u32(&fst, 0x8) as usize * FST_ENTRY_SIZE;
// Collect the .hps file metadata in the FST into a hash map
Ok(fst[..str_table_offset]
.chunks(FST_ENTRY_SIZE)
.filter_map(|entry| {
let is_file = entry[0] == 0;
let name_offset = str_table_offset + read_u24(entry, 0x1) as usize;
let offset = read_u32(entry, 0x4);
let size = read_u32(entry, 0x8);
let name = CStr::from_bytes_until_nul(&fst[name_offset..]).ok()?.to_str().ok()?;
if is_file && name.ends_with(".hps") {
match get_track_id_by_filename(&name) {
Some(track_id) => Some((track_id, (offset, size))),
None => None,
}
} else {
None
}
})
.collect())
}
/// Returns a tuple containing a randomly selected menu track tournament track
/// to play
pub(crate) fn get_random_menu_tracks() -> (TrackId, TrackId) {
// 25% chance to use the alternate menu theme
let menu_track = if fastrand::u8(0..4) == 0 {
TrackId::Menu2
} else {
TrackId::Menu1
};
// 50% chance to use the alternate tournament mode theme
let tournament_track = if fastrand::u8(0..2) == 0 {
TrackId::TournamentMode1
} else {
TrackId::TournamentMode2
};
(menu_track, tournament_track)
}
/// Returns true if the user is in an actual match
/// Sourced from M'Overlay: https://github.com/bkacjios/m-overlay/blob/d8c629d/source/melee.lua#L1177
pub(crate) fn is_in_game(scene_major: u8, scene_minor: u8) -> bool {
if scene_major == SCENE_ALL_STAR_MODE && scene_minor < SCENE_ALL_STAR_CSS {
return true;
}
if scene_major == SCENE_VS_MODE || scene_major == SCENE_VS_ONLINE {
return scene_minor == SCENE_VS_INGAME;
}
if (SCENE_TRAINING_MODE..=SCENE_STAMINA_MODE).contains(&scene_major) || scene_major == SCENE_FIXED_CAMERA_MODE {
return scene_minor == SCENE_TRAINING_INGAME;
}
if scene_major == SCENE_EVENT_MATCH {
return scene_minor == SCENE_EVENT_MATCH_INGAME;
}
if scene_major == SCENE_CLASSIC_MODE && scene_minor < SCENE_CLASSIC_CONTINUE {
return scene_minor % 2 == 1;
}
if scene_major == SCENE_ADVENTURE_MODE {
return scene_minor == SCENE_ADVENTURE_MUSHROOM_KINGDOM
|| scene_minor == SCENE_ADVENTURE_MUSHROOM_KINGDOM_BATTLE
|| scene_minor == SCENE_ADVENTURE_MUSHROOM_KONGO_JUNGLE_TINY_BATTLE
|| scene_minor == SCENE_ADVENTURE_MUSHROOM_KONGO_JUNGLE_GIANT_BATTLE
|| scene_minor == SCENE_ADVENTURE_UNDERGROUND_MAZE
|| scene_minor == SCENE_ADVENTURE_HYRULE_TEMPLE_BATTLE
|| scene_minor == SCENE_ADVENTURE_BRINSTAR
|| scene_minor == SCENE_ADVENTURE_ESCAPE_ZEBES
|| scene_minor == SCENE_ADVENTURE_GREEN_GREENS_KIRBY_BATTLE
|| scene_minor == SCENE_ADVENTURE_GREEN_GREENS_KIRBY_TEAM_BATTLE
|| scene_minor == SCENE_ADVENTURE_GREEN_GREENS_GIANT_KIRBY_BATTLE
|| scene_minor == SCENE_ADVENTURE_CORNERIA_BATTLE_1
|| scene_minor == SCENE_ADVENTURE_CORNERIA_BATTLE_2
|| scene_minor == SCENE_ADVENTURE_CORNERIA_BATTLE_3
|| scene_minor == SCENE_ADVENTURE_POKEMON_STADIUM_BATTLE
|| scene_minor == SCENE_ADVENTURE_FZERO_GRAND_PRIX_RACE
|| scene_minor == SCENE_ADVENTURE_FZERO_GRAND_PRIX_BATTLE
|| scene_minor == SCENE_ADVENTURE_ONETT_BATTLE
|| scene_minor == SCENE_ADVENTURE_ICICLE_MOUNTAIN_CLIMB
|| scene_minor == SCENE_ADVENTURE_BATTLEFIELD_BATTLE
|| scene_minor == SCENE_ADVENTURE_BATTLEFIELD_METAL_BATTLE
|| scene_minor == SCENE_ADVENTURE_FINAL_DESTINATION_BATTLE;
}
if scene_major == SCENE_TARGET_TEST {
return scene_minor == SCENE_TARGET_TEST_INGAME;
}
if (SCENE_SUPER_SUDDEN_DEATH..=MENU_LIGHTNING_MELEE).contains(&scene_major) {
return scene_minor == SCENE_SSD_INGAME;
}
if (SCENE_HOME_RUN_CONTEST..=SCENE_CRUEL_MELEE).contains(&scene_major) {
return scene_minor == SCENE_HOME_RUN_CONTEST_INGAME;
}
if scene_major == SCENE_TITLE_SCREEN_IDLE {
return scene_minor == SCENE_TITLE_SCREEN_IDLE_FIGHT_1 || scene_minor == SCENE_TITLE_SCREEN_IDLE_FIGHT_2;
}
false
}
/// Returns true if the player navigating the menus (including CSS and SSS)
/// Sourced from M'Overlay: https://github.com/bkacjios/m-overlay/blob/d8c629d/source/melee.lua#L1243
pub(crate) fn is_in_menus(scene_major: u8, scene_minor: u8) -> bool {
if scene_major == SCENE_MAIN_MENU {
return true;
}
if scene_major == SCENE_VS_MODE {
return scene_minor == SCENE_VS_CSS || scene_minor == SCENE_VS_SSS;
}
if scene_major == SCENE_VS_ONLINE {
return scene_minor == SCENE_VS_ONLINE_CSS || scene_minor == SCENE_VS_ONLINE_SSS || scene_minor == SCENE_VS_ONLINE_RANKED;
}
if (SCENE_TRAINING_MODE..=SCENE_STAMINA_MODE).contains(&scene_major) || scene_major == SCENE_FIXED_CAMERA_MODE {
return scene_minor == SCENE_TRAINING_CSS || scene_minor == SCENE_TRAINING_SSS;
}
if scene_major == SCENE_EVENT_MATCH {
return scene_minor == SCENE_EVENT_MATCH_SELECT;
}
if scene_major == SCENE_CLASSIC_MODE || scene_major == SCENE_ADVENTURE_MODE || scene_major == SCENE_ALL_STAR_MODE {
return scene_minor == SCENE_CLASSIC_CSS;
}
if scene_major == SCENE_TARGET_TEST {
return scene_minor == SCENE_TARGET_TEST_CSS;
}
if (SCENE_SUPER_SUDDEN_DEATH..=MENU_LIGHTNING_MELEE).contains(&scene_major) {
return scene_minor == SCENE_SSD_CSS || scene_minor == SCENE_SSD_SSS;
}
if (SCENE_HOME_RUN_CONTEST..=SCENE_CRUEL_MELEE).contains(&scene_major) {
return scene_minor == SCENE_HOME_RUN_CONTEST_CSS;
}
false
}

View File

@@ -0,0 +1,18 @@
[package]
name = "dolphin-logger"
description = "A shim for Dolphin's LogContainer with a custom tracing subscriber layer."
version = "0.1.0"
authors = [
"Slippi Team",
"Ryan McGrath <ryan@rymc.io>"
]
edition = "2021"
publish = false
[dependencies]
time = { workspace = true }
# We disable the "attributes" feature as we don't currently need it and it brings
# in extra dependencies.
tracing = { workspace = true }
tracing-subscriber = "0.3"

View File

@@ -0,0 +1,51 @@
# Dolphin-Logger
This crate implements a custom [tracing-subscriber](https://crates.io/crates/tracing-subscriber) that handles shuttling logs through the Dolphin application logging infrastructure. This is important not just for keeping logs contained all in one place, but for aiding in debugging on Windows where console logging is... odd.
## How it works
The first thing to understand is that the Slippi Dolphin `LogManager` module has some tweaks to support creating a "Rust-sourced" `LogContainer`.
When the `LogManager` is created, we initialize the Rust logging framework by calling `slprs_logging_init`, passing a function to dispatch logs through from the Rust side.
The `LogManager` has several `LogContainer` instances, generally corresponding to a particular grouping of log types. If a `LogContainer` is flagged as sourcing from the Rust library, then on instantiation it will register itself with the Rust logger by calling `slprs_logging_register_container`. This registration call caches the `LogType` and enabled status of the log so that the Rust side can transparently keep track of things and not have to concern itself with any changing enum variant types.
When a Rust-sourced `LogContainer` is updated - say, a status change to `disabled` - it will forward this change to the Rust side via `slprs_logging_update_container`. If a log container is disabled in Rust, then logs are dropped accordingly with no allocations made anywhere (i.e, nothing should be impacting gameplay).
## Adding a new LogContainer
If you need to add a new log to the Dolphin codebase, you will need to add a few lines to the log definitions on the C++ side. This enables using the Dolphin logs viewer to monitor Rust `tracing` events.
First, head to [`Source/Core/Common/Logging/Log.h`](../../../Source/Core/Common/Logging/Log.h) and define a new `LogTypes::LOG_TYPE` variant.
Next, head to [`Source/Core/Common/Logging/LogManager.cpp`](../../../Source/Core/Common/Logging/LogManager.cpp) and add a new `LogContainer` in `LogManager::LogManager()`. For example, let's say that we created `LogTypes::SLIPPI_RUST_EXI` - we'd now add:
``` c++
// This LogContainer will register with the Rust side under the "SLIPPI_RUST_EXI" target.
m_Log[LogTypes::SLIPPI_RUST_EXI] = new LogContainer(
"SLIPPI_RUST_EXI", // Internal identifier, Rust will need to match
"Slippi EXI (Rust)", // User-visible log label
LogTypes::SLIPPI_RUST_EXI, // The C++ LogTypes variant we created
true // Instructs the initializer that this is a Rust-sourced log
);
```
Finally, add an associated `const` declaration with your `LogContainer` internal identifier in `src/lib.rs`:
``` rust
pub mod Log {
// ...other logs etc
// Our new logger name
pub const EXI: &'static str = "SLIPPI_RUST_EXI";
}
```
Now your Rust module can specify that it should log to this container via the tracing module:
``` rust
use dolphin_logger::Log;
fn do_stuff() {
tracing::info!(target: Log::EXI, "Hello from the Rust side");
}
```
Make sure to _enable_ your log via Dolphin's Log Manager view to see these logs!

View File

@@ -0,0 +1,251 @@
//! This module implements a custom `tracing_subscriber::Layer` that facilitates
//! routing logs through the Dolphin log viewer.
use std::ffi::CString;
use std::fmt::Write;
use std::os::raw::{c_char, c_int};
use time::OffsetDateTime;
use tracing::{Level, Metadata};
use tracing_subscriber::Layer;
use super::{ForeignLoggerFn, Log, LOG_CONTAINERS};
/// Corresponds to Dolphin's `LogTypes::LOG_LEVELS::LNOTICE` value.
#[allow(dead_code)]
const LOG_LEVEL_NOTICE: c_int = 1;
/// Corresponds to Dolphin's `LogTypes::LOG_LEVELS::LERROR` value.
const LOG_LEVEL_ERROR: c_int = 2;
/// Corresponds to Dolphin's `LogTypes::LOG_LEVELS::LWARNING` value.
const LOG_LEVEL_WARNING: c_int = 3;
/// Corresponds to Dolphin's `LogTypes::LOG_LEVELS::LINFO` value.
const LOG_LEVEL_INFO: c_int = 4;
/// Corresponds to Dolphin's `LogTypes::LOG_LEVELS::LDEBUG` value.
const LOG_LEVEL_DEBUG: c_int = 5;
/// A helper method for converting Dolphin's levels to a tracing::Level.
///
/// Currently there's a bit of a mismatch, as `NOTICE` from Dolphin isn't
/// really covered here...
pub fn convert_dolphin_log_level_to_tracing_level(level: c_int) -> Level {
match level {
LOG_LEVEL_ERROR => Level::ERROR,
LOG_LEVEL_WARNING => Level::WARN,
LOG_LEVEL_INFO => Level::INFO,
_ => Level::DEBUG,
}
}
/// A custom tracing layer that forwards events back into the Dolphin logging infrastructure.
///
/// This implements `tracing_subscriber::Layer` and is the default way to log in this library.
#[derive(Debug)]
pub struct DolphinLoggerLayer {
logger_fn: ForeignLoggerFn,
}
impl DolphinLoggerLayer {
/// Creates and returns a new logger layer.
pub fn new(logger_fn: ForeignLoggerFn) -> Self {
Self { logger_fn }
}
}
impl<S> Layer<S> for DolphinLoggerLayer
where
S: tracing::Subscriber,
{
/// Unpacks a tracing event and routes it to the appropriate Dolphin log handler.
///
/// At the moment, this is somewhat "dumb" and may allocate more than we want to. Consider
/// it a hook for safely improving performance later. ;P
fn on_event(&self, event: &tracing::Event<'_>, _ctx: tracing_subscriber::layer::Context<'_, S>) {
let metadata = event.metadata();
let target = metadata.target();
let log_containers = LOG_CONTAINERS
.get()
.expect("[DolphinLoggerLayer::on_event]: Unable to acquire `LOG_CONTAINERS`?");
let reader = log_containers
.read()
.expect("[DolphinLoggerLayer::on_event]: Unable to acquire readlock on `LOG_CONTAINERS`?");
let mut log_container = reader.iter().find(|container| container.kind == target);
// If we can't find the requested container, then go ahead and see if we can dump it
// into the dependencies container. This also allows us to grab any logs that *aren't*
// tagged that may be of interest (i.e, from dependencies - hence the container name).
if log_container.is_none() {
log_container = reader.iter().find(|container| container.kind == Log::DEPENDENCIES);
}
if log_container.is_none() {
// We want to still dump errors to the console if no log handler is set at all,
// otherwise debugging is a nightmare (i.e, we want to surface event flow if a
// logger initialization is mis-called somewhere).
eprintln!("No logger handler found for target: {}", target);
return;
}
let container = log_container.unwrap();
let level = *metadata.level();
// In tracing, ERROR is the *lowest* - so we essentially just want to make sure that we're
// at a level *above* the container level before allocating a log message.
if !container.is_enabled {
// @TODO: We want to avoid any allocations if the log level
// is not appropriate, but there's a mismatch between how Dolphin prioritizes log
// levels and how tracing does it.
//
// || container.level > level {
return;
}
let log_level = match level {
Level::INFO => LOG_LEVEL_INFO,
Level::WARN => LOG_LEVEL_WARNING,
Level::ERROR => LOG_LEVEL_ERROR,
Level::DEBUG | Level::TRACE => LOG_LEVEL_DEBUG,
};
let mut visitor = DolphinLoggerVisitor::new(metadata, target);
event.record(&mut visitor);
match CString::new(visitor.finish()) {
Ok(c_str_msg) => {
// A note on ownership: the Dolphin logger cannot modify the contents of
// the passed over c_str, nor should it attempt to free anything. Rust owns
// this and will handle any cleanup after the logger_fn has dispatched.
unsafe {
(self.logger_fn)(log_level, container.log_type, c_str_msg.as_ptr() as *const c_char);
}
},
// This should never happen, but on the off chance it does, I guess
// just dump it to stderr?
Err(e) => {
eprintln!("Failed to convert info msg to CString: {:?}", e);
},
}
}
}
/// Implements a visitor that builds a log message for the logger functionality in Dolphin
/// to consume. This currently builds a String internally that can be passed over to
/// the Dolphin side.
#[derive(Debug)]
struct DolphinLoggerVisitor(String);
impl DolphinLoggerVisitor {
/// Creates and returns a new `DolphinLoggerVisitor`.
///
/// This performs some initial work up front to build a log that is similar to
/// what Dolphin would construct; we're doing it here to avoid extra allocations
/// that would occur on the Dolphin logging side otherwise.
pub fn new(metadata: &Metadata, log_type: &str) -> Self {
let file = metadata.file().unwrap_or("");
let line = metadata.line().unwrap_or(0);
let level = metadata.level();
// Dolphin logs in the format of {Minutes}:{Seconds}:{Milliseconds}.
let time = OffsetDateTime::now_local().unwrap_or_else(|e| {
eprintln!("[dolphin_logger/layer.rs] Failed to get local time: {:?}", e);
// This will only happen if, for whatever reason, the timezone offset
// on the current system cannot be determined. Frankly there's bigger issues
// than logging if that's the case.
OffsetDateTime::now_utc()
});
let mins = time.minute();
let secs = time.second();
let millsecs = time.millisecond();
// We want 0-padded mins/secs, but we don't need the entire formatting infra
// that time would use - and this is simple enough to just do in a few lines.
let mp = match mins < 10 {
true => "0",
false => "",
};
let sp = match secs < 10 {
true => "0",
false => "",
};
Self(format!(
"{mp}{mins}:{sp}{secs}:{millsecs} {file}:{line} {level}[{log_type}]: "
))
}
/// The Dolphin log window needs a newline attached to the end, so we just write one
/// as a finishing method.
pub fn finish(mut self) -> String {
if let Err(e) = write!(&mut self.0, "\n") {
eprintln!("Failed to finish logging string: {:?}", e);
}
self.0
}
}
impl tracing::field::Visit for DolphinLoggerVisitor {
fn record_f64(&mut self, field: &tracing::field::Field, value: f64) {
if let Err(e) = write!(&mut self.0, "{}={} ", field.name(), value) {
eprintln!("Failed to record_error: {:?}", e);
}
}
fn record_i64(&mut self, field: &tracing::field::Field, value: i64) {
if let Err(e) = write!(&mut self.0, "{}={} ", field.name(), value) {
eprintln!("Failed to record_error: {:?}", e);
}
}
fn record_u64(&mut self, field: &tracing::field::Field, value: u64) {
if let Err(e) = write!(&mut self.0, "{}={} ", field.name(), value) {
eprintln!("Failed to record_error: {:?}", e);
}
}
fn record_i128(&mut self, field: &tracing::field::Field, value: i128) {
if let Err(e) = write!(&mut self.0, "{}={} ", field.name(), value) {
eprintln!("Failed to record_error: {:?}", e);
}
}
fn record_u128(&mut self, field: &tracing::field::Field, value: u128) {
if let Err(e) = write!(&mut self.0, "{}={} ", field.name(), value) {
eprintln!("Failed to record_error: {:?}", e);
}
}
fn record_bool(&mut self, field: &tracing::field::Field, value: bool) {
if let Err(e) = write!(&mut self.0, "{}={} ", field.name(), value) {
eprintln!("Failed to record_bool: {:?}", e);
}
}
fn record_str(&mut self, field: &tracing::field::Field, value: &str) {
if let Err(e) = write!(&mut self.0, "{}={} ", field.name(), value) {
eprintln!("Failed to record_str: {:?}", e);
}
}
fn record_error(&mut self, field: &tracing::field::Field, value: &(dyn std::error::Error + 'static)) {
if let Err(e) = write!(&mut self.0, "{}={} ", field.name(), value) {
eprintln!("Failed to record_error: {:?}", e);
}
}
fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) {
if let Err(e) = write!(&mut self.0, "{}={:?} ", field.name(), value) {
eprintln!("Failed to record_debug: {:?}", e);
}
}
}

View File

@@ -0,0 +1,145 @@
//! This library provides a tracing subscriber configuration that works with the
//! Dolphin logging setup.
//!
//! It essentially maps the concept of a `LogContainer` over to Rust, and provides
//! hooks to forward state change calls in. On top of that, this module contains
//! a custom `tracing_subscriber::Layer` that will pass logs back to Dolphin.
//!
//! Ultimately this should mean no log fragmentation or confusion.
use std::ffi::{c_char, c_int, CStr};
use std::sync::{Arc, Once, OnceLock, RwLock};
use tracing::Level;
use tracing_subscriber::prelude::*;
mod layer;
use layer::{convert_dolphin_log_level_to_tracing_level, DolphinLoggerLayer};
/// A type that mirrors a function over on the C++ side; because the library exists as
/// a dylib, it can't depend on any functions from the host application - but we _can_
/// pass in a hook/callback fn.
///
/// This should correspond to:
///
/// ``` notest
/// void LogFn(level, log_type, msg);
/// ```
pub(crate) type ForeignLoggerFn = unsafe extern "C" fn(c_int, c_int, *const c_char);
/// A marker for where logs should be routed to.
///
/// Rust enum variants can't be strings, but we want to be able to pass an
/// enum to the tracing macro `target` field - which requires a static str.
///
/// Thus we'll fake things a bit and just expose a module that keys things
/// accordingly. The syntax will be the same as if using an enum.
///
/// If you want to add a new logger type, you will need to add a new value here
/// and create a corresponding `LogContainer` on the Dolphin side with the corresponding
/// tag. The rest should "just work".
#[allow(non_snake_case)]
#[allow(non_upper_case_globals)]
pub mod Log {
/// Used for dumping logs from dependencies that we may need to inspect.
/// This may also get logs that are not properly tagged! If that happens, you need
/// to fix your logging calls. :)
pub const DEPENDENCIES: &'static str = "SLIPPI_RUST_DEPENDENCIES";
/// The default target for EXI tracing.
pub const EXI: &'static str = "SLIPPI_RUST_EXI";
/// Can be used to segment Jukebox logs.
pub const Jukebox: &'static str = "SLIPPI_RUST_JUKEBOX";
}
/// Represents a `LogContainer` on the Dolphin side.
#[derive(Debug)]
struct LogContainer {
kind: String,
log_type: c_int,
is_enabled: bool,
level: Level,
}
/// A global stack of `LogContainers`.
///
/// All logger registrations (which require `write`) should happen up-front due to how
/// Dolphin itself works. RwLock here should provide us parallel reader access after.
static LOG_CONTAINERS: OnceLock<Arc<RwLock<Vec<LogContainer>>>> = OnceLock::new();
/// This should be called from the Dolphin LogManager initialization to ensure that
/// all logging needs on the Rust side are configured appropriately.
///
/// *Usually* you do not want a library installing a global logger, however our use case is
/// not so standard: this library does in a sense act as an application due to the way it's
/// called into, and we *want* a global subscriber.
pub fn init(logger_fn: ForeignLoggerFn) {
let _containers = LOG_CONTAINERS.get_or_init(|| Arc::new(RwLock::new(Vec::new())));
// A guard so that we can't double-init logging layers.
static LOGGER: Once = Once::new();
// We don't use `try_init` here because we do want to
// know if something else, somehow, registered before us.
LOGGER.call_once(|| {
tracing_subscriber::registry().with(DolphinLoggerLayer::new(logger_fn)).init();
});
}
/// Registers a log container, which mirrors a Dolphin `LogContainer`.
///
/// This enables passing a configured log level and/or enabled status across the boundary from
/// Dolphin to our tracing subscriber setup. This is important as we want to short-circuit any
/// allocations during log handling that aren't necessary (e.g if a log is outright disabled).
pub fn register_container(kind: *const c_char, log_type: c_int, is_enabled: bool, default_log_level: c_int) {
// We control the other end of the registration flow, so we can ensure this ptr's valid UTF-8.
let c_kind_str = unsafe { CStr::from_ptr(kind) };
let kind = c_kind_str
.to_str()
.expect("[dolphin_logger::register_container]: Failed to convert kind c_char to str")
.to_string();
let containers = LOG_CONTAINERS
.get()
.expect("[dolphin_logger::register_container]: Attempting to get `LOG_CONTAINERS` before init");
let mut writer = containers
.write()
.expect("[dolphin_logger::register_container]: Unable to acquire write lock on `LOG_CONTAINERS`?");
(*writer).push(LogContainer {
kind,
log_type,
is_enabled,
level: convert_dolphin_log_level_to_tracing_level(default_log_level),
});
}
/// Sets a particular log container to a new enabled state. When a log container is in a disabled
/// state, no allocations will happen behind the scenes for any logging period.
pub fn update_container(kind: *const c_char, enabled: bool, level: c_int) {
// We control the other end of the registration flow, so we can ensure this ptr's valid UTF-8.
let c_kind_str = unsafe { CStr::from_ptr(kind) };
let kind = c_kind_str
.to_str()
.expect("[dolphin_logger::update_container]: Failed to convert kind c_char to str");
let containers = LOG_CONTAINERS
.get()
.expect("[dolphin_logger::update_container]: Attempting to get `LOG_CONTAINERS` before init");
let mut writer = containers
.write()
.expect("[dolphin_logger::update_container]: Unable to acquire write lock on `LOG_CONTAINERS`?");
for container in (*writer).iter_mut() {
if container.kind == kind {
container.is_enabled = enabled;
container.level = convert_dolphin_log_level_to_tracing_level(level);
break;
}
}
}

View File

@@ -0,0 +1,4 @@
[toolchain]
channel = "1.70.0"
components = ["rustfmt", "rustc-dev"]
profile = "minimal"

View File

@@ -0,0 +1,2 @@
max_width = 130
match_block_trailing_comma = true

134
Externals/corrosion/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,134 @@
cmake_minimum_required(VERSION 3.15)
project(Corrosion
VERSION 0.4.0
LANGUAGES NONE
HOMEPAGE_URL "https://corrosion-rs.github.io/corrosion/"
)
# Default behavior:
# - If the project is being used as a subdirectory, then don't build tests and
# don't enable any languages.
# - If this is a top level project, then build tests and enable the C++ compiler
if (NOT CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
set(_CORROSION_TOP_LEVEL OFF)
else()
set(_CORROSION_TOP_LEVEL ON)
endif()
# ==== Corrosion Configuration ====
option(
CORROSION_DEV_MODE
"Enables some additional features if you're developing Corrosion"
${_CORROSION_TOP_LEVEL}
)
option(
CORROSION_BUILD_TESTS
"Build Corrosion test project"
${_CORROSION_TOP_LEVEL}
)
set(
CORROSION_GENERATOR_EXECUTABLE CACHE STRING
"Use prebuilt, non-bootstrapped corrosion-generator")
mark_as_advanced(CORROSION_GENERATOR_EXECUTABLE)
if (CORROSION_GENERATOR_EXECUTABLE)
add_executable(Corrosion::Generator IMPORTED GLOBAL)
set_property(
TARGET Corrosion::Generator
PROPERTY IMPORTED_LOCATION ${CORROSION_GENERATOR_EXECUTABLE})
set(CORROSION_INSTALL_EXECUTABLE_DEFAULT OFF)
elseif(CORROSION_NATIVE_TOOLING OR CMAKE_VERSION VERSION_LESS 3.19.0)
set(CORROSION_INSTALL_EXECUTABLE_DEFAULT "ON")
else()
set(CORROSION_INSTALL_EXECUTABLE_DEFAULT OFF)
endif()
option(
CORROSION_INSTALL_EXECUTABLE
"Controls whether corrosion-generator is installed with the package"
${CORROSION_INSTALL_EXECUTABLE_DEFAULT}
)
mark_as_advanced(CORROSION_INSTALL_EXECUTABLE)
if (_CORROSION_TOP_LEVEL)
# We need to enable a language for corrosions test to work.
# For projects using corrosion this is not needed
enable_language(C)
endif()
# This little bit self-hosts the Corrosion toolchain to build the generator
# tool.
#
# It is strongly encouraged to install Corrosion separately and use
# `find_package(Corrosion REQUIRED)` instead if that works with your workflow.
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
include(Corrosion)
# Testing
if (CORROSION_BUILD_TESTS)
include(CTest)
add_subdirectory(test)
endif()
# If Corrosion is a subdirectory, do not enable its install code
if (NOT _CORROSION_TOP_LEVEL)
return()
endif()
# Installation
include(GNUInstallDirs)
if(CORROSION_INSTALL_EXECUTABLE)
# Builds the generator executable
corrosion_import_crate(MANIFEST_PATH generator/Cargo.toml)
set(_CORROSION_GENERATOR_DESTINATION "${CMAKE_INSTALL_FULL_LIBEXECDIR}")
corrosion_install(
TARGETS corrosion-generator
DESTINATION "${_CORROSION_GENERATOR_DESTINATION}"
)
else()
message(DEBUG "Not installing corrosion-generator since "
"`CORROSION_INSTALL_EXECUTABLE` is set to ${CORROSION_INSTALL_EXECUTABLE}"
)
endif()
# Generate the Config file
include(CMakePackageConfigHelpers)
configure_package_config_file(
cmake/CorrosionConfig.cmake.in CorrosionConfig.cmake
INSTALL_DESTINATION
"${CMAKE_INSTALL_FULL_LIBDIR}/cmake/Corrosion"
)
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/CorrosionConfigVersion.cmake"
VERSION ${PROJECT_VERSION}
COMPATIBILITY
SameMinorVersion # TODO: Should be SameMajorVersion when 1.0 is released
ARCH_INDEPENDENT
)
install(
FILES
"${CMAKE_CURRENT_BINARY_DIR}/CorrosionConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/CorrosionConfigVersion.cmake"
DESTINATION
"${CMAKE_INSTALL_FULL_LIBDIR}/cmake/Corrosion"
)
# These CMake scripts are needed both for the install and as a subdirectory
install(
FILES
cmake/Corrosion.cmake
cmake/CorrosionGenerator.cmake
cmake/FindRust.cmake
DESTINATION
"${CMAKE_INSTALL_FULL_DATADIR}/cmake"
)

21
Externals/corrosion/LICENSE vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 Andrew Gaspar
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

40
Externals/corrosion/README.md vendored Normal file
View File

@@ -0,0 +1,40 @@
# Corrosion
[![Build Status](https://github.com/corrosion-rs/corrosion/actions/workflows/test.yaml/badge.svg)](https://github.com/corrosion-rs/corrosion/actions?query=branch%3Amaster)
[![Documentation](https://img.shields.io/badge/docs-latest-blue.svg)](https://corrosion-rs.github.io/corrosion/)
![License](https://img.shields.io/badge/license-MIT-blue)
Corrosion, formerly known as cmake-cargo, is a tool for integrating Rust into an existing CMake
project. Corrosion can automatically import executables, static libraries, and dynamic libraries
from a workspace or package manifest (`Cargo.toml` file).
## Features
- Automatic Import of Executable, Static, and Shared Libraries from Rust Crate
- Easy Installation of Rust Executables
- Trivially Link Rust Executables to C/C++ Libraries in Tree
- Multi-Config Generator Support
- Simple Cross-Compilation
## Sample Usage with FetchContent
Using the CMake `FetchContent` module allows you to easily integrate corrosion into your build.
Other methods including installing corrosion or adding it as a subdirectory are covered in the
[setup chapter](https://corrosion-rs.github.io/corrosion/setup_corrosion.html) of the
corrosion [documentation](https://corrosion-rs.github.io/corrosion/).
```cmake
include(FetchContent)
FetchContent_Declare(
Corrosion
GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git
GIT_TAG v0.3 # Optionally specify a commit hash, version tag or branch here
)
FetchContent_MakeAvailable(Corrosion)
# Import targets defined in a package or workspace manifest `Cargo.toml` file
corrosion_import_crate(MANIFEST_PATH rust-lib/Cargo.toml)
add_executable(your_cpp_bin main.cpp)
target_link_libraries(your_cpp_bin PUBLIC rust-lib)
```

302
Externals/corrosion/RELEASES.md vendored Normal file
View File

@@ -0,0 +1,302 @@
# Unreleased
## Breaking Changes
- The Visual Studio Generators now require at least CMake 3.20.
This was previously announced in the 0.3.0 release notes.
- The previously deprecated function `corrosion_set_linker_language()`
will now raise an error when called and may be removed without further
notice in future stable releases. Use `corrosion_set_linker()` instead.
- Improved the FindRust target triple detection, which may cause different behavior in some cases.
The detection does not require an enabled language anymore and will always fall back
to the default host target triple. A warning is issued if target triple detection failed.
## Potentially Breaking Changes
- Corrosion now sets the `IMPORTED_NO_SONAME` property for shared rust libraries, since by
default they won't have an `soname` field.
If you add a rustflag like `-Clink-arg=-Wl,-soname,libmycrate.so` in your project,
you should set this property to false on the shared rust library.
## New features
- `corrosion_import_crate()` has two new options `LOCKED` and `FROZEN` which pass the
`--locked` and `--frozen` flags to all invocations of cargo. Only with CMake >= 3.19.
## Other changes
- When installing Corrosion with CMake >= 3.19, the legacy Generator tool is
no longer built and installed by default.
- Corrosion now issues a warning when setting the linker or setting linker
options for a Rust static library.
- Corrosion no longer enables the `C` language when CMake is in crosscompiling mode and
no languages where previously enabled. This is not considered a breaking change.
- `corrosion_import_crate()` now warns about unexpected arguments.
## Fixes
- Fix building when the `dev` profile is explicitly set by the user.
## Experimental status (may be changed or removed before a stable release)
- Experimental cxxbridge and cbindgen integration.
- Add a helper function to parse the package version from a Cargo.toml file
- Expose rustup toolchains discovered by `FindRust` in the following cache variables
which contain a list.
- `Rust_RUSTUP_TOOLCHAINS`: List of toolchains names
- `Rust_RUSTUP_TOOLCHAINS_VERSION`: List of `rustc` version of the toolchains
- `Rust_RUSTUP_TOOLCHAINS_RUSTC_PATH`: List of the path to `rustc`
- `Rust_RUSTUP_TOOLCHAINS_CARGO_PATH`: List of the path to `cargo`. Entries may be `NOTFOUND` if cargo
is not available for that toolchain.
- Add target properties `INTERFACE_CORROSION_RUSTC` and `INTERFACE_CORROSION_CARGO`, which may
be set to paths to `rustc` and `cargo` respectively to override the toolchain for a specific
target.
# 0.3.5 (2023-03-19)
- Fix building the Legacy Generator on Rust toolchains < 1.56 ([#365])
[#365]: https://github.com/corrosion-rs/corrosion/pull/365
# 0.3.4 (2023-03-02)
## Fixes
- Fix hostbuild (when CMake/Cargo is configured for cross-compiling) if clang is used ([#338]).
## Other
- Pass `--no-deps` to cargo metadata ([#334]).
- Bump the legacy generator dependencies
[#334]: https://github.com/corrosion-rs/corrosion/pull/334
[#338]: https://github.com/corrosion-rs/corrosion/pull/338
# 0.3.3 (2023-02-17)
## New features (Only available on CMake >= 3.19)
- Add new `IMPORTED_CRATES` flag to `corrosion_import_crate()` to retrieve the list of imported crates in the current
scope ([#312](https://github.com/corrosion-rs/corrosion/pull/312)).
## Fixes
- Fix imported location target property when the rust target name contains dashes
and a custom OUTPUT_DIRECTORY was specified by the user ([#322](https://github.com/corrosion-rs/corrosion/pull/322)).
- Fix building for custom rust target-triples ([#316](https://github.com/corrosion-rs/corrosion/pull/316))
# 0.3.2 (2023-01-11)
## New features (Only available on CMake >= 3.19)
- Add new `CRATE_TYPES` flag to `corrosion_import_crate()` to restrict which
crate types should be imported ([#269](https://github.com/corrosion-rs/corrosion/pull/269)).
- Add `NO_LINKER_OVERRIDE` flag to let Rust choose the default linker for the target
instead of what Corrosion thinks is the appropriate linker driver ([#272](https://github.com/corrosion-rs/corrosion/pull/272)).
## Fixes
- Fix clean target when cross-compiling ([#291](https://github.com/corrosion-rs/corrosion/pull/291)).
- Don't set the linker for Rust static libraries ([#275](https://github.com/corrosion-rs/corrosion/pull/275)).
- Minor fixes in FindRust [#297](https://github.com/corrosion-rs/corrosion/pull/297):
- fix a logic error in the version detection
- fix a logic error in `QUIET` mode when rustup is not found.
# 0.3.1 (2022-12-13)
### Fixes
- Fix a regression in detecting the MSVC abi ([#256])
- Fix an issue on macOS 13 which affected rust crates compiling C++ code in build scripts ([#254]).
- Fix corrosion not respecting `CMAKE_<XYZ>_OUTPUT_DIRECTORY` values ([#268]).
- Don't override rusts linker choice for the msvc abi (previously this was only skipped for msvc generators) ([#271])
[#254]: https://github.com/corrosion-rs/corrosion/pull/254
[#256]: https://github.com/corrosion-rs/corrosion/pull/256
[#268]: https://github.com/corrosion-rs/corrosion/pull/268
[#271]: https://github.com/corrosion-rs/corrosion/pull/271
# 0.3.0 (2022-10-31)
## Breaking
- The minimum supported rust version (MSRV) was increased to 1.46, due to a cargo issue that recently
surfaced on CI when using crates.io. On MacOS 12 and Windows-2022 at least Rust 1.54 is required.
- MacOS 10 and 11 are no longer officially supported and untested in CI.
- The minimum required CMake version is now 3.15.
- Adding a `PRE_BUILD` custom command on a `cargo-build_<target_name>` CMake target will no
longer work as expected. To support executing user defined commands before cargo build is
invoked users should use the newly added targets `cargo-prebuild` (before all cargo build invocations)
or `cargo-prebuild_<target_name>` as a dependency target.
Example: `add_dependencies(cargo-prebuild code_generator_target)`
### Breaking: Removed previously deprecated functionality
- Removed `add_crate()` function. Use `corrosio_import_crate()` instead.
- Removed `cargo_link_libraries()` function. Use `corrosion_link_libraries()` instead.
- Removed experimental CMake option `CORROSION_EXPERIMENTAL_PARSER`.
The corresponding stable option is `CORROSION_NATIVE_TOOLING` albeit with inverted semantics.
- Previously Corrosion would set the `HOST_CC` and `HOST_CXX` environment variables when invoking
cargo build, if the environment variables `CC` and `CXX` outside of CMake where set.
However this did not work as expected in all cases and sometimes the `HOST_CC` variable would be set
to a cross-compiler for unknown reasons. For this reason `HOST_CC` and `HOST_CXX` are not set by
corrosion anymore, but users can still set them manually if required via `corrosion_set_env_vars()`.
- The `CARGO_RUST_FLAGS` family of cache variables were removed. Corrosion does not internally use them
anymore.
## Potentially breaking
- The working directory when invoking `cargo build` was changed to the directory of the Manifest
file. This now allows cargo to pick up `.cargo/config.toml` files located in the source tree.
([205](https://github.com/corrosion-rs/corrosion/pull/205))
- Corrosion internally invokes `cargo build`. When passing arguments to `cargo build`, Corrosion
now uses the CMake `VERBATIM` option. In rare cases this may require you to change how you quote
parameters passed to corrosion (e.g. via `corrosion_add_target_rustflags()`).
For example setting a `cfg` option previously required double escaping the rustflag like this
`"--cfg=something=\\\"value\\\""`, but now it can be passed to corrosion without any escapes:
`--cfg=something="value"`.
- Corrosion now respects the CMake `OUTPUT_DIRECTORY` target properties. More details in the "New features" section.
## New features
- Support setting rustflags for only the main target and none of its dependencies ([215](https://github.com/corrosion-rs/corrosion/pull/215)).
A new function `corrosion_add_target_local_rustflags(target_name rustc_flag [more_flags ...])`
is added for this purpose.
This is useful in cases where you only need rustflags on the main-crate, but need to set different
flags for different targets. Without "local" Rustflags this would require rebuilds of the
dependencies when switching targets.
- Support explicitly selecting a linker ([208](https://github.com/corrosion-rs/corrosion/pull/208)).
The linker can be selected via `corrosion_set_linker(target_name linker)`.
Please note that this only has an effect for targets, where the final linker invocation is done
by cargo, i.e. targets where foreign code is linked into rust code and not the other way around.
- Corrosion now respects the CMake `OUTPUT_DIRECTORY` target properties and copies build artifacts to the expected
locations ([217](https://github.com/corrosion-rs/corrosion/pull/217)), if the properties are set.
This feature requires at least CMake 3.19 and is enabled by default if supported. Please note that the `OUTPUT_NAME`
target properties are currently not supported.
Specifically, the following target properties are now respected:
- [ARCHIVE_OUTPUT_DIRECTORY](https://cmake.org/cmake/help/latest/prop_tgt/ARCHIVE_OUTPUT_DIRECTORY.html)
- [LIBRARY_OUTPUT_DIRECTORY](https://cmake.org/cmake/help/latest/prop_tgt/LIBRARY_OUTPUT_DIRECTORY.html)
- [RUNTIME_OUTPUT_DIRECTORY](https://cmake.org/cmake/help/latest/prop_tgt/RUNTIME_OUTPUT_DIRECTORY.html)
- [PDB_OUTPUT_DIRECTORY](https://cmake.org/cmake/help/latest/prop_tgt/PDB_OUTPUT_DIRECTORY.html)
- Corrosion now supports packages with potentially multiple binaries (bins) and a library (lib) at the
same time. The only requirement is that the names of all `bin`s and `lib`s in the whole project must be unique.
Users can set the names in the `Cargo.toml` by adding `name = <unique_name>` in the `[[bin]]` and `[lib]` tables.
- FindRust now has improved support for the `VERSION` option of `find_package` and will now attempt to find a matching
toolchain version. Previously it was only checked if the default toolchain matched to required version.
- For rustup managed toolchains a CMake error is issued with a helpful message if the required target for
the selected toolchain is not installed.
## Fixes
- Fix a CMake developer Warning when a Multi-Config Generator and Rust executable targets
([#213](https://github.com/corrosion-rs/corrosion/pull/213)).
- FindRust now respects the `QUIET` option to `find_package()` in most cases.
## Deprecation notice
- Support for the MSVC Generators with CMake toolchains before 3.20 is deprecated and will be removed in the next
release (v0.4). All other Multi-config Generators already require CMake 3.20.
## Internal Changes
- The CMake Generator written in Rust and `CorrosionGenerator.cmake` which are responsible for parsing
`cargo metadata` output to create corresponding CMake targets for all Rust targets now share most code.
This greatly simplified the CMake generator written in Rust and makes it much easier maintaining and adding
new features regardless of how `cargo metadata` is parsed.
# 0.2.2 (2022-09-01)
## Fixes
- Do not use C++17 in the tests (makes tests work with older C++ compilers) ([184](https://github.com/corrosion-rs/corrosion/pull/184))
- Fix finding cargo on NixOS ([192](https://github.com/corrosion-rs/corrosion/pull/192))
- Fix issue with Rustflags test when using a Build type other than Debug and Release ([203](https://github.com/corrosion-rs/corrosion/pull/203)).
# 0.2.1 (2022-05-07)
## Fixes
- Fix missing variables provided by corrosion, when corrosion is used as a subdirectory ([181](https://github.com/corrosion-rs/corrosion/pull/181)):
Public [Variables](https://github.com/corrosion-rs/corrosion#information-provided-by-corrosion) set
by Corrosion were not visible when using Corrosion as a subdirectory, due to the wrong scope of
the variables. This was fixed by promoting the respective variables to Cache variables.
# 0.2.0 (2022-05-05)
## Breaking changes
- Removed the integrator build script ([#156](https://github.com/corrosion-rs/corrosion/pull/156)).
The build script provided by corrosion (for rust code that links in foreign code) is no longer necessary,
so users can just remove the dependency.
## Deprecations
- Direct usage of the following target properties has been deprecated. The names of the custom properties are
no longer considered part of the public API and may change in the future. Instead, please use the functions
provided by corrosion. Internally different property names are used depending on the CMake version.
- `CORROSION_FEATURES`, `CORROSION_ALL_FEATURES`, `CORROSION_NO_DEFAULT_FEATURES`. Instead please use
`corrosion_set_features()`. See the updated Readme for details.
- `CORROSION_ENVIRONMENT_VARIABLES`. Please use `corrosion_set_env_vars()` instead.
- `CORROSION_USE_HOST_BUILD`. Please use `corrosion_set_hostbuild()` instead.
- The Minimum CMake version will likely be increased for the next major release. At the very least we want to drop
support for CMake 3.12, but requiring CMake 3.16 or even 3.18 is also on the table. If you are using a CMake version
that would be no longer supported by corrosion, please comment on issue
[#168](https://github.com/corrosion-rs/corrosion/issues/168), so that we can gauge the number of affected users.
## New features
- Add `NO_STD` option to `corrosion_import_crate` ([#154](https://github.com/corrosion-rs/corrosion/pull/154)).
- Remove the requirement of building the Rust based generator crate for CMake >= 3.19. This makes using corrosion as
a subdirectory as fast as the installed version (since everything is done in CMake).
([#131](https://github.com/corrosion-rs/corrosion/pull/131), [#161](https://github.com/corrosion-rs/corrosion/pull/161))
If you do choose to install Corrosion, then by default the old Generator is still compiled and installed, so you can
fall back to using it in case you use multiple cmake versions on the same machine for different projects.
## Fixes
- Fix Corrosion on MacOS 11 and 12 ([#167](https://github.com/corrosion-rs/corrosion/pull/167) and
[#164](https://github.com/corrosion-rs/corrosion/pull/164)).
- Improve robustness of parsing the LLVM version (exported in `Rust_LLVM_VERSION`). It now also works for
Rust versions, where the LLVM version is reported as `MAJOR.MINOR`. ([#148](https://github.com/corrosion-rs/corrosion/pull/148))
- Fix a bug which occurred when Corrosion was added multiple times via `add_subdirectory()`
([#143](https://github.com/corrosion-rs/corrosion/pull/143)).
- Set `CC_<target_triple_undercore>` and `CXX_<target_triple_undercore>` environment variables for the invocation of
`cargo build` to the compilers selected by CMake (if any)
([#138](https://github.com/corrosion-rs/corrosion/pull/138) and [#161](https://github.com/corrosion-rs/corrosion/pull/161)).
This should ensure that C dependencies built in cargo buildscripts via [cc-rs](https://github.com/alexcrichton/cc-rs)
use the same compiler as CMake built dependencies. Users can override the compiler by specifying the higher
priority environment variable variants with dashes instead of underscores (See cc-rs documentation for details).
- Fix Ninja-Multiconfig Generator support for CMake versions >= 3.20. Previous CMake versions are missing a feature,
which prevents us from supporting the Ninja-Multiconfig generator. ([#137](https://github.com/corrosion-rs/corrosion/pull/137))
# 0.1.0 (2022-02-01)
This is the first release of corrosion after it was moved to the new corrosion-rs organization.
Since there are no previous releases, this is not a complete changelog but only lists changes since
September 2021.
## New features
- [Add --profile support for rust >= 1.57](https://github.com/corrosion-rs/corrosion/pull/130):
Allows users to specify a custom cargo profile with
`corrosion_import_crate(... PROFILE <profilename>)`.
- [Add support for specifying per-target Rustflags](https://github.com/corrosion-rs/corrosion/pull/127):
Rustflags can be added via `corrosion_add_target_rustflags(<target_name> [rustflags1...])`
- [Add `Rust_IS_NIGHTLY` and `Rust_LLVM_VERSION` variables](https://github.com/corrosion-rs/corrosion/pull/123):
This may be useful if you want to conditionally enabled features when using a nightly toolchain
or a specific LLVM Version.
- [Let `FindRust` fail gracefully if rustc is not found](https://github.com/corrosion-rs/corrosion/pull/111):
This allows using `FindRust` in a more general setting (without corrosion).
- [Add support for cargo feature selection](https://github.com/corrosion-rs/corrosion/pull/108):
See the [README](https://github.com/corrosion-rs/corrosion#cargo-feature-selection) for details on
how to select features.
## Fixes
- [Fix the cargo-clean target](https://github.com/corrosion-rs/corrosion/pull/129)
- [Fix #84: CorrosionConfig.cmake looks in wrong place for Corrosion::Generator when CMAKE_INSTALL_LIBEXEC is an absolute path](https://github.com/corrosion-rs/corrosion/pull/122/commits/6f29af3ac53917ca2e0638378371e715a18a532d)
- [Fix #116: (Option CORROSION_INSTALL_EXECUTABLE not working)](https://github.com/corrosion-rs/corrosion/commit/97d44018fac1b1a2a7c095288c628f5bbd9b3184)
- [Fix building on Windows with rust >= 1.57](https://github.com/corrosion-rs/corrosion/pull/120)
## Known issues:
- Corrosion is currently not working on macos-11 and newer. See issue [#104](https://github.com/corrosion-rs/corrosion/issues/104).
Contributions are welcome.

5
Externals/corrosion/bors.toml vendored Normal file
View File

@@ -0,0 +1,5 @@
status = [
"bors-ci-status",
]
timeout_sec = 1800

1859
Externals/corrosion/cmake/Corrosion.cmake vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,18 @@
@PACKAGE_INIT@
if (Corrosion_FOUND)
return()
endif()
list(APPEND CMAKE_MODULE_PATH "@CMAKE_INSTALL_FULL_DATADIR@/cmake")
set(CORROSION_NATIVE_TOOLING_INSTALLED @CORROSION_NATIVE_TOOLING@)
if(CORROSION_NATIVE_TOOLING_INSTALLED AND NOT TARGET Corrosion::Generator)
add_executable(Corrosion::Generator IMPORTED GLOBAL)
set_property(
TARGET Corrosion::Generator
PROPERTY IMPORTED_LOCATION "@CMAKE_INSTALL_FULL_LIBEXECDIR@/corrosion-generator")
endif()
include(Corrosion)

View File

@@ -0,0 +1,303 @@
function(_cargo_metadata out manifest)
set(OPTIONS LOCKED FROZEN)
set(ONE_VALUE_KEYWORDS "")
set(MULTI_VALUE_KEYWORDS "")
cmake_parse_arguments(PARSE_ARGV 2 CM "${OPTIONS}" "${ONE_VALUE_KEYWORDS}" "${MULTI_VALUE_KEYWORDS}")
if(DEFINED CM_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "Internal error - unexpected arguments: ${CM_UNPARSED_ARGUMENTS}")
elseif(DEFINED CM_KEYWORDS_MISSING_VALUES)
message(FATAL_ERROR "Internal error - the following keywords had no associated value(s):"
"${CM_KEYWORDS_MISSING_VALUES}")
endif()
set(cargo_locked "")
set(cargo_frozen "")
if(LOCKED)
set(cargo_locked "--locked")
endif()
if(FROZEN)
set(cargo_frozen "--frozen")
endif()
execute_process(
COMMAND
${CMAKE_COMMAND} -E env
"CARGO_BUILD_RUSTC=${_CORROSION_RUSTC}"
"${_CORROSION_CARGO}"
metadata
--manifest-path "${manifest}"
--format-version 1
# We don't care about non-workspace dependencies
--no-deps
${cargo_locked}
${cargo_frozen}
OUTPUT_VARIABLE json
COMMAND_ERROR_IS_FATAL ANY
)
set(${out} "${json}" PARENT_SCOPE)
endfunction()
# Add targets (crates) of one package
function(_generator_add_package_targets)
set(OPTIONS NO_LINKER_OVERRIDE)
set(ONE_VALUE_KEYWORDS WORKSPACE_MANIFEST_PATH PACKAGE_MANIFEST_PATH PACKAGE_NAME PACKAGE_VERSION TARGETS_JSON OUT_CREATED_TARGETS)
set(MULTI_VALUE_KEYWORDS CRATE_TYPES)
cmake_parse_arguments(PARSE_ARGV 0 GAPT "${OPTIONS}" "${ONE_VALUE_KEYWORDS}" "${MULTI_VALUE_KEYWORDS}")
if(DEFINED GAPT_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "Internal error - unexpected arguments: ${GAPT_UNPARSED_ARGUMENTS}")
elseif(DEFINED GAPT_KEYWORDS_MISSING_VALUES)
message(FATAL_ERROR "Internal error - the following keywords had no associated value(s):"
"${GAPT_KEYWORDS_MISSING_VALUES}")
endif()
_corrosion_option_passthrough_helper(NO_LINKER_OVERRIDE GAPT no_linker_override)
set(workspace_manifest_path "${GAPT_WORKSPACE_MANIFEST_PATH}")
set(package_manifest_path "${GAPT_PACKAGE_MANIFEST_PATH}")
set(package_name "${GAPT_PACKAGE_NAME}")
set(package_version "${GAPT_PACKAGE_VERSION}")
set(targets "${GAPT_TARGETS_JSON}")
set(out_created_targets "${GAPT_OUT_CREATED_TARGETS}")
set(crate_types "${GAPT_CRATE_TYPES}")
set(corrosion_targets "")
file(TO_CMAKE_PATH "${package_manifest_path}" manifest_path)
string(JSON targets_len LENGTH "${targets}")
math(EXPR targets_len-1 "${targets_len} - 1")
message(DEBUG "Found ${targets_len} targets in package ${package_name}")
foreach(ix RANGE ${targets_len-1})
string(JSON target GET "${targets}" ${ix})
string(JSON target_name GET "${target}" "name")
string(JSON target_kind GET "${target}" "kind")
string(JSON target_kind_len LENGTH "${target_kind}")
string(JSON target_name GET "${target}" "name")
math(EXPR target_kind_len-1 "${target_kind_len} - 1")
set(kinds)
foreach(ix RANGE ${target_kind_len-1})
string(JSON kind GET "${target_kind}" ${ix})
if(NOT crate_types OR ${kind} IN_LIST crate_types)
list(APPEND kinds ${kind})
endif()
endforeach()
if(TARGET "${target_name}"
AND ("staticlib" IN_LIST kinds OR "cdylib" IN_LIST kinds OR "bin" IN_LIST kinds)
)
message(WARNING "Failed to import Rust crate ${target_name} (kind: `${target_kind}`) because a target "
"with the same name already exists. Skipping this target.\n"
"Help: If you are importing a package which exposes both a `lib` and "
"a `bin` target, please consider explicitly naming the targets in your `Cargo.toml` manifest.\n"
"Note: If you have multiple different packages which have targets with the same name, please note that "
"this is currently not supported by Corrosion. Feel free to open an issue on Github to request "
"supporting this scenario."
)
# Skip this target to prevent a hard error.
continue()
endif()
if("staticlib" IN_LIST kinds OR "cdylib" IN_LIST kinds)
set(archive_byproducts "")
set(shared_lib_byproduct "")
set(pdb_byproduct "")
_corrosion_add_library_target(
WORKSPACE_MANIFEST_PATH "${workspace_manifest_path}"
TARGET_NAME "${target_name}"
LIB_KINDS ${kinds}
OUT_ARCHIVE_OUTPUT_BYPRODUCTS archive_byproducts
OUT_SHARED_LIB_BYPRODUCTS shared_lib_byproduct
OUT_PDB_BYPRODUCT pdb_byproduct
)
set(byproducts "")
list(APPEND byproducts "${archive_byproducts}" "${shared_lib_byproduct}" "${pdb_byproduct}")
set(cargo_build_out_dir "")
_add_cargo_build(
cargo_build_out_dir
PACKAGE ${package_name}
TARGET ${target_name}
MANIFEST_PATH "${manifest_path}"
WORKSPACE_MANIFEST_PATH "${workspace_manifest_path}"
TARGET_KINDS "${kinds}"
BYPRODUCTS "${byproducts}"
# Optional
${no_linker_override}
)
if(archive_byproducts)
_corrosion_copy_byproducts(
${target_name} ARCHIVE_OUTPUT_DIRECTORY "${cargo_build_out_dir}" "${archive_byproducts}"
)
endif()
if(shared_lib_byproduct)
_corrosion_copy_byproducts(
${target_name} LIBRARY_OUTPUT_DIRECTORY "${cargo_build_out_dir}" "${shared_lib_byproduct}"
)
endif()
if(pdb_byproduct)
_corrosion_copy_byproducts(
${target_name} PDB_OUTPUT_DIRECTORY "${cargo_build_out_dir}" "${pdb_byproduct}"
)
endif()
list(APPEND corrosion_targets ${target_name})
# Note: "bin" is mutually exclusive with "staticlib/cdylib", since `bin`s are seperate crates from libraries.
elseif("bin" IN_LIST kinds)
set(bin_byproduct "")
set(pdb_byproduct "")
_corrosion_add_bin_target("${workspace_manifest_path}" "${target_name}"
"bin_byproduct" "pdb_byproduct"
)
set(byproducts "")
list(APPEND byproducts "${bin_byproduct}" "${pdb_byproduct}")
set(cargo_build_out_dir "")
_add_cargo_build(
cargo_build_out_dir
PACKAGE "${package_name}"
TARGET "${target_name}"
MANIFEST_PATH "${manifest_path}"
WORKSPACE_MANIFEST_PATH "${workspace_manifest_path}"
TARGET_KINDS "bin"
BYPRODUCTS "${byproducts}"
# Optional
${no_linker_override}
)
_corrosion_copy_byproducts(
${target_name} RUNTIME_OUTPUT_DIRECTORY "${cargo_build_out_dir}" "${bin_byproduct}"
)
if(pdb_byproduct)
_corrosion_copy_byproducts(
${target_name} PDB_OUTPUT_DIRECTORY "${cargo_build_out_dir}" "${pdb_byproduct}"
)
endif()
list(APPEND corrosion_targets ${target_name})
else()
# ignore other kinds (like examples, tests, build scripts, ...)
endif()
endforeach()
if(NOT corrosion_targets)
message(DEBUG "No relevant targets found in package ${package_name} - Ignoring")
else()
set_target_properties(${corrosion_targets} PROPERTIES INTERFACE_COR_PACKAGE_MANIFEST_PATH "${package_manifest_path}")
endif()
set(${out_created_targets} "${corrosion_targets}" PARENT_SCOPE)
endfunction()
# Add all cargo targets defined in the packages defined in the Cargo.toml manifest at
# `MANIFEST_PATH`.
function(_generator_add_cargo_targets)
set(options NO_LINKER_OVERRIDE)
set(one_value_args MANIFEST_PATH IMPORTED_CRATES)
set(multi_value_args CRATES CRATE_TYPES)
cmake_parse_arguments(
GGC
"${options}"
"${one_value_args}"
"${multi_value_args}"
${ARGN}
)
_corrosion_option_passthrough_helper(NO_LINKER_OVERRIDE GGC no_linker_override)
_corrosion_arg_passthrough_helper(CRATE_TYPES GGC crate_types)
_cargo_metadata(json "${GGC_MANIFEST_PATH}")
string(JSON packages GET "${json}" "packages")
string(JSON workspace_members GET "${json}" "workspace_members")
string(JSON pkgs_len LENGTH "${packages}")
math(EXPR pkgs_len-1 "${pkgs_len} - 1")
string(JSON ws_mems_len LENGTH ${workspace_members})
math(EXPR ws_mems_len-1 "${ws_mems_len} - 1")
set(created_targets "")
foreach(ix RANGE ${pkgs_len-1})
string(JSON pkg GET "${packages}" ${ix})
string(JSON pkg_id GET "${pkg}" "id")
string(JSON pkg_name GET "${pkg}" "name")
string(JSON pkg_manifest_path GET "${pkg}" "manifest_path")
string(JSON pkg_version GET "${pkg}" "version")
if(DEFINED GGC_CRATES)
if(NOT pkg_name IN_LIST GGC_CRATES)
message(DEBUG "Package `${pkg_name}` was not in the `CRATES` allowlist passed to "
"corrosion_import_crate. Ignoring the package."
)
continue()
endif()
endif()
# probably this loop is not necessary at all, since when using --no-deps, the
# contents of packages should already be only workspace members!
unset(pkg_is_ws_member)
foreach(ix RANGE ${ws_mems_len-1})
string(JSON ws_mem GET "${workspace_members}" ${ix})
if(ws_mem STREQUAL pkg_id)
set(pkg_is_ws_member YES)
break()
endif()
endforeach()
if(NOT DEFINED pkg_is_ws_member)
# Since we pass `--no-deps` to cargo metadata now, I think this situation can't happen, but lets check for
# it anyway, just to discover any potential issues.
# If nobody complains for a while, it should be safe to remove this check and the previous loop, which
# should speed up the configuration process.
message(WARNING "The package `${pkg_name}` unexpectedly is not part of the workspace."
"Please open an issue at corrosion with some background information on the package"
)
endif()
string(JSON targets GET "${pkg}" "targets")
_generator_add_package_targets(
WORKSPACE_MANIFEST_PATH "${GGC_MANIFEST_PATH}"
PACKAGE_MANIFEST_PATH "${pkg_manifest_path}"
PACKAGE_NAME "${pkg_name}"
PACKAGE_VERSION "${pkg_version}"
TARGETS_JSON "${targets}"
OUT_CREATED_TARGETS curr_created_targets
${no_linker_override}
${crate_types}
)
list(APPEND created_targets "${curr_created_targets}")
endforeach()
if(NOT created_targets)
message(FATAL_ERROR "found no targets in ${pkgs_len} packages")
else()
message(DEBUG "Corrosion created the following CMake targets: ${created_targets}")
endif()
if(GGC_IMPORTED_CRATES)
set(${GGC_IMPORTED_CRATES} "${created_targets}" PARENT_SCOPE)
endif()
foreach(target_name ${created_targets})
foreach(output_var RUNTIME_OUTPUT_DIRECTORY ARCHIVE_OUTPUT_DIRECTORY LIBRARY_OUTPUT_DIRECTORY PDB_OUTPUT_DIRECTORY)
get_target_property(output_dir ${target_name} "${output_var}")
if (NOT output_dir AND DEFINED "CMAKE_${output_var}")
set_property(TARGET ${target_name} PROPERTY ${output_var} "${CMAKE_${output_var}}")
endif()
foreach(config_type ${CMAKE_CONFIGURATION_TYPES})
string(TOUPPER "${config_type}" config_type_upper)
get_target_property(output_dir ${target_name} "${output_var}_${config_type_upper}")
if (NOT output_dir AND DEFINED "CMAKE_${output_var}_${config_type_upper}")
set_property(TARGET ${target_name} PROPERTY "${output_var}_${config_type_upper}" "${CMAKE_${output_var}_${config_type_upper}}")
endif()
endforeach()
endforeach()
endforeach()
endfunction()

728
Externals/corrosion/cmake/FindRust.cmake vendored Normal file
View File

@@ -0,0 +1,728 @@
#[=======================================================================[.rst:
FindRust
--------
Find Rust
This module finds an installed rustc compiler and the cargo build tool. If Rust
is managed by rustup it determines the available toolchains and returns a
concrete Rust version, not a rustup proxy.
#]=======================================================================]
cmake_minimum_required(VERSION 3.12)
# search for Cargo here and set up a bunch of cool flags and stuff
include(FindPackageHandleStandardArgs)
# Print error message and return.
macro(_findrust_failed)
if("${Rust_FIND_REQUIRED}")
message(FATAL_ERROR ${ARGN})
elseif(NOT "${Rust_FIND_QUIETLY}")
message(WARNING ${ARGN})
endif()
# Note: PARENT_SCOPE is the scope of the caller of the caller of this macro.
set(Rust_FOUND "" PARENT_SCOPE)
return()
endmacro()
# Checks if the actual version of a Rust toolchain matches the VERSION requirements specified in find_package.
function(_findrust_version_ok ACTUAL_VERSION OUT_IS_OK)
if(DEFINED Rust_FIND_VERSION_RANGE)
if(Rust_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE")
set(COMPARSION_OPERATOR "VERSION_LESS_EQUAL")
elseif(Rust_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE")
set(COMPARSION_OPERATOR "VERSION_LESS")
else()
message(FATAL_ERROR "Unexpected value in `<PackageName>_FIND_VERSION_RANGE_MAX`: "
"`${Rust_FIND_VERSION_RANGE_MAX}`.")
endif()
if(("${ACTUAL_VERSION}" VERSION_GREATER_EQUAL "${Rust_FIND_VERSION_RANGE_MIN}")
AND
( "${ACTUAL_VERSION}" ${COMPARSION_OPERATOR} "${Rust_FIND_VERSION_RANGE_MAX}" )
)
set("${OUT_IS_OK}" TRUE PARENT_SCOPE)
else()
set("${OUT_IS_OK}" FALSE PARENT_SCOPE)
endif()
elseif(DEFINED Rust_FIND_VERSION)
if(Rust_VERSION_EXACT)
set(COMPARISON_OPERATOR VERSION_EQUAL)
else()
set(COMPARISON_OPERATOR VERSION_GREATER_EQUAL)
endif()
if(_TOOLCHAIN_${_TOOLCHAIN_SELECTED}_VERSION "${COMPARISON_OPERATOR}" Rust_FIND_VERSION)
set("${OUT_IS_OK}" TRUE PARENT_SCOPE)
else()
set("${OUT_IS_OK}" FALSE PARENT_SCOPE)
endif()
else()
# if no VERSION requirement was specified, the version is always okay.
set("${OUT_IS_OK}" TRUE PARENT_SCOPE)
endif()
endfunction()
function(_corrosion_strip_target_triple input_triple_or_path output_triple)
# If the target_triple is a path to a custom target specification file, then strip everything
# except the filename from `target_triple`.
get_filename_component(target_triple_ext "${input_triple_or_path}" EXT)
set(target_triple "${input_triple_or_path}")
if(target_triple_ext)
if(target_triple_ext STREQUAL ".json")
get_filename_component(target_triple "${input_triple_or_path}" NAME_WE)
endif()
endif()
set(${output_triple} "${target_triple}" PARENT_SCOPE)
endfunction()
function(_corrosion_parse_target_triple target_triple out_arch out_vendor out_os out_env)
_corrosion_strip_target_triple(${target_triple} target_triple)
# The vendor part may be left out from the target triple, and since `env` is also optional,
# we determine if vendor is present by matching against a list of known vendors.
set(known_vendors
"apple"
"esp" # riscv32imc-esp-espidf
"fortanix"
"kmc"
"pc"
"nintendo"
"nvidia"
"openwrt"
"alpine"
"unknown"
"uwp" # aarch64-uwp-windows-msvc
"wrs" # e.g. aarch64-wrs-vxworks
"sony"
"sun"
)
# todo: allow users to add additional vendors to the list via a cmake variable.
list(JOIN known_vendors "|" known_vendors_joined)
# vendor is optional - We detect if vendor is present by matching against a known list of
# vendors. The next field is the OS, which we assume to always be present, while the last field
# is again optional and contains the environment.
string(REGEX MATCH
"^([a-z0-9_\.]+)-((${known_vendors_joined})-)?([a-z0-9_]+)(-([a-z0-9_]+))?$"
whole_match
"${target_triple}"
)
if((NOT whole_match) AND (NOT CORROSION_NO_WARN_PARSE_TARGET_TRIPLE_FAILED))
message(WARNING "Failed to parse target-triple `${target_triple}`."
"Corrosion attempts to link required C libraries depending on the OS "
"specified in the Rust target-triple for Linux, MacOS and windows.\n"
"Note: If you are targeting a different OS you can surpress this warning by"
" setting the CMake cache variable "
"`CORROSION_NO_WARN_PARSE_TARGET_TRIPLE_FAILED`."
"Please consider opening an issue on github if you encounter this warning."
)
endif()
message(DEBUG "Parsed Target triple: arch: ${CMAKE_MATCH_1}, vendor: ${CMAKE_MATCH_3}, "
"OS: ${CMAKE_MATCH_4}, env: ${CMAKE_MATCH_6}")
set("${out_arch}" "${CMAKE_MATCH_1}" PARENT_SCOPE)
set("${out_vendor}" "${CMAKE_MATCH_3}" PARENT_SCOPE)
set("${out_os}" "${CMAKE_MATCH_4}" PARENT_SCOPE)
set("${out_env}" "${CMAKE_MATCH_6}" PARENT_SCOPE)
endfunction()
# Hardcoded, best effort approach
function(_corrosion_determine_libs arch vendor os env out_libs)
if(os STREQUAL "windows")
list(APPEND libs "advapi32" "userenv" "ws2_32")
if(env STREQUAL "msvc")
list(APPEND libs "$<$<CONFIG:Debug>:msvcrtd>")
# CONFIG takes a comma seperated list starting with CMake 3.19, but we still need to
# support older CMake versions.
set(config_is_release "$<OR:$<CONFIG:Release>,$<CONFIG:MinSizeRel>,$<CONFIG:RelWithDebInfo>>")
list(APPEND libs "$<${config_is_release}:msvcrt>")
elseif(env STREQUAL "gnu")
list(APPEND libs "gcc_eh" "pthread")
endif()
if(Rust_VERSION VERSION_LESS "1.33.0")
list(APPEND libs "shell32" "kernel32")
endif()
if(Rust_VERSION VERSION_GREATER_EQUAL "1.57.0")
list(APPEND libs "bcrypt")
endif()
elseif(vendor STREQUAL "apple" AND os STREQUAL "darwin")
list(APPEND libs "System" "resolv" "c" "m")
elseif(os STREQUAL "linux")
list(APPEND libs "dl" "rt" "pthread" "gcc_s" "c" "m" "util")
endif()
set("${out_libs}" "${libs}" PARENT_SCOPE)
endfunction()
if (NOT "${Rust_TOOLCHAIN}" STREQUAL "$CACHE{Rust_TOOLCHAIN}")
# Promote Rust_TOOLCHAIN to a cache variable if it is not already a cache variable
set(Rust_TOOLCHAIN ${Rust_TOOLCHAIN} CACHE STRING "Requested rustup toolchain" FORCE)
endif()
# This block checks to see if we're prioritizing a rustup-managed toolchain.
if (DEFINED Rust_TOOLCHAIN)
# If the user specifies `Rust_TOOLCHAIN`, then look for `rustup` first, rather than `rustc`.
find_program(Rust_RUSTUP rustup PATHS "$ENV{HOME}/.cargo/bin")
if(Rust_RUSTUP)
set(_RESOLVE_RUSTUP_TOOLCHAINS ON)
else()
set(_RESOLVE_RUSTUP_TOOLCHAINS OFF)
if(NOT "${Rust_FIND_QUIETLY}")
message(
WARNING "CMake variable `Rust_TOOLCHAIN` specified, but `rustup` was not found. "
"Ignoring toolchain and looking for a Rust toolchain not managed by rustup.")
endif()
endif()
else()
# If we aren't definitely using a rustup toolchain, look for rustc first - the user may have
# a toolchain installed via a method other than rustup higher in the PATH, which should be
# preferred. However, if the first-found rustc is a rustup proxy, then we'll revert to
# finding the preferred toolchain via rustup.
# Uses `Rust_COMPILER` to let user-specified `rustc` win. But we will still "override" the
# user's setting if it is pointing to `rustup`. Default rustup install path is provided as a
# backup if a toolchain cannot be found in the user's PATH.
if (DEFINED Rust_COMPILER)
set(_Rust_COMPILER_TEST "${Rust_COMPILER}")
set(_USER_SPECIFIED_RUSTC ON)
if(NOT (EXISTS "${_Rust_COMPILER_TEST}" AND NOT IS_DIRECTORY "${_Rust_COMPILER_TEST}"))
set(_ERROR_MESSAGE "Rust_COMPILER was set to `${Rust_COMPILER}`, but this file does "
"not exist."
)
_findrust_failed(${_ERROR_MESSAGE})
return()
endif()
else()
find_program(_Rust_COMPILER_TEST rustc PATHS "$ENV{HOME}/.cargo/bin")
if(NOT EXISTS "${_Rust_COMPILER_TEST}")
set(_ERROR_MESSAGE "`rustc` not found in PATH or `$ENV{HOME}/.cargo/bin`.\n"
"Hint: Check if `rustc` is in PATH or manually specify the location "
"by setting `Rust_COMPILER` to the path to `rustc`.")
_findrust_failed(${_ERROR_MESSAGE})
endif()
endif()
# Check if the discovered rustc is actually a "rustup" proxy.
execute_process(
COMMAND
${CMAKE_COMMAND} -E env
RUSTUP_FORCE_ARG0=rustup
"${_Rust_COMPILER_TEST}" --version
OUTPUT_VARIABLE _RUSTC_VERSION_RAW
ERROR_VARIABLE _RUSTC_VERSION_STDERR
RESULT_VARIABLE _RUSTC_VERSION_RESULT
)
if(NOT (_RUSTC_VERSION_RESULT EQUAL "0"))
_findrust_failed("`${_Rust_COMPILER_TEST} --version` failed with ${_RUSTC_VERSION_RESULT}\n"
"rustc stderr:\n${_RUSTC_VERSION_STDERR}"
)
endif()
if (_RUSTC_VERSION_RAW MATCHES "rustup [0-9\\.]+")
if (_USER_SPECIFIED_RUSTC)
message(
WARNING "User-specified Rust_COMPILER pointed to rustup's rustc proxy. Corrosion's "
"FindRust will always try to evaluate to an actual Rust toolchain, and so the "
"user-specified Rust_COMPILER will be discarded in favor of the default "
"rustup-managed toolchain."
)
unset(Rust_COMPILER)
unset(Rust_COMPILER CACHE)
endif()
set(_RESOLVE_RUSTUP_TOOLCHAINS ON)
# Get `rustup` next to the `rustc` proxy
get_filename_component(_RUST_PROXIES_PATH "${_Rust_COMPILER_TEST}" DIRECTORY)
find_program(Rust_RUSTUP rustup HINTS "${_RUST_PROXIES_PATH}" NO_DEFAULT_PATH)
endif()
unset(_Rust_COMPILER_TEST CACHE)
endif()
# At this point, the only thing we should have evaluated is a path to `rustup` _if that's what the
# best source for a Rust toolchain was determined to be_.
# List of user variables that will override any toolchain-provided setting
set(_Rust_USER_VARS Rust_COMPILER Rust_CARGO Rust_CARGO_TARGET Rust_CARGO_HOST_TARGET)
foreach(_VAR ${_Rust_USER_VARS})
if (DEFINED "${_VAR}")
set(${_VAR}_CACHED "${${_VAR}}" CACHE INTERNAL "Internal cache of ${_VAR}")
else()
unset(${_VAR}_CACHED CACHE)
endif()
endforeach()
# Discover what toolchains are installed by rustup, if the discovered `rustc` is a proxy from
# `rustup`, then select either the default toolchain, or the requested toolchain Rust_TOOLCHAIN
if (_RESOLVE_RUSTUP_TOOLCHAINS)
execute_process(
COMMAND
"${Rust_RUSTUP}" toolchain list --verbose
OUTPUT_VARIABLE _TOOLCHAINS_RAW
)
string(REPLACE "\n" ";" _TOOLCHAINS_RAW "${_TOOLCHAINS_RAW}")
set(_DISCOVERED_TOOLCHAINS "")
set(_DISCOVERED_TOOLCHAINS_RUSTC_PATH "")
set(_DISCOVERED_TOOLCHAINS_CARGO_PATH "")
set(_DISCOVERED_TOOLCHAINS_VERSION "")
foreach(_TOOLCHAIN_RAW ${_TOOLCHAINS_RAW})
if (_TOOLCHAIN_RAW MATCHES "([a-zA-Z0-9\\._\\-]+)[ \t\r\n]?(\\(default\\) \\(override\\)|\\(default\\)|\\(override\\))?[ \t\r\n]+(.+)")
set(_TOOLCHAIN "${CMAKE_MATCH_1}")
set(_TOOLCHAIN_TYPE "${CMAKE_MATCH_2}")
set(_TOOLCHAIN_PATH "${CMAKE_MATCH_3}")
set(_TOOLCHAIN_${_TOOLCHAIN}_PATH "${CMAKE_MATCH_3}")
if (_TOOLCHAIN_TYPE MATCHES ".*\\(default\\).*")
set(_TOOLCHAIN_DEFAULT "${_TOOLCHAIN}")
endif()
if (_TOOLCHAIN_TYPE MATCHES ".*\\(override\\).*")
set(_TOOLCHAIN_OVERRIDE "${_TOOLCHAIN}")
endif()
execute_process(
COMMAND
"${_TOOLCHAIN_PATH}/bin/rustc" --version
OUTPUT_VARIABLE _TOOLCHAIN_RAW_VERSION
)
if (_TOOLCHAIN_RAW_VERSION MATCHES "rustc ([0-9]+)\\.([0-9]+)\\.([0-9]+)(-nightly)?")
list(APPEND _DISCOVERED_TOOLCHAINS "${_TOOLCHAIN}")
list(APPEND _DISCOVERED_TOOLCHAINS_RUSTC_PATH "${_TOOLCHAIN_PATH}/bin/rustc")
list(APPEND _DISCOVERED_TOOLCHAINS_VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}")
# We need this variable to determine the default toolchain, since `foreach(... IN ZIP_LISTS ...)`
# requires CMake 3.17. As a workaround we define this variable to lookup the version when iterating
# through the `_DISCOVERED_TOOLCHAINS` lists.
set(_TOOLCHAIN_${_TOOLCHAIN}_VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}")
if(CMAKE_MATCH_4)
set(_TOOLCHAIN_${_TOOLCHAIN}_IS_NIGHTLY "TRUE")
else()
set(_TOOLCHAIN_${_TOOLCHAIN}_IS_NIGHTLY "FALSE")
endif()
if(EXISTS "${_TOOLCHAIN_PATH}/bin/cargo")
list(APPEND _DISCOVERED_TOOLCHAINS_CARGO_PATH "${_TOOLCHAIN_PATH}/bin/cargo")
else()
list(APPEND _DISCOVERED_TOOLCHAINS_CARGO_PATH "NOTFOUND")
endif()
else()
message(AUTHOR_WARNING "Unexpected output from `rustc --version` for Toolchain `${_TOOLCHAIN}`: "
"`${_TOOLCHAIN_RAW_VERSION}`.\n"
"Ignoring this toolchain."
)
endif()
else()
message(AUTHOR_WARNING "Didn't recognize toolchain: ${_TOOLCHAIN_RAW}. Ignoring this toolchain.\n"
"Rustup toolchain list output( `${Rust_RUSTUP} toolchain list --verbose`):\n"
"${_TOOLCHAINS_RAW}"
)
endif()
endforeach()
# Expose a list of available rustup toolchains.
list(LENGTH _DISCOVERED_TOOLCHAINS _toolchain_len)
list(LENGTH _DISCOVERED_TOOLCHAINS_RUSTC_PATH _toolchain_rustc_len)
list(LENGTH _DISCOVERED_TOOLCHAINS_CARGO_PATH _toolchain_cargo_len)
list(LENGTH _DISCOVERED_TOOLCHAINS_VERSION _toolchain_version_len)
if(NOT
(_toolchain_len EQUAL _toolchain_rustc_len
AND _toolchain_cargo_len EQUAL _toolchain_version_len
AND _toolchain_len EQUAL _toolchain_cargo_len)
)
message(FATAL_ERROR "Internal error - list length mismatch."
"List lengths: ${_toolchain_len} toolchains, ${_toolchain_rustc_len} rustc, ${_toolchain_cargo_len} cargo,"
" ${_toolchain_version_len} version. The lengths should be the same."
)
endif()
set(Rust_RUSTUP_TOOLCHAINS CACHE INTERNAL "List of available Rustup toolchains" "${_DISCOVERED_TOOLCHAINS}")
set(Rust_RUSTUP_TOOLCHAINS_RUSTC_PATH
CACHE INTERNAL
"List of the rustc paths corresponding to the toolchain at the same index in `Rust_RUSTUP_TOOLCHAINS`."
"${_DISCOVERED_TOOLCHAINS_RUSTC_PATH}"
)
set(Rust_RUSTUP_TOOLCHAINS_CARGO_PATH
CACHE INTERNAL
"List of the cargo paths corresponding to the toolchain at the same index in `Rust_RUSTUP_TOOLCHAINS`. \
May also be `NOTFOUND` if the toolchain does not have a cargo executable."
"${_DISCOVERED_TOOLCHAINS_CARGO_PATH}"
)
set(Rust_RUSTUP_TOOLCHAINS_VERSION
CACHE INTERNAL
"List of the rust toolchain version corresponding to the toolchain at the same index in \
`Rust_RUSTUP_TOOLCHAINS`."
"${_DISCOVERED_TOOLCHAINS_VERSION}"
)
# Rust_TOOLCHAIN is preferred over a requested version if it is set.
if (NOT DEFINED Rust_TOOLCHAIN)
if (NOT DEFINED _TOOLCHAIN_OVERRIDE)
set(_TOOLCHAIN_SELECTED "${_TOOLCHAIN_DEFAULT}")
else()
set(_TOOLCHAIN_SELECTED "${_TOOLCHAIN_OVERRIDE}")
endif()
# Check default toolchain first.
_findrust_version_ok("_TOOLCHAIN_${_TOOLCHAIN_SELECTED}_VERSION" _VERSION_OK)
if(NOT "${_VERSION_OK}")
foreach(_TOOLCHAIN "${_DISCOVERED_TOOLCHAINS}")
_findrust_version_ok("_TOOLCHAIN_${_TOOLCHAIN}_VERSION" _VERSION_OK)
if("${_VERSION_OK}")
set(_TOOLCHAIN_SELECTED "${_TOOLCHAIN}")
break()
endif()
endforeach()
# Check if we found a suitable version in the for loop.
if(NOT "${_VERSION_OK}")
string(REPLACE ";" "\n" _DISCOVERED_TOOLCHAINS "${_DISCOVERED_TOOLCHAINS}")
_findrust_failed("Failed to find a Rust toolchain matching the version requirements of "
"${Rust_FIND_VERSION}. Available toolchains: ${_DISCOVERED_TOOLCHAINS}")
endif()
endif()
endif()
set(Rust_TOOLCHAIN "${_TOOLCHAIN_SELECTED}" CACHE STRING "The rustup toolchain to use")
set_property(CACHE Rust_TOOLCHAIN PROPERTY STRINGS "${_DISCOVERED_TOOLCHAINS}")
if(NOT Rust_FIND_QUIETLY)
message(STATUS "Rust Toolchain: ${Rust_TOOLCHAIN}")
endif()
if (NOT Rust_TOOLCHAIN IN_LIST _DISCOVERED_TOOLCHAINS)
# If the precise toolchain wasn't found, try appending the default host
execute_process(
COMMAND
"${Rust_RUSTUP}" show
RESULT_VARIABLE _SHOW_RESULT
OUTPUT_VARIABLE _SHOW_RAW
)
if(NOT "${_SHOW_RESULT}" EQUAL "0")
_findrust_failed("Command `${Rust_RUSTUP} show` failed")
endif()
if (_SHOW_RAW MATCHES "Default host: ([a-zA-Z0-9_\\-]*)\n")
set(_DEFAULT_HOST "${CMAKE_MATCH_1}")
else()
_findrust_failed("Failed to parse \"Default host\" from `${Rust_RUSTUP} show`. Got: ${_SHOW_RAW}")
endif()
if (NOT "${Rust_TOOLCHAIN}-${_DEFAULT_HOST}" IN_LIST _DISCOVERED_TOOLCHAINS)
set(_NOT_FOUND_MESSAGE "Could not find toolchain '${Rust_TOOLCHAIN}'\n"
"Available toolchains:\n"
)
foreach(_TOOLCHAIN ${_DISCOVERED_TOOLCHAINS})
list(APPEND _NOT_FOUND_MESSAGE " `${_TOOLCHAIN}`\n")
endforeach()
_findrust_failed(${_NOT_FOUND_MESSAGE})
endif()
set(_RUSTUP_TOOLCHAIN_FULL "${Rust_TOOLCHAIN}-${_DEFAULT_HOST}")
else()
set(_RUSTUP_TOOLCHAIN_FULL "${Rust_TOOLCHAIN}")
endif()
set(_RUST_TOOLCHAIN_PATH "${_TOOLCHAIN_${_RUSTUP_TOOLCHAIN_FULL}_PATH}")
if(NOT "${Rust_FIND_QUIETLY}")
message(VERBOSE "Rust toolchain ${_RUSTUP_TOOLCHAIN_FULL}")
message(VERBOSE "Rust toolchain path ${_RUST_TOOLCHAIN_PATH}")
endif()
# Is overridden if the user specifies `Rust_COMPILER` explicitly.
find_program(
Rust_COMPILER_CACHED
rustc
HINTS "${_RUST_TOOLCHAIN_PATH}/bin"
NO_DEFAULT_PATH)
else()
find_program(Rust_COMPILER_CACHED rustc)
if (EXISTS "${Rust_COMPILER_CACHED}")
# rustc is expected to be at `<toolchain_path>/bin/rustc`.
get_filename_component(_RUST_TOOLCHAIN_PATH "${Rust_COMPILER_CACHED}" DIRECTORY)
get_filename_component(_RUST_TOOLCHAIN_PATH "${_RUST_TOOLCHAIN_PATH}" DIRECTORY)
endif()
endif()
if (NOT EXISTS "${Rust_COMPILER_CACHED}")
set(_NOT_FOUND_MESSAGE "The rustc executable was not found. "
"Rust not installed or ~/.cargo/bin not added to path?\n"
"Hint: Consider setting `Rust_COMPILER` to the absolute path of `rustc`."
)
_findrust_failed(${_NOT_FOUND_MESSAGE})
endif()
if (_RESOLVE_RUSTUP_TOOLCHAINS)
set(_NOT_FOUND_MESSAGE "Rust was detected to be managed by rustup, but failed to find `cargo` "
"next to `rustc` in `${_RUST_TOOLCHAIN_PATH}/bin`. This can happen for custom toolchains, "
"if cargo was not built. "
"Please manually specify the path to a compatible `cargo` by setting `Rust_CARGO`."
)
find_program(
Rust_CARGO_CACHED
cargo
HINTS "${_RUST_TOOLCHAIN_PATH}/bin"
NO_DEFAULT_PATH
)
# note: maybe can use find_package_handle_standard_args here, if we remove the _CACHED postfix.
# not sure why that is here...
if(NOT EXISTS "${Rust_CARGO_CACHED}")
_findrust_failed(${_NOT_FOUND_MESSAGE})
endif()
set(Rust_TOOLCHAIN_IS_RUSTUP_MANAGED TRUE CACHE INTERNAL "" FORCE)
else()
set(_NOT_FOUND_MESSAGE "Failed to find `cargo` in PATH and `${_RUST_TOOLCHAIN_PATH}/bin`.\n"
"Please ensure cargo is in PATH or manually specify the path to a compatible `cargo` by "
"setting `Rust_CARGO`."
)
# On some systems (e.g. NixOS) cargo is not managed by rustup and also not next to rustc.
find_program(
Rust_CARGO_CACHED
cargo
HINTS "${_RUST_TOOLCHAIN_PATH}/bin"
)
# note: maybe can use find_package_handle_standard_args here, if we remove the _CACHED postfix.
# not sure why that is here...
if(NOT EXISTS "${Rust_CARGO_CACHED}")
_findrust_failed(${_NOT_FOUND_MESSAGE})
endif()
endif()
execute_process(
COMMAND "${Rust_CARGO_CACHED}" --version --verbose
OUTPUT_VARIABLE _CARGO_VERSION_RAW
RESULT_VARIABLE _CARGO_VERSION_RESULT
)
# todo: check if cargo is a required component!
if(NOT ( "${_CARGO_VERSION_RESULT}" EQUAL "0" ))
_findrust_failed("Failed to get cargo version.\n"
"`${Rust_CARGO_CACHED} --version` failed with error: `${_CARGO_VERSION_RESULT}"
)
endif()
# todo: don't set cache variables here, but let find_package_handle_standard_args do the promotion
# later.
if (_CARGO_VERSION_RAW MATCHES "cargo ([0-9]+)\\.([0-9]+)\\.([0-9]+)")
set(Rust_CARGO_VERSION_MAJOR "${CMAKE_MATCH_1}" CACHE INTERNAL "" FORCE)
set(Rust_CARGO_VERSION_MINOR "${CMAKE_MATCH_2}" CACHE INTERNAL "" FORCE)
set(Rust_CARGO_VERSION_PATCH "${CMAKE_MATCH_3}" CACHE INTERNAL "" FORCE)
set(Rust_CARGO_VERSION "${Rust_CARGO_VERSION_MAJOR}.${Rust_CARGO_VERSION_MINOR}.${Rust_CARGO_VERSION_PATCH}" CACHE INTERNAL "" FORCE)
# Workaround for the version strings where the `cargo ` prefix is missing.
elseif(_CARGO_VERSION_RAW MATCHES "([0-9]+)\\.([0-9]+)\\.([0-9]+)")
set(Rust_CARGO_VERSION_MAJOR "${CMAKE_MATCH_1}" CACHE INTERNAL "" FORCE)
set(Rust_CARGO_VERSION_MINOR "${CMAKE_MATCH_2}" CACHE INTERNAL "" FORCE)
set(Rust_CARGO_VERSION_PATCH "${CMAKE_MATCH_3}" CACHE INTERNAL "" FORCE)
set(Rust_CARGO_VERSION "${Rust_CARGO_VERSION_MAJOR}.${Rust_CARGO_VERSION_MINOR}.${Rust_CARGO_VERSION_PATCH}" CACHE INTERNAL "" FORCE)
else()
_findrust_failed(
"Failed to parse cargo version. `cargo --version` evaluated to (${_CARGO_VERSION_RAW}). "
"Expected a <Major>.<Minor>.<Patch> version triple."
)
endif()
execute_process(
COMMAND "${Rust_COMPILER_CACHED}" --version --verbose
OUTPUT_VARIABLE _RUSTC_VERSION_RAW
RESULT_VARIABLE _RUSTC_VERSION_RESULT
)
if(NOT ( "${_RUSTC_VERSION_RESULT}" EQUAL "0" ))
_findrust_failed("Failed to get rustc version.\n"
"${Rust_COMPILER_CACHED} --version failed with error: `${_RUSTC_VERSION_RESULT}`")
endif()
if (_RUSTC_VERSION_RAW MATCHES "rustc ([0-9]+)\\.([0-9]+)\\.([0-9]+)(-nightly)?")
set(Rust_VERSION_MAJOR "${CMAKE_MATCH_1}" CACHE INTERNAL "" FORCE)
set(Rust_VERSION_MINOR "${CMAKE_MATCH_2}" CACHE INTERNAL "" FORCE)
set(Rust_VERSION_PATCH "${CMAKE_MATCH_3}" CACHE INTERNAL "" FORCE)
set(Rust_VERSION "${Rust_VERSION_MAJOR}.${Rust_VERSION_MINOR}.${Rust_VERSION_PATCH}" CACHE INTERNAL "" FORCE)
if(CMAKE_MATCH_4)
set(Rust_IS_NIGHTLY 1 CACHE INTERNAL "" FORCE)
else()
set(Rust_IS_NIGHTLY 0 CACHE INTERNAL "" FORCE)
endif()
else()
_findrust_failed("Failed to parse rustc version. `${Rust_COMPILER_CACHED} --version --verbose` "
"evaluated to:\n`${_RUSTC_VERSION_RAW}`"
)
endif()
if (_RUSTC_VERSION_RAW MATCHES "host: ([a-zA-Z0-9_\\-]*)\n")
set(Rust_DEFAULT_HOST_TARGET "${CMAKE_MATCH_1}")
set(Rust_CARGO_HOST_TARGET_CACHED "${Rust_DEFAULT_HOST_TARGET}" CACHE STRING "Host triple")
else()
_findrust_failed(
"Failed to parse rustc host target. `rustc --version --verbose` evaluated to:\n${_RUSTC_VERSION_RAW}"
)
endif()
if (_RUSTC_VERSION_RAW MATCHES "LLVM version: ([0-9]+)\\.([0-9]+)(\\.([0-9]+))?")
set(Rust_LLVM_VERSION_MAJOR "${CMAKE_MATCH_1}" CACHE INTERNAL "" FORCE)
set(Rust_LLVM_VERSION_MINOR "${CMAKE_MATCH_2}" CACHE INTERNAL "" FORCE)
# With the Rust toolchain 1.44.1 the reported LLVM version is 9.0, i.e. without a patch version.
# Since cmake regex does not support non-capturing groups, just ignore Match 3.
set(Rust_LLVM_VERSION_PATCH "${CMAKE_MATCH_4}" CACHE INTERNAL "" FORCE)
set(Rust_LLVM_VERSION "${Rust_LLVM_VERSION_MAJOR}.${Rust_LLVM_VERSION_MINOR}.${Rust_LLVM_VERSION_PATCH}" CACHE INTERNAL "" FORCE)
elseif(NOT Rust_FIND_QUIETLY)
message(
WARNING
"Failed to parse rustc LLVM version. `rustc --version --verbose` evaluated to:\n${_RUSTC_VERSION_RAW}"
)
endif()
if (NOT Rust_CARGO_TARGET_CACHED)
unset(_CARGO_ARCH)
unset(_CARGO_ABI)
if (WIN32)
if (CMAKE_VS_PLATFORM_NAME)
if ("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "Win32")
set(_CARGO_ARCH i686)
elseif("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "x64")
set(_CARGO_ARCH x86_64)
elseif("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "ARM64")
set(_CARGO_ARCH aarch64)
else()
message(WARNING "VS Platform '${CMAKE_VS_PLATFORM_NAME}' not recognized")
endif()
endif()
# Fallback path
if(NOT DEFINED _CARGO_ARCH)
# Possible values for windows when not cross-compiling taken from here:
# https://learn.microsoft.com/en-us/windows/win32/winprog64/wow64-implementation-details
# When cross-compiling the user is expected to supply the value, so we match more variants.
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(AMD64|amd64|x86_64)$")
set(_CARGO_ARCH x86_64)
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(ARM64|arm64|aarch64)$")
set(_CARGO_ARCH aarch64)
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(X86|x86|i686)$")
set(_CARGO_ARCH i686)
elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "i586")
set(_CARGO_ARCH i586)
elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "IA64")
message(FATAL_ERROR "No rust target for Intel Itanium.")
elseif(NOT "${CMAKE_SYSTEM_PROCESSOR}")
message(WARNING "Failed to detect target architecture. Please set `CMAKE_SYSTEM_PROCESSOR`"
" to your target architecture or set `Rust_CARGO_TARGET` to your cargo target triple."
)
else()
message(WARNING "Failed to detect target architecture. Please set "
"`Rust_CARGO_TARGET` to your cargo target triple."
)
endif()
endif()
set(_CARGO_VENDOR "pc-windows")
# The MSVC Generators will always target the msvc ABI.
# For other generators we check the compiler ID and compiler target (if present)
# If no compiler is set and we are not cross-compiling then we just choose the
# default rust host target.
if(DEFINED MSVC
OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC"
OR "${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC"
OR "${CMAKE_CXX_COMPILER_TARGET}" MATCHES "-msvc$"
OR "${CMAKE_C_COMPILER_TARGET}" MATCHES "-msvc$"
)
set(_CARGO_ABI msvc)
elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU"
OR "${CMAKE_C_COMPILER_ID}" STREQUAL "GNU"
OR "${CMAKE_CXX_COMPILER_TARGET}" MATCHES "-gnu$"
OR "${CMAKE_C_COMPILER_TARGET}" MATCHES "-gnu$"
OR (NOT CMAKE_CROSSCOMPILING AND "${Rust_DEFAULT_HOST_TARGET}" MATCHES "-gnu$")
)
set(_CARGO_ABI gnu)
elseif(NOT "${CMAKE_CROSSCOMPILING}" AND "${Rust_DEFAULT_HOST_TARGET}" MATCHES "-msvc$")
# We first check if the gnu branch matches to ensure this fallback is only used
# if no compiler is enabled.
set(_CARGO_ABI msvc)
else()
message(WARNING "Could not determine the target ABI. Please specify `Rust_CARGO_TARGET` manually.")
endif()
if(DEFINED _CARGO_ARCH AND DEFINED _CARGO_VENDOR AND DEFINED _CARGO_ABI)
set(Rust_CARGO_TARGET_CACHED "${_CARGO_ARCH}-${_CARGO_VENDOR}-${_CARGO_ABI}"
CACHE STRING "Target triple")
endif()
elseif (ANDROID)
if (CMAKE_ANDROID_ARCH_ABI STREQUAL armeabi-v7a)
if (CMAKE_ANDROID_ARM_MODE)
set(_Rust_ANDROID_TARGET armv7-linux-androideabi)
else ()
set(_Rust_ANDROID_TARGET thumbv7neon-linux-androideabi)
endif()
elseif (CMAKE_ANDROID_ARCH_ABI STREQUAL arm64-v8a)
set(_Rust_ANDROID_TARGET aarch64-linux-android)
elseif (CMAKE_ANDROID_ARCH_ABI STREQUAL x86)
set(_Rust_ANDROID_TARGET i686-linux-android)
elseif (CMAKE_ANDROID_ARCH_ABI STREQUAL x86_64)
set(_Rust_ANDROID_TARGET x86_64-linux-android)
endif()
if (_Rust_ANDROID_TARGET)
set(Rust_CARGO_TARGET_CACHED "${_Rust_ANDROID_TARGET}" CACHE STRING "Target triple")
endif()
endif()
# Fallback to the default host target
if(NOT Rust_CARGO_TARGET_CACHED)
if(CMAKE_CROSSCOMPILING)
message(WARNING "CMake is in cross-compiling mode, but the cargo target-triple could not be inferred."
"Falling back to the default host target. Please consider manually setting `Rust_CARGO_TARGET`."
)
endif()
set(Rust_CARGO_TARGET_CACHED "${Rust_DEFAULT_HOST_TARGET}" CACHE STRING "Target triple")
endif()
message(STATUS "Rust Target: ${Rust_CARGO_TARGET_CACHED}")
endif()
if(Rust_CARGO_TARGET_CACHED STREQUAL Rust_DEFAULT_HOST_TARGET)
set(Rust_CROSSCOMPILING FALSE CACHE INTERNAL "Rust is configured for cross-compiling")
else()
set(Rust_CROSSCOMPILING TRUE CACHE INTERNAL "Rust is configured for cross-compiling")
endif()
_corrosion_parse_target_triple("${Rust_CARGO_TARGET_CACHED}" rust_arch rust_vendor rust_os rust_env)
_corrosion_determine_libs("${rust_arch}" "${rust_vendor}" "${rust_os}" "${rust_env}" rust_libs)
set(Rust_CARGO_TARGET_ARCH "${rust_arch}" CACHE INTERNAL "Target architecture")
set(Rust_CARGO_TARGET_VENDOR "${rust_vendor}" CACHE INTERNAL "Target vendor")
set(Rust_CARGO_TARGET_OS "${rust_os}" CACHE INTERNAL "Target Operating System")
set(Rust_CARGO_TARGET_ENV "${rust_env}" CACHE INTERNAL "Target environment")
set(Rust_CARGO_TARGET_LINK_NATIVE_LIBS "${rust_libs}" CACHE INTERNAL
"Required native libraries when linking Rust static libraries")
# Set the input variables as non-cache variables so that the variables are available after
# `find_package`, even if the values were evaluated to defaults.
foreach(_VAR ${_Rust_USER_VARS})
set(${_VAR} "${${_VAR}_CACHED}")
# Ensure cached variables have type INTERNAL
set(${_VAR}_CACHED "${${_VAR}_CACHED}" CACHE INTERNAL "Internal cache of ${_VAR}")
endforeach()
find_package_handle_standard_args(
Rust
REQUIRED_VARS Rust_COMPILER Rust_VERSION Rust_CARGO Rust_CARGO_VERSION Rust_CARGO_TARGET Rust_CARGO_HOST_TARGET
VERSION_VAR Rust_VERSION
)
if(NOT TARGET Rust::Rustc)
add_executable(Rust::Rustc IMPORTED GLOBAL)
set_property(
TARGET Rust::Rustc
PROPERTY IMPORTED_LOCATION "${Rust_COMPILER_CACHED}"
)
add_executable(Rust::Cargo IMPORTED GLOBAL)
set_property(
TARGET Rust::Cargo
PROPERTY IMPORTED_LOCATION "${Rust_CARGO_CACHED}"
)
set(Rust_FOUND true)
endif()

184
Externals/corrosion/generator/Cargo.lock generated vendored Normal file
View File

@@ -0,0 +1,184 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "camino"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c530edf18f37068ac2d977409ed5cd50d53d73bc653c7647b48eb78976ac9ae2"
dependencies = [
"serde",
]
[[package]]
name = "cargo-platform"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27"
dependencies = [
"serde",
]
[[package]]
name = "cargo_metadata"
version = "0.15.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08a1ec454bc3eead8719cb56e15dbbfecdbc14e4b3a3ae4936cc6e31f5fc0d07"
dependencies = [
"camino",
"cargo-platform",
"semver",
"serde",
"serde_json",
"thiserror",
]
[[package]]
name = "clap"
version = "2.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
dependencies = [
"bitflags",
"textwrap",
"unicode-width",
]
[[package]]
name = "corrosion-generator"
version = "0.1.0"
dependencies = [
"cargo_metadata",
"clap",
"serde",
"thiserror",
]
[[package]]
name = "itoa"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
[[package]]
name = "proc-macro2"
version = "1.0.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d0e1ae9e836cc3beddd63db0df682593d7e2d3d891ae8c9083d2113e1744224"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
dependencies = [
"proc-macro2",
]
[[package]]
name = "ryu"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
[[package]]
name = "semver"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
dependencies = [
"serde",
]
[[package]]
name = "serde"
version = "1.0.156"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "314b5b092c0ade17c00142951e50ced110ec27cea304b1037c6969246c2469a4"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.156"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7e29c4601e36bcec74a223228dce795f4cd3616341a4af93520ca1a837c087d"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width",
]
[[package]]
name = "thiserror"
version = "1.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-ident"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
[[package]]
name = "unicode-width"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"

View File

@@ -0,0 +1,23 @@
[package]
name = "corrosion-generator"
version = "0.1.0"
authors = ["Andrew Gaspar <andrew.gaspar@outlook.com>"]
license = "MIT"
edition = "2018"
[dependencies]
cargo_metadata = "0.15"
# The crates below are indirect dependencies of cargo metadata,
# We explicitly specify maximum versions to allow building the generator
# with older toolchains.
# Version 1.0.157 upgrades to syn 2.0 and raises MSRV to 1.56
serde = { version = ">=1, <1.0.157", default-features=false }
# Version 1.0.40 upgrades to syn 2.0 and raises MSRV to 1.56
thiserror = { version = ">=1, <1.0.40", default-features=false }
[dependencies.clap]
version = "2.34"
default-features = false
# Make sure this crate still compiles while it is checked out
# in a sub-directory of a repository that has a Cargo.toml.
[workspace]

View File

@@ -0,0 +1,95 @@
use std::path::PathBuf;
use cargo_metadata::Metadata;
use clap::{App, Arg};
mod subcommands {
pub mod gen_cmake;
}
use subcommands::*;
// common options
const MANIFEST_PATH: &str = "manifest-path";
const CARGO_EXECUTABLE: &str = "cargo-executable";
const VERBOSE: &str = "verbose";
const LOCKED: &str = "locked";
const FROZEN: &str = "frozen";
pub struct GeneratorSharedArgs {
pub manifest_path: PathBuf,
pub cargo_executable: PathBuf,
pub metadata: Metadata,
pub verbose: bool,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let matches = App::new("CMake Generator for Cargo")
.version("0.1")
.author("Andrew Gaspar <andrew.gaspar@outlook.com>")
.about("Generates CMake files for Cargo projects")
.arg(
Arg::with_name(MANIFEST_PATH)
.long("manifest-path")
.value_name("Cargo.toml")
.help("Specifies the target Cargo project")
.required(true)
.takes_value(true),
)
.arg(
Arg::with_name(CARGO_EXECUTABLE)
.long("cargo")
.value_name("EXECUTABLE")
.required(true)
.help("Path to the cargo executable to use"),
)
.arg(
Arg::with_name(VERBOSE)
.long("verbose")
.help("Request verbose output"),
)
.arg(
Arg::with_name(LOCKED)
.long("locked")
.help("Pass --locked to cargo invocations"),
)
.arg(
Arg::with_name(FROZEN)
.long("frozen")
.help("Pass --frozen to cargo invocations"),
)
.subcommand(gen_cmake::subcommand())
.get_matches();
let mut cmd = cargo_metadata::MetadataCommand::new();
cmd.no_deps();
if matches.is_present(LOCKED) {
cmd.other_options(["--locked".into()]);
}
if matches.is_present(FROZEN) {
cmd.other_options(["--frozen".into()]);
}
let manifest_path = matches.value_of(MANIFEST_PATH).unwrap();
let cargo_executable = matches.value_of(CARGO_EXECUTABLE).unwrap();
cmd.manifest_path(manifest_path);
cmd.cargo_path(cargo_executable);
let metadata = cmd.exec()?;
let shared_args = GeneratorSharedArgs {
manifest_path: manifest_path.into(),
cargo_executable: cargo_executable.into(),
metadata,
verbose: matches.is_present(VERBOSE),
};
match matches.subcommand() {
(gen_cmake::GEN_CMAKE, Some(matches)) => gen_cmake::invoke(&shared_args, matches)?,
_ => unreachable!(),
};
// We should never reach this statement
std::process::exit(1);
}

View File

@@ -0,0 +1,160 @@
use std::{
fs::{create_dir_all, File},
io::{stdout, Write},
path::Path,
rc::Rc,
};
use clap::{App, Arg, ArgMatches, SubCommand};
mod target;
// Command name
pub const GEN_CMAKE: &str = "gen-cmake";
// Options
const OUT_FILE: &str = "out-file";
const CONFIGURATION_ROOT: &str = "configuration-root";
const CRATES: &str = "crates";
const IMPORTED_CRATES: &str = "imported-crates";
const CRATE_TYPE: &str = "crate-type";
const PASSTHROUGH_ADD_CARGO_BUILD: &str = "passthrough-acb";
pub fn subcommand() -> App<'static, 'static> {
SubCommand::with_name(GEN_CMAKE)
.arg(
Arg::with_name(CONFIGURATION_ROOT)
.long("configuration-root")
.value_name("DIRECTORY")
.takes_value(true)
.help(
"Specifies a root directory for configuration folders. E.g. Win32 \
in VS Generator.",
),
)
.arg(
Arg::with_name(CRATES)
.long("crates")
.value_name("crates")
.takes_value(true)
.multiple(true)
.require_delimiter(true)
.help("Specifies which crates of the workspace to import"),
)
.arg(
Arg::with_name(CRATE_TYPE)
.long(CRATE_TYPE)
.value_name("kind")
.possible_values(&["staticlib", "cdylib", "bin"])
.multiple(true)
.value_delimiter(";")
.help("Only import the specified crate types")
)
.arg(
Arg::with_name(OUT_FILE)
.short("o")
.long("out-file")
.value_name("FILE")
.help("Output CMake file name. Defaults to stdout."),
)
.arg(
Arg::with_name(IMPORTED_CRATES)
.long(IMPORTED_CRATES)
.value_name("variable_name")
.takes_value(true)
.help("Save a list of the imported target names into c CMake variable with the given name"),
)
.arg(
Arg::with_name(PASSTHROUGH_ADD_CARGO_BUILD)
.long(PASSTHROUGH_ADD_CARGO_BUILD)
.takes_value(true)
.multiple(true)
.value_delimiter(std::char::from_u32(0x1f).unwrap().to_string().as_str())
.help("Passthrough arguments to the _add_cargo_build invocation(s) in CMake")
)
}
pub fn invoke(
args: &crate::GeneratorSharedArgs,
matches: &ArgMatches,
) -> Result<(), Box<dyn std::error::Error>> {
let mut out_file: Box<dyn Write> = if let Some(path) = matches.value_of(OUT_FILE) {
let path = Path::new(path);
if let Some(parent) = path.parent() {
create_dir_all(parent).expect("Failed to create directory!");
}
let file = File::create(path).expect("Unable to open out-file!");
Box::new(file)
} else {
Box::new(stdout())
};
writeln!(
out_file,
"\
cmake_minimum_required(VERSION 3.15)
"
)?;
let crates = matches
.values_of(CRATES)
.map_or(Vec::new(), |c| c.collect());
let crate_kinds: Option<Vec<&str>> = matches.values_of(CRATE_TYPE).map(|c| c.collect());
let workspace_manifest_path = Rc::new(args.manifest_path.clone());
let targets: Vec<_> = args
.metadata
.packages
.iter()
.filter(|p| {
args.metadata.workspace_members.contains(&p.id)
&& (crates.is_empty() || crates.contains(&p.name.as_str()))
})
.cloned()
.map(Rc::new)
.flat_map(|package| {
package
.targets
.iter()
.filter_map(|t| {
target::CargoTarget::from_metadata(
package.clone(),
t.clone(),
workspace_manifest_path.clone(),
&crate_kinds,
)
})
.collect::<Vec<_>>()
})
.collect();
let passthrough_args: Vec<String> = matches
.values_of(PASSTHROUGH_ADD_CARGO_BUILD)
.map(|values| {
// Add quotes around each argument for CMake to preserve which arguments belong together.
values
.filter(|val| !val.is_empty())
.map(|val| format!("\"{}\"", val))
.collect()
})
.unwrap_or_default();
let passthrough_str = passthrough_args.join(" ");
for target in &targets {
target
.emit_cmake_target(&mut out_file, &passthrough_str)
.unwrap();
}
if let Some(imported_crate_list_name) = matches.value_of(IMPORTED_CRATES) {
let imported_targets: Vec<_> = targets.iter().map(|target| target.target_name()).collect();
let imported_targets_list = imported_targets.join(";");
writeln!(
out_file,
"set({} \"{}\")",
imported_crate_list_name, imported_targets_list
)?;
}
writeln!(out_file)?;
std::process::exit(0);
}

View File

@@ -0,0 +1,216 @@
use std::error::Error;
use std::path::PathBuf;
use std::rc::Rc;
#[derive(Clone)]
pub enum CargoTargetType {
Executable,
Library {
has_staticlib: bool,
has_cdylib: bool,
},
}
#[derive(Clone)]
pub struct CargoTarget {
cargo_package: Rc<cargo_metadata::Package>,
cargo_target: cargo_metadata::Target,
target_type: CargoTargetType,
workspace_manifest_path: Rc<PathBuf>,
}
impl CargoTargetType {
fn to_string(&self) -> String {
let mut s = String::new();
match self {
Self::Executable => {
s.push_str("bin");
}
Self::Library {
has_staticlib,
has_cdylib,
} => {
if *has_staticlib {
s.push_str("staticlib")
}
if *has_cdylib {
s.push_str(" cdylib")
}
}
}
s
}
}
impl CargoTarget {
pub fn from_metadata(
cargo_package: Rc<cargo_metadata::Package>,
cargo_target: cargo_metadata::Target,
workspace_manifest_path: Rc<PathBuf>,
// If Some, only import crates if the kind variant is given in crate_kinds.
crate_kinds: &Option<Vec<&str>>,
) -> Option<Self> {
let filtered_kinds: Vec<String> = cargo_target
.kind
.clone()
.into_iter()
.filter(|kind| match crate_kinds {
None => true,
Some(allowed_kinds_subset) => allowed_kinds_subset.contains(&&**kind),
})
.collect();
let target_type = if filtered_kinds
.iter()
.any(|k| k.as_str() == "staticlib" || k.as_str() == "cdylib")
{
CargoTargetType::Library {
has_staticlib: filtered_kinds.iter().any(|k| k == "staticlib"),
has_cdylib: filtered_kinds.iter().any(|k| k == "cdylib"),
}
} else if filtered_kinds.iter().any(|k| k == "bin") {
CargoTargetType::Executable
} else {
return None;
};
Some(Self {
cargo_package,
cargo_target,
target_type,
workspace_manifest_path,
})
}
pub(crate) fn target_name(&self) -> &str {
&self.cargo_target.name
}
pub fn emit_cmake_target(
&self,
out_file: &mut dyn std::io::Write,
passthrough_add_cargo_build: &str,
) -> Result<(), Box<dyn Error>> {
writeln!(
out_file,
"set(byproducts \"\")
set(cargo_build_out_dir \"\")
set(archive_byproducts \"\")
set(shared_lib_byproduct \"\")
set(pdb_byproduct \"\")
set(bin_byproduct \"\")
"
)?;
let ws_manifest = self
.workspace_manifest_path
.to_str()
.expect("Non-utf8 path encountered")
.replace("\\", "/");
match self.target_type {
CargoTargetType::Library {
has_staticlib,
has_cdylib,
} => {
assert!(has_staticlib || has_cdylib);
let ws_manifest = self
.workspace_manifest_path
.to_str()
.expect("Non-utf8 path encountered")
.replace("\\", "/");
let mut lib_kinds = if has_staticlib { "staticlib" } else { "" }.to_string();
if has_cdylib {
if has_staticlib {
lib_kinds.push(' ');
}
lib_kinds.push_str("cdylib")
}
writeln!(
out_file,
"
_corrosion_add_library_target(
WORKSPACE_MANIFEST_PATH \"{workspace_manifest_path}\"
TARGET_NAME \"{target_name}\"
LIB_KINDS {lib_kinds}
OUT_ARCHIVE_OUTPUT_BYPRODUCTS archive_byproducts
OUT_SHARED_LIB_BYPRODUCTS shared_lib_byproduct
OUT_PDB_BYPRODUCT pdb_byproduct
)
list(APPEND byproducts
\"${{archive_byproducts}}\"
\"${{shared_lib_byproduct}}\"
\"${{pdb_byproduct}}\"
)
",
workspace_manifest_path = ws_manifest,
target_name = self.cargo_target.name,
lib_kinds = lib_kinds,
)?;
}
CargoTargetType::Executable => {
writeln!(
out_file,
"
_corrosion_add_bin_target(\"{workspace_manifest_path}\" \"{target_name}\"
bin_byproduct pdb_byproduct
)
set(byproducts \"\")
list(APPEND byproducts \"${{bin_byproduct}}\" \"${{pdb_byproduct}}\")
",
workspace_manifest_path = ws_manifest,
target_name = self.cargo_target.name,
)?;
}
};
let target_kinds = self.target_type.to_string();
writeln!(out_file,
"
set(cargo_build_out_dir \"\")
_add_cargo_build(
cargo_build_out_dir
PACKAGE \"{package_name}\"
TARGET \"{target_name}\"
MANIFEST_PATH \"{package_manifest_path}\"
WORKSPACE_MANIFEST_PATH \"{workspace_manifest_path}\"
TARGET_KINDS {target_kinds}
BYPRODUCTS \"${{byproducts}}\"
{passthrough_add_cargo_build}
)
set_target_properties({target_name} PROPERTIES
INTERFACE_COR_PACKAGE_MANIFEST_PATH \"{package_manifest_path}\"
)
if(archive_byproducts)
_corrosion_copy_byproducts(
{target_name} ARCHIVE_OUTPUT_DIRECTORY \"${{cargo_build_out_dir}}\" \"${{archive_byproducts}}\"
)
endif()
if(shared_lib_byproduct)
_corrosion_copy_byproducts(
{target_name} LIBRARY_OUTPUT_DIRECTORY \"${{cargo_build_out_dir}}\" \"${{shared_lib_byproduct}}\"
)
endif()
if(pdb_byproduct)
_corrosion_copy_byproducts(
{target_name} PDB_OUTPUT_DIRECTORY \"${{cargo_build_out_dir}}\" \"${{pdb_byproduct}}\"
)
endif()
if(bin_byproduct)
_corrosion_copy_byproducts(
{target_name} RUNTIME_OUTPUT_DIRECTORY \"${{cargo_build_out_dir}}\" \"${{bin_byproduct}}\"
)
endif()
",
package_name = self.cargo_package.name,
target_name = self.cargo_target.name,
package_manifest_path = self.cargo_package.manifest_path.as_str().replace("\\", "/"),
workspace_manifest_path = ws_manifest,
target_kinds = target_kinds,
passthrough_add_cargo_build = passthrough_add_cargo_build,
)?;
Ok(())
}
}

View File

@@ -22,6 +22,11 @@ Please read the [FAQ](https://dolphin-emu.org/docs/faq/) before using Dolphin.
* A reasonably modern graphics card (Direct3D 10.0 / OpenGL 3.0).
* A graphics card that supports Direct3D 11 / OpenGL 4.4 is recommended.
## General Requirements
This fork includes a [Rust submodule](Externals/slippi-rust-extensions) that needs to be built and linked to the final executable.
This means that you will need to install a Rust compiler for your current system; to do this, simply visit
[rustup.rs](https://rustup.rs). Once installed, both CMake and Visual Studio should be able to automatically handle the rest for you.
## Building for Windows
Open the solution file `Source/Dolphin.sln` to build Dolphin on Windows using [Visual Studio 2019](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&rel=16).
Other compilers might be

View File

@@ -30,6 +30,12 @@ std::unique_ptr<SoundStream> g_sound_stream;
static bool s_audio_dump_start = false;
// SlippiChange: Added as a hook for the Jukebox to call.
int AudioCommonGetCurrentVolume()
{
return SConfig::GetInstance().m_IsMuted ? 0 : SConfig::GetInstance().m_Volume;
}
namespace AudioCommon
{
static const int AUDIO_VOLUME_MIN = 0;

View File

@@ -13,6 +13,16 @@ class CMixer;
extern std::unique_ptr<SoundStream> g_sound_stream;
// SlippiChange: Added as a hook for the Jukebox to call.
//
// I've intentionally kept this outside of the C++ namespace as I am unsure of the risks
// of having this inside there with regards to how Rust receives it.
//
// If someone can prove that it's safe, then feel free to move it back into the namespace
// proper. For now, I've just prefixed the method name with the namespace name
// for grep-ability.
int AudioCommonGetCurrentVolume();
namespace AudioCommon
{
void InitSoundStream(void* hwnd);

View File

@@ -193,7 +193,16 @@ void CMixer::MixerFifo::PushSamples(const s16* samples, u32 num_samples)
// Check if we have enough free space
// indexW == m_indexR results in empty buffer, so indexR must always be smaller than indexW
if (num_samples * 2 + ((current_write_index - m_read_index.load()) & INDEX_MASK) >= MAX_SAMPLES * 2)
{
// @TODO: We would ideally like to be able to push Jukebox audio samples through Dolphin's mixer,
// however attempts at doing so seem to conflict with some expected logic regarding sample submission.
//
// For whoever chooses to try and debug this, you may want to uncomment the following line to examine
// why some samples get dropped and not pushed into the buffer.
// NOTICE_LOG(AUDIO, "PushSamples exiting early");
return;
}
// AyuanX: Actual re-sampling work has been moved to sound thread
// to alleviate the workload on main thread
// convert to float while copying to buffer
@@ -353,4 +362,4 @@ void CMixer::StopLogDSPAudio()
unsigned int CMixer::MixerFifo::GetInputSampleRate() const
{
return m_input_sample_rate;
}
}

View File

@@ -1,3 +1,5 @@
include_directories(${CMAKE_SOURCE_DIR}/Externals/SlippiRustExtensions/ffi/includes)
set(SRCS Analytics.cpp
BreakPoints.cpp
CDUtils.cpp

View File

@@ -1,259 +1,259 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="DebugFast|x64">
<Configuration>DebugFast</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="ReleasePlayback|x64">
<Configuration>ReleasePlayback</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{C87A4178-44F6-49B2-B7AA-C79AF1B8C534}</ProjectGuid>
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='ReleasePlayback'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='DebugFast'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\..\VSProps\Base.props" />
<Import Project="..\..\VSProps\PCHUse.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleasePlayback|x64'">
<ClCompile>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>IS_PLAYBACK;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'">
<ClCompile>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="Align.h" />
<ClInclude Include="AllocationMap.h" />
<ClInclude Include="Analytics.h" />
<ClInclude Include="Assert.h" />
<ClInclude Include="Atomic.h" />
<ClInclude Include="Atomic_GCC.h" />
<ClInclude Include="Atomic_Win32.h" />
<ClInclude Include="BitField.h" />
<ClInclude Include="BitHelpers.h" />
<ClInclude Include="BitSet.h" />
<ClInclude Include="BlockingLoop.h" />
<ClInclude Include="BreakPoints.h" />
<ClInclude Include="CDUtils.h" />
<ClInclude Include="ChunkFile.h" />
<ClInclude Include="CodeBlock.h" />
<ClInclude Include="ColorUtil.h" />
<ClInclude Include="Common.h" />
<ClInclude Include="CommonFuncs.h" />
<ClInclude Include="CommonPaths.h" />
<ClInclude Include="CommonTypes.h" />
<ClInclude Include="ConstantBuffer.h" />
<ClInclude Include="CPUDetect.h" />
<ClInclude Include="DebugInterface.h" />
<ClInclude Include="ENetUtil.h" />
<ClInclude Include="Event.h" />
<ClInclude Include="FifoQueue.h" />
<ClInclude Include="FileSearch.h" />
<ClInclude Include="FileUtil.h" />
<ClInclude Include="FixedSizeQueue.h" />
<ClInclude Include="Flag.h" />
<ClInclude Include="FPURoundMode.h" />
<ClInclude Include="GekkoDisassembler.h" />
<ClInclude Include="GL\GLExtensions\AMD_pinned_memory.h" />
<ClInclude Include="GL\GLExtensions\ARB_blend_func_extended.h" />
<ClInclude Include="GL\GLExtensions\ARB_buffer_storage.h" />
<ClInclude Include="GL\GLExtensions\ARB_clip_control.h" />
<ClInclude Include="GL\GLExtensions\ARB_compute_shader.h" />
<ClInclude Include="GL\GLExtensions\ARB_copy_image.h" />
<ClInclude Include="GL\GLExtensions\ARB_debug_output.h" />
<ClInclude Include="GL\GLExtensions\ARB_draw_elements_base_vertex.h" />
<ClInclude Include="GL\GLExtensions\ARB_ES2_compatibility.h" />
<ClInclude Include="GL\GLExtensions\ARB_ES3_compatibility.h" />
<ClInclude Include="GL\GLExtensions\ARB_framebuffer_object.h" />
<ClInclude Include="GL\GLExtensions\ARB_get_program_binary.h" />
<ClInclude Include="GL\GLExtensions\ARB_map_buffer_range.h" />
<ClInclude Include="GL\GLExtensions\ARB_occlusion_query2.h" />
<ClInclude Include="GL\GLExtensions\ARB_sampler_objects.h" />
<ClInclude Include="GL\GLExtensions\ARB_sample_shading.h" />
<ClInclude Include="GL\GLExtensions\ARB_shader_storage_buffer_object.h" />
<ClInclude Include="GL\GLExtensions\ARB_sync.h" />
<ClInclude Include="GL\GLExtensions\ARB_texture_multisample.h" />
<ClInclude Include="GL\GLExtensions\ARB_texture_storage.h" />
<ClInclude Include="GL\GLExtensions\ARB_texture_storage_multisample.h" />
<ClInclude Include="GL\GLExtensions\ARB_uniform_buffer_object.h" />
<ClInclude Include="GL\GLExtensions\ARB_vertex_array_object.h" />
<ClInclude Include="GL\GLExtensions\ARB_viewport_array.h" />
<ClInclude Include="GL\GLExtensions\EXT_texture_compression_s3tc.h" />
<ClInclude Include="GL\GLExtensions\EXT_texture_filter_anisotropic.h" />
<ClInclude Include="GL\GLExtensions\GLExtensions.h" />
<ClInclude Include="GL\GLExtensions\gl_1_1.h" />
<ClInclude Include="GL\GLExtensions\gl_1_2.h" />
<ClInclude Include="GL\GLExtensions\gl_1_3.h" />
<ClInclude Include="GL\GLExtensions\gl_1_4.h" />
<ClInclude Include="GL\GLExtensions\gl_1_5.h" />
<ClInclude Include="GL\GLExtensions\gl_2_0.h" />
<ClInclude Include="GL\GLExtensions\gl_2_1.h" />
<ClInclude Include="GL\GLExtensions\gl_3_0.h" />
<ClInclude Include="GL\GLExtensions\gl_3_1.h" />
<ClInclude Include="GL\GLExtensions\gl_3_2.h" />
<ClInclude Include="GL\GLExtensions\gl_4_2.h" />
<ClInclude Include="GL\GLExtensions\gl_4_3.h" />
<ClInclude Include="GL\GLExtensions\gl_4_4.h" />
<ClInclude Include="GL\GLExtensions\gl_4_5.h" />
<ClInclude Include="GL\GLExtensions\gl_common.h" />
<ClInclude Include="GL\GLExtensions\HP_occlusion_test.h" />
<ClInclude Include="GL\GLExtensions\KHR_debug.h" />
<ClInclude Include="GL\GLExtensions\NV_depth_buffer_float.h" />
<ClInclude Include="GL\GLExtensions\NV_occlusion_query_samples.h" />
<ClInclude Include="GL\GLExtensions\NV_primitive_restart.h" />
<ClInclude Include="GL\GLInterfaceBase.h" />
<ClInclude Include="GL\GLInterface\WGL.h" />
<ClInclude Include="GL\GLUtil.h" />
<ClInclude Include="Hash.h" />
<ClInclude Include="IniFile.h" />
<ClInclude Include="JitRegister.h" />
<ClInclude Include="LinearDiskCache.h" />
<ClInclude Include="MathUtil.h" />
<ClInclude Include="MD5.h" />
<ClInclude Include="MemArena.h" />
<ClInclude Include="MemoryUtil.h" />
<ClInclude Include="MsgHandler.h" />
<ClInclude Include="NandPaths.h" />
<ClInclude Include="Network.h" />
<ClInclude Include="NonCopyable.h" />
<ClInclude Include="PcapFile.h" />
<ClInclude Include="Profiler.h" />
<ClInclude Include="ScopeGuard.h" />
<ClInclude Include="SDCardUtil.h" />
<ClInclude Include="SettingsHandler.h" />
<ClInclude Include="StringUtil.h" />
<ClInclude Include="SymbolDB.h" />
<ClInclude Include="SysConf.h" />
<ClInclude Include="Thread.h" />
<ClInclude Include="ThreadPool.h" />
<ClInclude Include="Timer.h" />
<ClInclude Include="TraversalClient.h" />
<ClInclude Include="TraversalProto.h" />
<ClInclude Include="x64ABI.h" />
<ClInclude Include="x64Analyzer.h" />
<ClInclude Include="x64Emitter.h" />
<ClInclude Include="x64Reg.h" />
<ClInclude Include="Crypto\bn.h" />
<ClInclude Include="Crypto\ec.h" />
<ClInclude Include="Logging\ConsoleListener.h" />
<ClInclude Include="Logging\Log.h" />
<ClInclude Include="Logging\LogManager.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="Analytics.cpp" />
<ClCompile Include="BreakPoints.cpp" />
<ClCompile Include="CDUtils.cpp" />
<ClCompile Include="ColorUtil.cpp" />
<ClCompile Include="ENetUtil.cpp" />
<ClCompile Include="FileSearch.cpp" />
<ClCompile Include="FileUtil.cpp" />
<ClCompile Include="GekkoDisassembler.cpp" />
<ClCompile Include="GL\GLExtensions\GLExtensions.cpp" />
<ClCompile Include="GL\GLInterface\GLInterface.cpp" />
<ClCompile Include="GL\GLInterface\WGL.cpp" />
<ClCompile Include="GL\GLUtil.cpp" />
<ClCompile Include="Hash.cpp" />
<ClCompile Include="IniFile.cpp" />
<ClCompile Include="JitRegister.cpp" />
<ClCompile Include="Logging\ConsoleListenerWin.cpp" />
<ClCompile Include="MathUtil.cpp" />
<ClCompile Include="MD5.cpp" />
<ClCompile Include="MemArena.cpp" />
<ClCompile Include="MemoryUtil.cpp" />
<ClCompile Include="Misc.cpp" />
<ClCompile Include="MsgHandler.cpp" />
<ClCompile Include="NandPaths.cpp" />
<ClCompile Include="Network.cpp" />
<ClCompile Include="PcapFile.cpp" />
<ClCompile Include="Profiler.cpp" />
<ClCompile Include="SDCardUtil.cpp" />
<ClCompile Include="SettingsHandler.cpp" />
<ClCompile Include="StringUtil.cpp" />
<ClCompile Include="SymbolDB.cpp" />
<ClCompile Include="SysConf.cpp" />
<ClCompile Include="Thread.cpp" />
<ClCompile Include="ThreadPool.cpp" />
<ClCompile Include="Timer.cpp" />
<ClCompile Include="TraversalClient.cpp" />
<ClCompile Include="ucrtFreadWorkaround.cpp" />
<ClCompile Include="Version.cpp" />
<ClCompile Include="x64ABI.cpp" />
<ClCompile Include="x64Analyzer.cpp" />
<ClCompile Include="x64CPUDetect.cpp" />
<ClCompile Include="x64Emitter.cpp" />
<ClCompile Include="x64FPURoundMode.cpp" />
<ClCompile Include="Crypto\bn.cpp" />
<ClCompile Include="Crypto\ec.cpp" />
<ClCompile Include="Logging\LogManager.cpp" />
</ItemGroup>
<ItemGroup>
<Text Include="CMakeLists.txt" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(ExternalsDir)enet\enet.vcxproj">
<Project>{cbc76802-c128-4b17-bf6c-23b08c313e5e}</Project>
</ProjectReference>
<ProjectReference Include="$(ExternalsDir)mbedtls\mbedTLS.vcxproj">
<Project>{bdb6578b-0691-4e80-a46c-df21639fd3b8}</Project>
</ProjectReference>
<ProjectReference Include="$(ExternalsDir)xxhash\xxhash.vcxproj">
<Project>{677ea016-1182-440c-9345-dc88d1e98c0c}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\Externals\curl\curl.vcxproj">
<Project>{bb00605c-125f-4a21-b33b-7bf418322dcb}</Project>
</ProjectReference>
<ProjectReference Include="SCMRevGen.vcxproj">
<Project>{41279555-f94f-4ebc-99de-af863c10c5c4}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Natvis Include="BitField.natvis" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="DebugFast|x64">
<Configuration>DebugFast</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="ReleasePlayback|x64">
<Configuration>ReleasePlayback</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{C87A4178-44F6-49B2-B7AA-C79AF1B8C534}</ProjectGuid>
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='ReleasePlayback'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='DebugFast'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\..\VSProps\Base.props" />
<Import Project="..\..\VSProps\PCHUse.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<AdditionalIncludeDirectories>$(ExternalsDir)SlippiRustExtensions/ffi/includes;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleasePlayback|x64'">
<ClCompile>
<AdditionalIncludeDirectories>$(ExternalsDir)SlippiRustExtensions/ffi/includes;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>IS_PLAYBACK;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'">
<ClCompile>
<AdditionalIncludeDirectories>$(ExternalsDir)SlippiRustExtensions/ffi/includes;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="Align.h" />
<ClInclude Include="AllocationMap.h" />
<ClInclude Include="Analytics.h" />
<ClInclude Include="Assert.h" />
<ClInclude Include="Atomic.h" />
<ClInclude Include="Atomic_GCC.h" />
<ClInclude Include="Atomic_Win32.h" />
<ClInclude Include="BitField.h" />
<ClInclude Include="BitHelpers.h" />
<ClInclude Include="BitSet.h" />
<ClInclude Include="BlockingLoop.h" />
<ClInclude Include="BreakPoints.h" />
<ClInclude Include="CDUtils.h" />
<ClInclude Include="ChunkFile.h" />
<ClInclude Include="CodeBlock.h" />
<ClInclude Include="ColorUtil.h" />
<ClInclude Include="Common.h" />
<ClInclude Include="CommonFuncs.h" />
<ClInclude Include="CommonPaths.h" />
<ClInclude Include="CommonTypes.h" />
<ClInclude Include="ConstantBuffer.h" />
<ClInclude Include="CPUDetect.h" />
<ClInclude Include="DebugInterface.h" />
<ClInclude Include="ENetUtil.h" />
<ClInclude Include="Event.h" />
<ClInclude Include="FifoQueue.h" />
<ClInclude Include="FileSearch.h" />
<ClInclude Include="FileUtil.h" />
<ClInclude Include="FixedSizeQueue.h" />
<ClInclude Include="Flag.h" />
<ClInclude Include="FPURoundMode.h" />
<ClInclude Include="GekkoDisassembler.h" />
<ClInclude Include="GL\GLExtensions\AMD_pinned_memory.h" />
<ClInclude Include="GL\GLExtensions\ARB_blend_func_extended.h" />
<ClInclude Include="GL\GLExtensions\ARB_buffer_storage.h" />
<ClInclude Include="GL\GLExtensions\ARB_clip_control.h" />
<ClInclude Include="GL\GLExtensions\ARB_compute_shader.h" />
<ClInclude Include="GL\GLExtensions\ARB_copy_image.h" />
<ClInclude Include="GL\GLExtensions\ARB_debug_output.h" />
<ClInclude Include="GL\GLExtensions\ARB_draw_elements_base_vertex.h" />
<ClInclude Include="GL\GLExtensions\ARB_ES2_compatibility.h" />
<ClInclude Include="GL\GLExtensions\ARB_ES3_compatibility.h" />
<ClInclude Include="GL\GLExtensions\ARB_framebuffer_object.h" />
<ClInclude Include="GL\GLExtensions\ARB_get_program_binary.h" />
<ClInclude Include="GL\GLExtensions\ARB_map_buffer_range.h" />
<ClInclude Include="GL\GLExtensions\ARB_occlusion_query2.h" />
<ClInclude Include="GL\GLExtensions\ARB_sampler_objects.h" />
<ClInclude Include="GL\GLExtensions\ARB_sample_shading.h" />
<ClInclude Include="GL\GLExtensions\ARB_shader_storage_buffer_object.h" />
<ClInclude Include="GL\GLExtensions\ARB_sync.h" />
<ClInclude Include="GL\GLExtensions\ARB_texture_multisample.h" />
<ClInclude Include="GL\GLExtensions\ARB_texture_storage.h" />
<ClInclude Include="GL\GLExtensions\ARB_texture_storage_multisample.h" />
<ClInclude Include="GL\GLExtensions\ARB_uniform_buffer_object.h" />
<ClInclude Include="GL\GLExtensions\ARB_vertex_array_object.h" />
<ClInclude Include="GL\GLExtensions\ARB_viewport_array.h" />
<ClInclude Include="GL\GLExtensions\EXT_texture_compression_s3tc.h" />
<ClInclude Include="GL\GLExtensions\EXT_texture_filter_anisotropic.h" />
<ClInclude Include="GL\GLExtensions\GLExtensions.h" />
<ClInclude Include="GL\GLExtensions\gl_1_1.h" />
<ClInclude Include="GL\GLExtensions\gl_1_2.h" />
<ClInclude Include="GL\GLExtensions\gl_1_3.h" />
<ClInclude Include="GL\GLExtensions\gl_1_4.h" />
<ClInclude Include="GL\GLExtensions\gl_1_5.h" />
<ClInclude Include="GL\GLExtensions\gl_2_0.h" />
<ClInclude Include="GL\GLExtensions\gl_2_1.h" />
<ClInclude Include="GL\GLExtensions\gl_3_0.h" />
<ClInclude Include="GL\GLExtensions\gl_3_1.h" />
<ClInclude Include="GL\GLExtensions\gl_3_2.h" />
<ClInclude Include="GL\GLExtensions\gl_4_2.h" />
<ClInclude Include="GL\GLExtensions\gl_4_3.h" />
<ClInclude Include="GL\GLExtensions\gl_4_4.h" />
<ClInclude Include="GL\GLExtensions\gl_4_5.h" />
<ClInclude Include="GL\GLExtensions\gl_common.h" />
<ClInclude Include="GL\GLExtensions\HP_occlusion_test.h" />
<ClInclude Include="GL\GLExtensions\KHR_debug.h" />
<ClInclude Include="GL\GLExtensions\NV_depth_buffer_float.h" />
<ClInclude Include="GL\GLExtensions\NV_occlusion_query_samples.h" />
<ClInclude Include="GL\GLExtensions\NV_primitive_restart.h" />
<ClInclude Include="GL\GLInterfaceBase.h" />
<ClInclude Include="GL\GLInterface\WGL.h" />
<ClInclude Include="GL\GLUtil.h" />
<ClInclude Include="Hash.h" />
<ClInclude Include="IniFile.h" />
<ClInclude Include="JitRegister.h" />
<ClInclude Include="LinearDiskCache.h" />
<ClInclude Include="MathUtil.h" />
<ClInclude Include="MD5.h" />
<ClInclude Include="MemArena.h" />
<ClInclude Include="MemoryUtil.h" />
<ClInclude Include="MsgHandler.h" />
<ClInclude Include="NandPaths.h" />
<ClInclude Include="Network.h" />
<ClInclude Include="NonCopyable.h" />
<ClInclude Include="PcapFile.h" />
<ClInclude Include="Profiler.h" />
<ClInclude Include="ScopeGuard.h" />
<ClInclude Include="SDCardUtil.h" />
<ClInclude Include="SettingsHandler.h" />
<ClInclude Include="StringUtil.h" />
<ClInclude Include="SymbolDB.h" />
<ClInclude Include="SysConf.h" />
<ClInclude Include="Thread.h" />
<ClInclude Include="ThreadPool.h" />
<ClInclude Include="Timer.h" />
<ClInclude Include="TraversalClient.h" />
<ClInclude Include="TraversalProto.h" />
<ClInclude Include="x64ABI.h" />
<ClInclude Include="x64Analyzer.h" />
<ClInclude Include="x64Emitter.h" />
<ClInclude Include="x64Reg.h" />
<ClInclude Include="Crypto\bn.h" />
<ClInclude Include="Crypto\ec.h" />
<ClInclude Include="Logging\ConsoleListener.h" />
<ClInclude Include="Logging\Log.h" />
<ClInclude Include="Logging\LogManager.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="Analytics.cpp" />
<ClCompile Include="BreakPoints.cpp" />
<ClCompile Include="CDUtils.cpp" />
<ClCompile Include="ColorUtil.cpp" />
<ClCompile Include="ENetUtil.cpp" />
<ClCompile Include="FileSearch.cpp" />
<ClCompile Include="FileUtil.cpp" />
<ClCompile Include="GekkoDisassembler.cpp" />
<ClCompile Include="GL\GLExtensions\GLExtensions.cpp" />
<ClCompile Include="GL\GLInterface\GLInterface.cpp" />
<ClCompile Include="GL\GLInterface\WGL.cpp" />
<ClCompile Include="GL\GLUtil.cpp" />
<ClCompile Include="Hash.cpp" />
<ClCompile Include="IniFile.cpp" />
<ClCompile Include="JitRegister.cpp" />
<ClCompile Include="Logging\ConsoleListenerWin.cpp" />
<ClCompile Include="MathUtil.cpp" />
<ClCompile Include="MD5.cpp" />
<ClCompile Include="MemArena.cpp" />
<ClCompile Include="MemoryUtil.cpp" />
<ClCompile Include="Misc.cpp" />
<ClCompile Include="MsgHandler.cpp" />
<ClCompile Include="NandPaths.cpp" />
<ClCompile Include="Network.cpp" />
<ClCompile Include="PcapFile.cpp" />
<ClCompile Include="Profiler.cpp" />
<ClCompile Include="SDCardUtil.cpp" />
<ClCompile Include="SettingsHandler.cpp" />
<ClCompile Include="StringUtil.cpp" />
<ClCompile Include="SymbolDB.cpp" />
<ClCompile Include="SysConf.cpp" />
<ClCompile Include="Thread.cpp" />
<ClCompile Include="ThreadPool.cpp" />
<ClCompile Include="Timer.cpp" />
<ClCompile Include="TraversalClient.cpp" />
<ClCompile Include="ucrtFreadWorkaround.cpp" />
<ClCompile Include="Version.cpp" />
<ClCompile Include="x64ABI.cpp" />
<ClCompile Include="x64Analyzer.cpp" />
<ClCompile Include="x64CPUDetect.cpp" />
<ClCompile Include="x64Emitter.cpp" />
<ClCompile Include="x64FPURoundMode.cpp" />
<ClCompile Include="Crypto\bn.cpp" />
<ClCompile Include="Crypto\ec.cpp" />
<ClCompile Include="Logging\LogManager.cpp" />
</ItemGroup>
<ItemGroup>
<Text Include="CMakeLists.txt" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(ExternalsDir)enet\enet.vcxproj">
<Project>{cbc76802-c128-4b17-bf6c-23b08c313e5e}</Project>
</ProjectReference>
<ProjectReference Include="$(ExternalsDir)mbedtls\mbedTLS.vcxproj">
<Project>{bdb6578b-0691-4e80-a46c-df21639fd3b8}</Project>
</ProjectReference>
<ProjectReference Include="$(ExternalsDir)xxhash\xxhash.vcxproj">
<Project>{677ea016-1182-440c-9345-dc88d1e98c0c}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\Externals\curl\curl.vcxproj">
<Project>{bb00605c-125f-4a21-b33b-7bf418322dcb}</Project>
</ProjectReference>
<ProjectReference Include="SCMRevGen.vcxproj">
<Project>{41279555-f94f-4ebc-99de-af863c10c5c4}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Natvis Include="BitField.natvis" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@@ -40,6 +40,9 @@ enum LOG_TYPE
SERIALINTERFACE,
SLIPPI,
SLIPPI_ONLINE,
SLIPPI_RUST_DEPENDENCIES,
SLIPPI_RUST_EXI,
SLIPPI_RUST_JUKEBOX,
SP1,
VIDEO,
VIDEOINTERFACE,
@@ -73,6 +76,22 @@ static const char LOG_LEVEL_TO_CHAR[7] = "-NEWID";
} // namespace
// A "simple" logger that doesn't actually do any formatting at all, simply
// taking in a `level`, `file` name, `line` number, and the `msg` to log.
//
// The reason for this is that we now have some modules written in other languages
// that need to hop the boundary for logging, and we explicitly *do not* want to
// attempt to deal with who owns what and/or how to format with variable-length args
// from another language. Offering this just makes logging easier on some
// platforms (e.g, Windows). We also avoid needing to expose the LOG_LEVELS enum into
// other languages by just accepting an integer, which is shuffled into the correct type
// in the implementation.
//
// If you are calling this from another language and you need to log any form of structured
// data, you should do the formatting *on your side* and pass it over here. You are also
// responsible for ensuring that the msg string lives for an appropriate lifetime.
void SlippiRustLogger(int level, int slp_log_type, const char* file, int line, const char *msg);
void GenericLog(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char* file, int line,
const char* fmt, ...)
#ifdef __GNUC__

View File

@@ -20,6 +20,8 @@
#include "Common/StringUtil.h"
#include "Common/Timer.h"
#include "SlippiRustExtensions.h"
void GenericLog(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char* file, int line,
const char* fmt, ...)
{
@@ -30,6 +32,16 @@ void GenericLog(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char*
va_end(args);
}
// See the notes in the header definition for why this exists.
void SlippiRustExtensionsLogger(int level, int slp_log_type, const char *msg)
{
LogTypes::LOG_LEVELS log_level = static_cast<LogTypes::LOG_LEVELS>(level);
LogTypes::LOG_TYPE log_type = static_cast<LogTypes::LOG_TYPE>(slp_log_type);
if (LogManager::GetInstance())
LogManager::GetInstance()->LogPreformatted(log_level, log_type, msg);
}
LogManager* LogManager::m_logManager = nullptr;
static size_t DeterminePathCutOffPoint()
@@ -53,55 +65,86 @@ static size_t DeterminePathCutOffPoint()
LogManager::LogManager()
{
// create log containers
m_Log[LogTypes::ACTIONREPLAY] = new LogContainer("ActionReplay", "ActionReplay");
m_Log[LogTypes::AUDIO] = new LogContainer("Audio", "Audio Emulator");
m_Log[LogTypes::AUDIO_INTERFACE] = new LogContainer("AI", "Audio Interface (AI)");
m_Log[LogTypes::BOOT] = new LogContainer("BOOT", "Boot");
m_Log[LogTypes::COMMANDPROCESSOR] = new LogContainer("CP", "CommandProc");
m_Log[LogTypes::COMMON] = new LogContainer("COMMON", "Common");
m_Log[LogTypes::CONSOLE] = new LogContainer("CONSOLE", "Dolphin Console");
m_Log[LogTypes::DISCIO] = new LogContainer("DIO", "Disc IO");
m_Log[LogTypes::DSPHLE] = new LogContainer("DSPHLE", "DSP HLE");
m_Log[LogTypes::DSPLLE] = new LogContainer("DSPLLE", "DSP LLE");
m_Log[LogTypes::DSP_MAIL] = new LogContainer("DSPMails", "DSP Mails");
m_Log[LogTypes::DSPINTERFACE] = new LogContainer("DSP", "DSPInterface");
m_Log[LogTypes::DVDINTERFACE] = new LogContainer("DVD", "DVD Interface");
m_Log[LogTypes::DYNA_REC] = new LogContainer("JIT", "Dynamic Recompiler");
m_Log[LogTypes::EXPANSIONINTERFACE] = new LogContainer("EXI", "Expansion Interface");
m_Log[LogTypes::SLIPPI] = new LogContainer("SLIPPI", "Slippi");
m_Log[LogTypes::SLIPPI_ONLINE] = new LogContainer("SLIPPI_ONLINE", "Slippi Online");
m_Log[LogTypes::FILEMON] = new LogContainer("FileMon", "File Monitor");
m_Log[LogTypes::GDB_STUB] = new LogContainer("GDB_STUB", "GDB Stub");
m_Log[LogTypes::GPFIFO] = new LogContainer("GP", "GPFifo");
m_Log[LogTypes::HOST_GPU] = new LogContainer("Host GPU", "Host GPU");
m_Log[LogTypes::MASTER_LOG] = new LogContainer("*", "Master Log");
m_Log[LogTypes::MEMCARD_MANAGER] = new LogContainer("MemCard Manager", "MemCard Manager");
m_Log[LogTypes::MEMMAP] = new LogContainer("MI", "MI & memmap");
m_Log[LogTypes::NETPLAY] = new LogContainer("NETPLAY", "Netplay");
m_Log[LogTypes::OSHLE] = new LogContainer("HLE", "HLE");
m_Log[LogTypes::OSREPORT] = new LogContainer("OSREPORT", "OSReport");
m_Log[LogTypes::PAD] = new LogContainer("PAD", "Pad");
m_Log[LogTypes::PIXELENGINE] = new LogContainer("PE", "PixelEngine");
m_Log[LogTypes::PROCESSORINTERFACE] = new LogContainer("PI", "ProcessorInt");
m_Log[LogTypes::POWERPC] = new LogContainer("PowerPC", "IBM CPU");
m_Log[LogTypes::SERIALINTERFACE] = new LogContainer("SI", "Serial Interface (SI)");
m_Log[LogTypes::SP1] = new LogContainer("SP1", "Serial Port 1");
m_Log[LogTypes::VIDEO] = new LogContainer("Video", "Video Backend");
m_Log[LogTypes::VIDEOINTERFACE] = new LogContainer("VI", "Video Interface (VI)");
m_Log[LogTypes::WIIMOTE] = new LogContainer("Wiimote", "Wiimote");
m_Log[LogTypes::WII_IPC] = new LogContainer("WII_IPC", "WII IPC");
m_Log[LogTypes::WII_IPC_DVD] = new LogContainer("WII_IPC_DVD", "WII IPC DVD");
m_Log[LogTypes::WII_IPC_ES] = new LogContainer("WII_IPC_ES", "WII IPC ES");
m_Log[LogTypes::WII_IPC_FILEIO] = new LogContainer("WII_IPC_FILEIO", "WII IPC FILEIO");
m_Log[LogTypes::WII_IPC_HID] = new LogContainer("WII_IPC_HID", "WII IPC HID");
m_Log[LogTypes::WII_IPC_HLE] = new LogContainer("WII_IPC_HLE", "WII IPC HLE");
m_Log[LogTypes::WII_IPC_SD] = new LogContainer("WII_IPC_SD", "WII IPC SD");
m_Log[LogTypes::WII_IPC_SSL] = new LogContainer("WII_IPC_SSL", "WII IPC SSL");
m_Log[LogTypes::WII_IPC_STM] = new LogContainer("WII_IPC_STM", "WII IPC STM");
m_Log[LogTypes::WII_IPC_NET] = new LogContainer("WII_IPC_NET", "WII IPC NET");
m_Log[LogTypes::WII_IPC_WC24] = new LogContainer("WII_IPC_WC24", "WII IPC WC24");
m_Log[LogTypes::WII_IPC_WIIMOTE] = new LogContainer("WII_IPC_WIIMOTE", "WII IPC WIIMOTE");
// We want this called before we create any `LogContainer`s below that may register with the Rust
// side of things.
slprs_logging_init(SlippiRustExtensionsLogger);
// create log containers
m_Log[LogTypes::ACTIONREPLAY] = new LogContainer("ActionReplay", "ActionReplay", LogTypes::ACTIONREPLAY);
m_Log[LogTypes::AUDIO] = new LogContainer("Audio", "Audio Emulator", LogTypes::AUDIO);
m_Log[LogTypes::AUDIO_INTERFACE] = new LogContainer("AI", "Audio Interface (AI)", LogTypes::AUDIO_INTERFACE);
m_Log[LogTypes::BOOT] = new LogContainer("BOOT", "Boot", LogTypes::BOOT);
m_Log[LogTypes::COMMANDPROCESSOR] = new LogContainer("CP", "CommandProc", LogTypes::COMMANDPROCESSOR);
m_Log[LogTypes::COMMON] = new LogContainer("COMMON", "Common", LogTypes::COMMON);
m_Log[LogTypes::CONSOLE] = new LogContainer("CONSOLE", "Dolphin Console", LogTypes::CONSOLE);
m_Log[LogTypes::DISCIO] = new LogContainer("DIO", "Disc IO", LogTypes::DISCIO);
m_Log[LogTypes::DSPHLE] = new LogContainer("DSPHLE", "DSP HLE", LogTypes::DSPHLE);
m_Log[LogTypes::DSPLLE] = new LogContainer("DSPLLE", "DSP LLE", LogTypes::DSPLLE);
m_Log[LogTypes::DSP_MAIL] = new LogContainer("DSPMails", "DSP Mails", LogTypes::DSP_MAIL);
m_Log[LogTypes::DSPINTERFACE] = new LogContainer("DSP", "DSPInterface", LogTypes::DSPINTERFACE);
m_Log[LogTypes::DVDINTERFACE] = new LogContainer("DVD", "DVD Interface", LogTypes::DVDINTERFACE);
m_Log[LogTypes::DYNA_REC] = new LogContainer("JIT", "Dynamic Recompiler", LogTypes::DYNA_REC);
m_Log[LogTypes::EXPANSIONINTERFACE] = new LogContainer("EXI", "Expansion Interface", LogTypes::EXPANSIONINTERFACE);
m_Log[LogTypes::SLIPPI] = new LogContainer("SLIPPI", "Slippi", LogTypes::SLIPPI);
m_Log[LogTypes::SLIPPI_ONLINE] = new LogContainer("SLIPPI_ONLINE", "Slippi Online", LogTypes::SLIPPI_ONLINE);
// This LogContainer will register with the Rust side under the "SLIPPI_RUST_DEPENDENCIES" target.
// This is intended to be a catch-all for situations where we want to inspect logs from dependencies
// we pull in.
m_Log[LogTypes::SLIPPI_RUST_DEPENDENCIES] = new LogContainer(
"SLIPPI_RUST_DEPENDENCIES",
"[Rust] Slippi Dependencies",
LogTypes::SLIPPI_RUST_DEPENDENCIES,
true
);
// This LogContainer will register with the Rust side under the "SLIPPI_RUST_EXI" target.
m_Log[LogTypes::SLIPPI_RUST_EXI] = new LogContainer(
"SLIPPI_RUST_EXI",
"[Rust] Slippi EXI",
LogTypes::SLIPPI_RUST_EXI,
true
);
// This LogContainer will register with the Rust side under the "SLIPPI_RUST_JUKEBOX" target.
m_Log[LogTypes::SLIPPI_RUST_JUKEBOX] = new LogContainer(
"SLIPPI_RUST_JUKEBOX",
"[Rust] Slippi Jukebox",
LogTypes::SLIPPI_RUST_JUKEBOX,
true
);
m_Log[LogTypes::FILEMON] = new LogContainer("FileMon", "File Monitor", LogTypes::FILEMON);
m_Log[LogTypes::GDB_STUB] = new LogContainer("GDB_STUB", "GDB Stub", LogTypes::GDB_STUB);
m_Log[LogTypes::GPFIFO] = new LogContainer("GP", "GPFifo", LogTypes::GPFIFO);
m_Log[LogTypes::HOST_GPU] = new LogContainer("Host GPU", "Host GPU", LogTypes::HOST_GPU);
m_Log[LogTypes::MASTER_LOG] = new LogContainer("*", "Master Log", LogTypes::MASTER_LOG);
m_Log[LogTypes::MEMCARD_MANAGER] = new LogContainer("MemCard Manager", "MemCard Manager", LogTypes::MEMCARD_MANAGER);
m_Log[LogTypes::MEMMAP] = new LogContainer("MI", "MI & memmap", LogTypes::MEMMAP);
m_Log[LogTypes::NETPLAY] = new LogContainer("NETPLAY", "Netplay", LogTypes::NETPLAY);
m_Log[LogTypes::OSHLE] = new LogContainer("HLE", "HLE", LogTypes::OSHLE);
m_Log[LogTypes::OSREPORT] = new LogContainer("OSREPORT", "OSReport", LogTypes::OSREPORT);
m_Log[LogTypes::PAD] = new LogContainer("PAD", "Pad", LogTypes::PAD);
m_Log[LogTypes::PIXELENGINE] = new LogContainer("PE", "PixelEngine", LogTypes::PIXELENGINE);
m_Log[LogTypes::PROCESSORINTERFACE] = new LogContainer("PI", "ProcessorInt", LogTypes::PROCESSORINTERFACE);
m_Log[LogTypes::POWERPC] = new LogContainer("PowerPC", "IBM CPU", LogTypes::POWERPC);
m_Log[LogTypes::SERIALINTERFACE] = new LogContainer("SI", "Serial Interface (SI)", LogTypes::SERIALINTERFACE);
m_Log[LogTypes::SP1] = new LogContainer("SP1", "Serial Port 1", LogTypes::SP1);
m_Log[LogTypes::VIDEO] = new LogContainer("Video", "Video Backend", LogTypes::VIDEO);
m_Log[LogTypes::VIDEOINTERFACE] = new LogContainer("VI", "Video Interface (VI)", LogTypes::VIDEOINTERFACE);
m_Log[LogTypes::WIIMOTE] = new LogContainer("Wiimote", "Wiimote", LogTypes::WIIMOTE);
m_Log[LogTypes::WII_IPC] = new LogContainer("WII_IPC", "WII IPC", LogTypes::WII_IPC);
m_Log[LogTypes::WII_IPC_DVD] = new LogContainer("WII_IPC_DVD", "WII IPC DVD", LogTypes::WII_IPC_DVD);
m_Log[LogTypes::WII_IPC_ES] = new LogContainer("WII_IPC_ES", "WII IPC ES", LogTypes::WII_IPC_ES);
m_Log[LogTypes::WII_IPC_FILEIO] = new LogContainer("WII_IPC_FILEIO", "WII IPC FILEIO", LogTypes::WII_IPC_FILEIO);
m_Log[LogTypes::WII_IPC_HID] = new LogContainer("WII_IPC_HID", "WII IPC HID", LogTypes::WII_IPC_HID);
m_Log[LogTypes::WII_IPC_HLE] = new LogContainer("WII_IPC_HLE", "WII IPC HLE", LogTypes::WII_IPC_HLE);
m_Log[LogTypes::WII_IPC_SD] = new LogContainer("WII_IPC_SD", "WII IPC SD", LogTypes::WII_IPC_SD);
m_Log[LogTypes::WII_IPC_SSL] = new LogContainer("WII_IPC_SSL", "WII IPC SSL", LogTypes::WII_IPC_SSL);
m_Log[LogTypes::WII_IPC_STM] = new LogContainer("WII_IPC_STM", "WII IPC STM", LogTypes::WII_IPC_STM);
m_Log[LogTypes::WII_IPC_NET] = new LogContainer("WII_IPC_NET", "WII IPC NET", LogTypes::WII_IPC_NET);
m_Log[LogTypes::WII_IPC_WC24] = new LogContainer("WII_IPC_WC24", "WII IPC WC24", LogTypes::WII_IPC_WC24);
m_Log[LogTypes::WII_IPC_WIIMOTE] = new LogContainer("WII_IPC_WIIMOTE", "WII IPC WIIMOTE", LogTypes::WII_IPC_WIIMOTE);
RegisterListener(LogListener::FILE_LISTENER,
new FileLogListener(File::GetUserPath(F_MAINLOG_IDX)));
@@ -140,6 +183,23 @@ LogManager::~LogManager()
delete m_listeners[LogListener::FILE_LISTENER];
}
// Extensions that need to log across the boundary often have to allocate
// an owned String on their side; if they can vend us a c_str then we can avoid
// duplicating the allocation over here for the logger.
//
// The alternative here would be opening up `m_log` and `m_listeners` to be public,
// but this feels like it'll transplant easier onto mainline.
void LogManager::LogPreformatted(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char* msg)
{
LogContainer* log = m_Log[type];
if (!log->IsEnabled() || level > log->GetLevel() || !log->HasListeners())
return;
for (auto listener_id : *log)
m_listeners[listener_id]->Log(level, msg);
}
void LogManager::Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char* file,
int line, const char* format, va_list args)
{
@@ -172,9 +232,38 @@ void LogManager::Shutdown()
m_logManager = nullptr;
}
LogContainer::LogContainer(const std::string& shortName, const std::string& fullName, bool enable)
: m_fullName(fullName), m_shortName(shortName), m_enable(enable), m_level(LogTypes::LWARNING)
LogContainer::LogContainer(
const std::string& shortName,
const std::string& fullName,
LogTypes::LOG_TYPE logtype,
bool isRustLog,
bool enable
) : m_fullName(fullName), m_shortName(shortName), m_logtype(logtype), m_isRustLog(isRustLog), m_enable(enable), m_level(LogTypes::LWARNING)
{
if(m_isRustLog)
{
slprs_logging_register_container(m_shortName.c_str(), m_logtype, m_enable, m_level);
}
}
void LogContainer::SetEnable(bool enable)
{
m_enable = enable;
if(m_isRustLog)
{
slprs_logging_update_container(m_shortName.c_str(), m_enable, m_level);
}
}
void LogContainer::SetLevel(LogTypes::LOG_LEVELS level)
{
m_level = level;
if(m_isRustLog)
{
slprs_logging_update_container(m_shortName.c_str(), m_enable, m_level);
}
}
FileLogListener::FileLogListener(const std::string& filename)

View File

@@ -55,18 +55,24 @@ private:
class LogContainer
{
public:
LogContainer(const std::string& shortName, const std::string& fullName, bool enable = false);
LogContainer(
const std::string& shortName,
const std::string& fullName,
LogTypes::LOG_TYPE logtype,
bool isRustLog = false,
bool enable = false
);
std::string GetShortName() const { return m_shortName; }
std::string GetFullName() const { return m_fullName; }
void AddListener(LogListener::LISTENER id) { m_listener_ids[id] = 1; }
void AddListener(LogListener::LISTENER id) { m_listener_ids[id] = 1; }
void RemoveListener(LogListener::LISTENER id) { m_listener_ids[id] = 0; }
void Trigger(LogTypes::LOG_LEVELS, const char* msg);
bool IsEnabled() const { return m_enable; }
void SetEnable(bool enable) { m_enable = enable; }
void SetEnable(bool enable);// { m_enable = enable; }
LogTypes::LOG_LEVELS GetLevel() const { return m_level; }
void SetLevel(LogTypes::LOG_LEVELS level) { m_level = level; }
void SetLevel(LogTypes::LOG_LEVELS level);// { m_level = level; }
bool HasListeners() const { return bool(m_listener_ids); }
typedef class BitSet32::Iterator iterator;
iterator begin() const { return m_listener_ids.begin(); }
@@ -74,6 +80,8 @@ public:
private:
std::string m_fullName;
std::string m_shortName;
LogTypes::LOG_TYPE m_logtype;
bool m_isRustLog;
bool m_enable;
LogTypes::LOG_LEVELS m_level;
BitSet32 m_listener_ids;
@@ -94,6 +102,7 @@ private:
public:
static u32 GetMaxLevel() { return MAX_LOGLEVEL; }
void LogPreformatted(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char* msg);
void Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char* file, int line,
const char* fmt, va_list args);

View File

@@ -1,3 +1,5 @@
include_directories(${CMAKE_SOURCE_DIR}/Externals/SlippiRustExtensions/ffi/includes)
set(SRCS ActionReplay.cpp
Analytics.cpp
ARDecrypt.cpp

View File

@@ -279,6 +279,7 @@ void SConfig::SaveCoreSettings(IniFile &ini)
core->Set("RSHACK", bRSHACK);
core->Set("Latency", iLatency);
core->Set("ReduceTimingDispersion", bReduceTimingDispersion);
core->Set("SlippiJukeboxEnabled", bSlippiJukeboxEnabled);
core->Set("SlippiOnlineDelay", m_slippiOnlineDelay);
core->Set("SlippiEnableSpectator", m_enableSpectator);
core->Set("SlippiSpectatorLocalPort", m_spectator_local_port);
@@ -620,6 +621,7 @@ void SConfig::LoadCoreSettings(IniFile &ini)
core->Get("RSHACK", &bRSHACK, false);
core->Get("Latency", &iLatency, 0);
core->Get("ReduceTimingDispersion", &bReduceTimingDispersion, false);
core->Get("SlippiJukeboxEnabled", &bSlippiJukeboxEnabled, true);
core->Get("SlippiEnableSpectator", &m_enableSpectator, true);
core->Get("SlippiSpectatorLocalPort", &m_spectator_local_port, 51441);
core->Get("SlippiOnlineDelay", &m_slippiOnlineDelay, 2);

View File

@@ -150,6 +150,7 @@ struct SConfig : NonCopyable
bool bAdapterWarning = true;
bool bReduceTimingDispersion = false;
bool bSlippiJukeboxEnabled = true;
MeleeLagReductionCode iLagReductionCode = MELEE_LAG_REDUCTION_CODE_UNSET;
bool bHasShownLagReductionWarning = false;

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,8 @@
#include "Core/Slippi/SlippiPlayback.h"
#include "Core/Slippi/SlippiPremadeText.h"
#include "Core/Slippi/SlippiReplayComm.h"
#include <SlippiGame.h>
#include <SlippiLib/SlippiGame.h>
#include <semver/include/semver200.h>
#include <utility> // std::move
@@ -21,6 +22,7 @@
#include "Common/Thread.h"
#include "Core/HW/Memmap.h"
#include "AudioCommon/AudioCommon.h"
#include "VideoCommon/OnScreenDisplay.h"
#include "Core/Core.h"
@@ -39,6 +41,9 @@
#include "DolphinWX/Frame.h"
#include "DolphinWX/Main.h"
// The Rust library that houses a "shadow" EXI Device that we can call into.
#include "SlippiRustExtensions.h"
#define FRAME_INTERVAL 900
#define SLEEP_TIME_MS 8
#define WRITE_FILE_SLEEP_TIME_MS 85
@@ -129,6 +134,8 @@ std::string ConvertConnectCodeForGame(const std::string &input)
CEXISlippi::CEXISlippi()
{
INFO_LOG(SLIPPI, "EXI SLIPPI Constructor called.");
slprs_exi_device_ptr = slprs_exi_device_create();
m_slippiserver = SlippiSpectateServer::getInstance();
user = std::make_unique<SlippiUser>();
@@ -263,6 +270,9 @@ CEXISlippi::CEXISlippi()
CEXISlippi::~CEXISlippi()
{
u8 empty[1];
// Instruct the Rust EXI device to shut down/drop everything.
slprs_exi_device_destroy(slprs_exi_device_ptr);
// Closes file gracefully to prevent file corruption when emulation
// suddenly stops. This would happen often on netplay when the opponent
@@ -3207,6 +3217,7 @@ void CEXISlippi::DMAWrite(u32 _uAddr, u32 _uSize)
break;
case CMD_GCT_LOAD:
prepareGctLoad(&memPtr[bufLoc + 1]);
ConfigureJukebox();
break;
case CMD_GET_DELAY:
prepareDelayResponse();
@@ -3256,6 +3267,37 @@ void CEXISlippi::DMARead(u32 addr, u32 size)
Memory::CopyToEmu(addr, queueAddr, size);
}
// Configures (or reconfigures) the Jukebox by calling over the C FFI boundary.
//
// This method can also be called, indirectly, from the Settings panel.
void CEXISlippi::ConfigureJukebox()
{
#ifndef IS_PLAYBACK
// TODO: For mainline port, ISO file path can't be fetched this way. Look at the following:
// https://github.com/dolphin-emu/dolphin/blob/7f450f1d7e7d37bd2300f3a2134cb443d07251f9/Source/Core/Core/Movie.cpp#L246-L249
std::string iso_path = SConfig::GetInstance().m_strFilename;
// Exclusive WASAPI and the Jukebox do not play nicely, so we just don't bother enabling
// the Jukebox in that scenario - why bother doing the processing work when it's not even
// possible to play it?
#ifdef _WIN32
std::string backend = SConfig::GetInstance().sBackend;
if(backend.find(BACKEND_EXCLUSIVE_WASAPI) != std::string::npos)
{
return;
}
#endif
slprs_exi_device_configure_jukebox(
slprs_exi_device_ptr,
SConfig::GetInstance().bSlippiJukeboxEnabled,
Memory::m_pRAM,
iso_path.c_str(),
AudioCommonGetCurrentVolume
);
#endif
}
bool CEXISlippi::IsPresent() const
{
return true;

View File

@@ -37,6 +37,8 @@ class CEXISlippi : public IEXIDevice
void DMAWrite(u32 _uAddr, u32 _uSize) override;
void DMARead(u32 addr, u32 size) override;
void ConfigureJukebox();
bool IsPresent() const override;
private:
@@ -154,6 +156,10 @@ class CEXISlippi : public IEXIDevice
std::vector<u8> data;
std::string operation;
};
// A pointer to a "shadow" EXI Device that lives on the Rust side of things.
// This should be cleaned up in any destructor!
uintptr_t slprs_exi_device_ptr;
// .slp File creation stuff
u32 writtenByteCount = 0;

View File

@@ -42,6 +42,7 @@ void Init()
ProcessorInterface::Init();
ExpansionInterface::Init(); // Needs to be initialized before Memory
Memory::Init();
DSP::Init(SConfig::GetInstance().bDSPHLE);
DVDInterface::Init();
GPFifo::Init();
@@ -71,8 +72,12 @@ void Shutdown()
CPU::Shutdown();
DVDInterface::Shutdown();
DSP::Shutdown();
Memory::Shutdown();
ExpansionInterface::Shutdown();
// Slippi-specific change: We need to shut this down *before* `Memory`
// as we make use of some known offsets in `Memory` for the Jukebox.
ExpansionInterface::Shutdown();
Memory::Shutdown();
SerialInterface::Shutdown();
AudioInterface::Shutdown();

View File

@@ -139,6 +139,18 @@ if(wxWidgets_FOUND)
add_executable("${DOLPHIN_EXE}" ${SRCS} ${GUI_SRCS})
target_link_libraries("${DOLPHIN_EXE}" ${LIBS} ${WXLIBS})
# Link SlippiRustExtensions in.
#
# (This doesn't feel perfect here, but truth be told I can't think offhand of
# a better location for it at the moment.)
set(RUST_FEATURES "")
if(DIS_PLAYBACK)
set(RUST_FEATURES "playback")
endif()
corrosion_import_crate(MANIFEST_PATH ${CMAKE_SOURCE_DIR}/Externals/SlippiRustExtensions/Cargo.toml ${RUST_FEATURES})
target_link_libraries("${DOLPHIN_EXE}" PUBLIC slippi_rust_extensions)
if(APPLE)
include(BundleUtilities)
set(BUNDLE_PATH ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/"${DOLPHIN_EXE}".app)

View File

@@ -33,6 +33,10 @@
#include "DolphinWX/WxUtils.h"
#include <wx/valtext.h>
#ifndef IS_PLAYBACK
#include "Core/HW/EXI_DeviceSlippi.h"
#endif
SlippiNetplayConfigPane::SlippiNetplayConfigPane(wxWindow *parent, wxWindowID id)
: wxPanel(parent, id)
{
@@ -97,6 +101,22 @@ void SlippiNetplayConfigPane::InitializeGUI()
_("Make inputs feel more console-like for overclocked GCC to USB "
"adapters at the cost of 1.6ms of input lag (2ms for single-port official adapter)."));
#ifndef IS_PLAYBACK
m_slippi_jukebox_enabled_checkbox = new wxCheckBox(this, wxID_ANY, _("Enable Music (Beta)"));
// WASAPI does not work with this and we want a note for the user.
#ifdef _WIN32
m_slippi_jukebox_enabled_checkbox->SetToolTip(
_("Toggle in-game music for stages and menus. Changing this does not affect "
"other audio like character hits or effects. This option does nothing when "
"using the Exclusive WASAPI audio backend."));
#else
m_slippi_jukebox_enabled_checkbox->SetToolTip(
_("Toggle in-game music for stages and menus. Changing this does not affect "
"other audio like character hits or effects."));
#endif
#endif
const int space5 = FromDIP(5);
const int space10 = FromDIP(10);
@@ -153,6 +173,16 @@ void SlippiNetplayConfigPane::InitializeGUI()
main_sizer->Add(sbSlippiInputSettings, 0, wxEXPAND | wxLEFT | wxRIGHT, space5);
main_sizer->AddSpacer(space5);
#ifndef IS_PLAYBACK
wxStaticBoxSizer *const sbSlippiJukeboxSettings = new wxStaticBoxSizer(wxVERTICAL, this, _("Slippi Jukebox Settings"));
sbSlippiJukeboxSettings->AddSpacer(space5);
sbSlippiJukeboxSettings->Add(m_slippi_jukebox_enabled_checkbox, 0, wxLEFT | wxRIGHT, space5);
sbSlippiJukeboxSettings->AddSpacer(space5);
main_sizer->Add(sbSlippiJukeboxSettings, 0, wxEXPAND | wxLEFT | wxRIGHT, space5);
main_sizer->AddSpacer(space5);
#endif
SetSizer(main_sizer);
}
@@ -194,6 +224,10 @@ void SlippiNetplayConfigPane::LoadGUIValues()
}
m_reduce_timing_dispersion_checkbox->SetValue(startup_params.bReduceTimingDispersion);
#ifndef IS_PLAYBACK
m_slippi_jukebox_enabled_checkbox->SetValue(startup_params.bSlippiJukeboxEnabled);
#endif
}
void SlippiNetplayConfigPane::BindEvents()
@@ -216,6 +250,11 @@ void SlippiNetplayConfigPane::BindEvents()
m_reduce_timing_dispersion_checkbox->Bind(wxEVT_CHECKBOX, &SlippiNetplayConfigPane::OnReduceTimingDispersionToggle,
this);
#ifndef IS_PLAYBACK
m_slippi_jukebox_enabled_checkbox->Bind(wxEVT_CHECKBOX, &SlippiNetplayConfigPane::OnToggleJukeboxEnabled,
this);
#endif
}
void SlippiNetplayConfigPane::OnQuickChatChanged(wxCommandEvent &event)
@@ -300,6 +339,28 @@ void SlippiNetplayConfigPane::OnReduceTimingDispersionToggle(wxCommandEvent &eve
SConfig::GetInstance().bReduceTimingDispersion = m_reduce_timing_dispersion_checkbox->GetValue();
}
#ifndef IS_PLAYBACK
void SlippiNetplayConfigPane::OnToggleJukeboxEnabled(wxCommandEvent &event)
{
bool isEnabled = m_slippi_jukebox_enabled_checkbox->GetValue();
SConfig::GetInstance().bSlippiJukeboxEnabled = isEnabled;
// If we have a Slippi EXI device loaded, grab it and tell it to reconfigure the Jukebox.
// Note that this should only execute if `Core` is loaded and running, as otherwise the Expansion
// Interface is not actually initialized.
if (Core::IsRunning())
{
CEXISlippi* slippiEXIDevice = (CEXISlippi*)ExpansionInterface::FindDevice(TEXIDevices::EXIDEVICE_SLIPPI);
if (slippiEXIDevice != nullptr && slippiEXIDevice->IsPresent())
{
slippiEXIDevice->ConfigureJukebox();
}
}
}
#endif
void SlippiNetplayConfigPane::PopulateEnableChatChoiceBox()
{

View File

@@ -56,6 +56,11 @@ class SlippiNetplayConfigPane final : public wxPanel
wxChoice *m_slippi_enable_quick_chat_choice;
wxCheckBox *m_reduce_timing_dispersion_checkbox;
#ifndef IS_PLAYBACK
void OnToggleJukeboxEnabled(wxCommandEvent &event);
wxCheckBox *m_slippi_jukebox_enabled_checkbox;
#endif
};
class SlippiPlaybackConfigPane final : public wxPanel

View File

@@ -1,349 +1,363 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="DebugFast|x64">
<Configuration>DebugFast</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="ReleasePlayback|x64">
<Configuration>ReleasePlayback</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{1B099EF8-6F87-47A2-A3E7-898A24584F49}</ProjectGuid>
<ProjectName>Dolphin</ProjectName>
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='ReleasePlayback'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='DebugFast'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\..\VSProps\Base.props" />
<Import Project="..\..\VSProps\PCHUse.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LibraryPath>$(DXSDK_DIR)Lib\x64;$(LibraryPath)</LibraryPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LibraryPath>$(DXSDK_DIR)Lib\x64;$(LibraryPath)</LibraryPath>
<TargetName>Slippi $(ProjectName)$(TargetSuffix)</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleasePlayback|x64'">
<LibraryPath>$(DXSDK_DIR)Lib\x64;$(LibraryPath)</LibraryPath>
<TargetName>Slippi $(ProjectName)$(TargetSuffix)</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'">
<LibraryPath>$(DXSDK_DIR)Lib\x64;$(LibraryPath)</LibraryPath>
</PropertyGroup>
<ItemDefinitionGroup>
<Link>
<BaseAddress>0x00400000</BaseAddress>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<FixedBaseAddress>true</FixedBaseAddress>
<AdditionalLibraryDirectories>$(ExternalsDir)ffmpeg\lib;$(ExternalsDir)OpenAL\$(PlatformName);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>avrt.lib;dsound.lib;iphlpapi.lib;winmm.lib;setupapi.lib;OpenAL32.lib;opengl32.lib;glu32.lib;rpcrt4.lib;comctl32.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;version.lib;qwave.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">/NODEFAULTLIB:libcmt /ignore:4281 %(AdditionalOptions)</AdditionalOptions>
<AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">/NODEFAULTLIB:libcmt /ignore:4281 %(AdditionalOptions)</AdditionalOptions>
<AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='ReleasePlayback|x64'">/NODEFAULTLIB:libcmt /ignore:4281 %(AdditionalOptions)</AdditionalOptions>
<AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'">/NODEFAULTLIB:libcmt /ignore:4281 %(AdditionalOptions)</AdditionalOptions>
<SubSystem>Windows</SubSystem>
</Link>
<ResourceCompile>
<AdditionalIncludeDirectories>$(ExternalsDir)wxWidgets3\include;$(ExternalsDir)nlohmann;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ResourceCompile>
<ClCompile />
<ClCompile />
<Manifest>
<EnableDpiAwareness>true</EnableDpiAwareness>
</Manifest>
<ClCompile>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='ReleasePlayback|x64'">IS_PLAYBACK;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="AboutDolphin.cpp" />
<ClCompile Include="Cheats\ActionReplayCodesPanel.cpp" />
<ClCompile Include="Cheats\ARCodeAddEdit.cpp" />
<ClCompile Include="Cheats\CheatSearchTab.cpp" />
<ClCompile Include="Cheats\CheatsWindow.cpp" />
<ClCompile Include="Cheats\CreateCodeDialog.cpp" />
<ClCompile Include="Cheats\GeckoCodeDiag.cpp" />
<ClCompile Include="Config\AdvancedConfigPane.cpp" />
<ClCompile Include="Config\AudioConfigPane.cpp" />
<ClCompile Include="Config\ConfigMain.cpp" />
<ClCompile Include="Config\GameCubeConfigPane.cpp" />
<ClCompile Include="Config\GCAdapterConfigDiag.cpp" />
<ClCompile Include="Config\GeneralConfigPane.cpp" />
<ClCompile Include="Config\InterfaceConfigPane.cpp" />
<ClCompile Include="Config\PathConfigPane.cpp" />
<ClCompile Include="Config\SlippiConfigPane.cpp" />
<ClCompile Include="Config\WiiConfigPane.cpp" />
<ClCompile Include="Debugger\BreakpointDlg.cpp" />
<ClCompile Include="Debugger\BreakpointView.cpp" />
<ClCompile Include="Debugger\BreakpointWindow.cpp" />
<ClCompile Include="Debugger\CodeView.cpp" />
<ClCompile Include="Debugger\CodeWindow.cpp" />
<ClCompile Include="Debugger\CodeWindowFunctions.cpp" />
<ClCompile Include="Debugger\DebuggerPanel.cpp" />
<ClCompile Include="Debugger\DebuggerUIUtil.cpp" />
<ClCompile Include="Debugger\DSPDebugWindow.cpp" />
<ClCompile Include="Debugger\DSPRegisterView.cpp" />
<ClCompile Include="Debugger\JitWindow.cpp" />
<ClCompile Include="Debugger\MemoryCheckDlg.cpp" />
<ClCompile Include="Debugger\MemoryView.cpp" />
<ClCompile Include="Debugger\MemoryWindow.cpp" />
<ClCompile Include="Debugger\RegisterView.cpp" />
<ClCompile Include="Debugger\RegisterWindow.cpp" />
<ClCompile Include="Debugger\WatchView.cpp" />
<ClCompile Include="Debugger\WatchWindow.cpp" />
<ClCompile Include="DolphinSlider.cpp" />
<ClCompile Include="Input\ClassicInputConfigDiag.cpp" />
<ClCompile Include="Input\DrumsInputConfigDiag.cpp" />
<ClCompile Include="Input\GCKeyboardInputConfigDiag.cpp" />
<ClCompile Include="Input\GCPadInputConfigDiag.cpp" />
<ClCompile Include="Input\GuitarInputConfigDiag.cpp" />
<ClCompile Include="Input\HotkeyInputConfigDiag.cpp" />
<ClCompile Include="Input\InputConfigDiag.cpp" />
<ClCompile Include="Input\InputConfigDiagBitmaps.cpp" />
<ClCompile Include="Input\MicButtonConfigDiag.cpp" />
<ClCompile Include="Input\NunchukInputConfigDiag.cpp" />
<ClCompile Include="Input\TurntableInputConfigDiag.cpp" />
<ClCompile Include="Input\WiimoteInputConfigDiag.cpp" />
<ClCompile Include="ISOProperties\FileSystemPanel.cpp" />
<ClCompile Include="ISOProperties\InfoPanel.cpp" />
<ClCompile Include="ISOProperties\ISOProperties.cpp" />
<ClCompile Include="NetPlay\ChangeGameDialog.cpp" />
<ClCompile Include="NetPlay\MD5Dialog.cpp" />
<ClCompile Include="NetPlay\NetPlayLauncher.cpp" />
<ClCompile Include="NetPlay\NetPlaySetupFrame.cpp" />
<ClCompile Include="NetPlay\NetWindow.cpp" />
<ClCompile Include="FifoPlayerDlg.cpp" />
<ClCompile Include="Frame.cpp" />
<ClCompile Include="FrameAui.cpp" />
<ClCompile Include="FrameTools.cpp" />
<ClCompile Include="GameListCtrl.cpp" />
<ClCompile Include="ISOFile.cpp" />
<ClCompile Include="LogConfigWindow.cpp" />
<ClCompile Include="LogWindow.cpp" />
<ClCompile Include="Main.cpp" />
<ClCompile Include="MainMenuBar.cpp" />
<ClCompile Include="MainNoGUI.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="MainToolBar.cpp" />
<ClCompile Include="MemcardManager.cpp" />
<ClCompile Include="NetPlay\PadMapDialog.cpp" />
<ClCompile Include="PatchAddEdit.cpp" />
<ClCompile Include="SoftwareVideoConfigDialog.cpp" />
<ClCompile Include="TASInputDlg.cpp" />
<ClCompile Include="VideoConfigDiag.cpp" />
<ClCompile Include="PostProcessingConfigDiag.cpp" />
<ClCompile Include="ControllerConfigDiag.cpp" />
<ClCompile Include="WxEventUtils.cpp" />
<ClCompile Include="WXInputBase.cpp" />
<ClCompile Include="WxUtils.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="Cheats\ActionReplayCodesPanel.h" />
<ClInclude Include="Cheats\ARCodeAddEdit.h" />
<ClInclude Include="Config\AdvancedConfigPane.h" />
<ClInclude Include="Config\AudioConfigPane.h" />
<ClInclude Include="Config\GameCubeConfigPane.h" />
<ClInclude Include="Config\GCAdapterConfigDiag.h" />
<ClInclude Include="Config\GeneralConfigPane.h" />
<ClInclude Include="Config\InterfaceConfigPane.h" />
<ClInclude Include="Config\PathConfigPane.h" />
<ClInclude Include="Config\SlippiConfigPane.h" />
<ClInclude Include="Config\WiiConfigPane.h" />
<ClInclude Include="DolphinSlider.h" />
<ClInclude Include="Input\ClassicInputConfigDiag.h" />
<ClInclude Include="Input\DrumsInputConfigDiag.h" />
<ClInclude Include="Input\GCKeyboardInputConfigDiag.h" />
<ClInclude Include="Input\GCPadInputConfigDiag.h" />
<ClInclude Include="Input\GuitarInputConfigDiag.h" />
<ClInclude Include="Input\HotkeyInputConfigDiag.h" />
<ClInclude Include="Input\InputConfigDiag.h" />
<ClInclude Include="Input\MicButtonConfigDiag.h" />
<ClInclude Include="Input\NunchukInputConfigDiag.h" />
<ClInclude Include="Input\TurntableInputConfigDiag.h" />
<ClInclude Include="Input\WiimoteInputConfigDiag.h" />
<ClInclude Include="ISOProperties\FileSystemPanel.h" />
<ClInclude Include="ISOProperties\InfoPanel.h" />
<ClInclude Include="ISOProperties\ISOProperties.h" />
<ClInclude Include="NetPlay\ChangeGameDialog.h" />
<ClInclude Include="NetPlay\MD5Dialog.h" />
<ClInclude Include="NetPlay\NetPlayLauncher.h" />
<ClInclude Include="NetPlay\NetPlaySetupFrame.h" />
<ClInclude Include="NetPlay\PadMapDialog.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="AboutDolphin.h" />
<ClInclude Include="Cheats\CheatSearchTab.h" />
<ClInclude Include="Cheats\CheatsWindow.h" />
<ClInclude Include="Cheats\CreateCodeDialog.h" />
<ClInclude Include="Cheats\GeckoCodeDiag.h" />
<ClInclude Include="Config\ConfigMain.h" />
<ClInclude Include="Debugger\BreakpointDlg.h" />
<ClInclude Include="Debugger\BreakpointView.h" />
<ClInclude Include="Debugger\BreakpointWindow.h" />
<ClInclude Include="Debugger\CodeView.h" />
<ClInclude Include="Debugger\CodeWindow.h" />
<ClInclude Include="Debugger\DebuggerPanel.h" />
<ClInclude Include="Debugger\DebuggerUIUtil.h" />
<ClInclude Include="Debugger\DSPDebugWindow.h" />
<ClInclude Include="Debugger\DSPRegisterView.h" />
<ClInclude Include="Debugger\JitWindow.h" />
<ClInclude Include="Debugger\MemoryCheckDlg.h" />
<ClInclude Include="Debugger\MemoryView.h" />
<ClInclude Include="Debugger\MemoryWindow.h" />
<ClInclude Include="Debugger\RegisterView.h" />
<ClInclude Include="Debugger\RegisterWindow.h" />
<ClInclude Include="Debugger\WatchView.h" />
<ClInclude Include="Debugger\WatchWindow.h" />
<ClInclude Include="NetPlay\NetWindow.h" />
<ClInclude Include="FifoPlayerDlg.h" />
<ClInclude Include="Frame.h" />
<ClInclude Include="GameListCtrl.h" />
<ClInclude Include="Globals.h" />
<ClInclude Include="ISOFile.h" />
<ClInclude Include="LogConfigWindow.h" />
<ClInclude Include="LogWindow.h" />
<ClInclude Include="Main.h" />
<ClInclude Include="MainMenuBar.h" />
<ClInclude Include="MainToolBar.h" />
<ClInclude Include="MemcardManager.h" />
<ClInclude Include="PatchAddEdit.h" />
<ClInclude Include="SoftwareVideoConfigDialog.h" />
<ClInclude Include="TASInputDlg.h" />
<ClInclude Include="VideoConfigDiag.h" />
<ClInclude Include="PostProcessingConfigDiag.h" />
<ClInclude Include="ControllerConfigDiag.h" />
<ClInclude Include="WxEventUtils.h" />
<ClInclude Include="WXInputBase.h" />
<ClInclude Include="WxUtils.h" />
</ItemGroup>
<ItemGroup>
<Text Include="CMakeLists.txt" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="DolphinWX.rc" />
</ItemGroup>
<ItemGroup>
<Image Include="$(CoreDir)..\..\Installer\Dolphin.ico" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(ExternalsDir)Bochs_disasm\Bochs_disasm.vcxproj">
<Project>{CD3D4C3C-1027-4D33-B047-AEC7B56D0BF6}</Project>
</ProjectReference>
<ProjectReference Include="$(ExternalsDir)libpng\png\png.vcxproj">
<Project>{01573C36-AC6E-49F6-94BA-572517EB9740}</Project>
</ProjectReference>
<ProjectReference Include="$(ExternalsDir)LZO\LZO.vcxproj">
<Project>{D8890B98-26F7-4CFF-BBFB-B95F371B5F20}</Project>
</ProjectReference>
<ProjectReference Include="$(ExternalsDir)SFML\build\vc2010\SFML_Network.vcxproj">
<Project>{93D73454-2512-424E-9CDA-4BB357FE13DD}</Project>
</ProjectReference>
<ProjectReference Include="$(ExternalsDir)wxWidgets3\build\msw\wx_base.vcxproj">
<Project>{1C8436C9-DBAF-42BE-83BC-CF3EC9175ABE}</Project>
</ProjectReference>
<ProjectReference Include="$(ExternalsDir)zlib\zlib.vcxproj">
<Project>{3E1339F5-9311-4122-9442-369702E8FCAD}</Project>
</ProjectReference>
<ProjectReference Include="$(CoreDir)VideoBackends\DX11\DX11.vcxproj">
<Project>{9A4C733C-BADE-4AC6-B58A-6E274395E90E}</Project>
</ProjectReference>
<ProjectReference Include="$(CoreDir)VideoBackends\DX9\DX9.vcxproj">
<Project>{DC7D7AF4-CE47-49E8-8B63-265CB6233A49}</Project>
</ProjectReference>
<ProjectReference Include="$(CoreDir)VideoBackends\OGL\OGL.vcxproj">
<Project>{1909CD2D-1707-456F-86CA-0DF42A727C99}</Project>
</ProjectReference>
<ProjectReference Include="$(CoreDir)AudioCommon\AudioCommon.vcxproj">
<Project>{37D007BD-D66C-4EAF-B56C-BD1AAC340A05}</Project>
</ProjectReference>
<ProjectReference Include="$(CoreDir)Common\Common.vcxproj">
<Project>{C87A4178-44F6-49B2-B7AA-C79AF1B8C534}</Project>
</ProjectReference>
<ProjectReference Include="$(CoreDir)Common\SCMRevGen.vcxproj">
<Project>{41279555-f94f-4ebc-99de-af863c10c5c4}</Project>
</ProjectReference>
<ProjectReference Include="$(CoreDir)Core\Core.vcxproj">
<Project>{8C60E805-0DA5-4E25-8F84-038DB504BB0D}</Project>
</ProjectReference>
<ProjectReference Include="$(CoreDir)DiscIO\DiscIO.vcxproj">
<Project>{B6398059-EBB6-4C34-B547-95F365B71FF4}</Project>
</ProjectReference>
<ProjectReference Include="$(CoreDir)InputCommon\InputCommon.vcxproj">
<Project>{B39AC394-5DB5-4DA9-9D98-09D46CA3701F}</Project>
</ProjectReference>
<ProjectReference Include="$(CoreDir)VideoBackends\Vulkan\Vulkan.vcxproj">
<Project>{29F29A19-F141-45AD-9679-5A2923B49DA3}</Project>
</ProjectReference>
<ProjectReference Include="$(CoreDir)VideoCommon\VideoCommon.vcxproj">
<Project>{3E5C4E02-1BA9-4776-BDBE-E3F91FFA34CF}</Project>
</ProjectReference>
<ProjectReference Include="..\UICommon\UICommon.vcxproj">
<Project>{604c8368-f34a-4d55-82c8-cc92a0c13254}</Project>
</ProjectReference>
<ProjectReference Include="..\VideoBackends\D3D12\D3D12.vcxproj">
<Project>{570215b7-e32f-4438-95ae-c8d955f9fca3}</Project>
</ProjectReference>
<ProjectReference Include="..\VideoBackends\Software\Software.vcxproj">
<Project>{9e9da440-e9ad-413c-b648-91030e792211}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\Externals\nlohmann\nlohmann.vcxproj">
<Project>{732d2110-06a3-4aa1-9634-7bb5a4f75b82}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
<!--Copy Exe, Data directory and DLLs which should be located in the executable directory-->
<ItemGroup>
<DataSysFiles Include="$(DolphinRootDir)Data\**\Sys\**\*.*" />
<DataLicenseFile Include="$(DolphinRootDir)Data\license.txt" />
<DataPortableFile Include="$(DolphinRootDir)Data\portable.txt" />
<ExternalDlls Include="$(ExternalsDir)OpenAL\$(PlatformName)\*.dll;$(ExternalsDir)wxWidgets3\dlls\Webview2\$(PlatformName)\*.dll" />
<BinaryFiles Include="$(TargetPath)" />
<AllInputFiles Include="@(DataSysFiles);@(DataTxtFiles);@(ExternalDlls);@(BinaryFiles)" />
</ItemGroup>
<Target Name="AfterBuild" Inputs="@(AllInputFiles)" Outputs="@(AllInputFiles -> '$(BinaryOutputDir)%(RecursiveDir)%(Filename)%(Extension)')">
<Message Text="Copying Data directory..." Importance="High" />
<Copy SourceFiles="@(DataSysFiles)" DestinationFolder="$(BinaryOutputDir)%(RecursiveDir)" Condition="!Exists('$(BinaryOutputDir)%(RecursiveDir)%(Filename)%(DataSysFiles.Extension)') OR $([System.DateTime]::Parse('%(ModifiedTime)').Ticks) &gt; $([System.IO.File]::GetLastWriteTime('$(BinaryOutputDir)%(RecursiveDir)%(Filename)%(DataSysFiles.Extension)').Ticks)" />
<Copy SourceFiles="@(DataLicenseFile)" DestinationFolder="$(BinaryOutputDir)" Condition="!Exists('$(BinaryOutputDir)%(Filename)%(DataTxtFiles.Extension)') OR $([System.DateTime]::Parse('%(ModifiedTime)').Ticks) &gt; $([System.IO.File]::GetLastWriteTime('$(BinaryOutputDir)%(RecursiveDir)%(Filename)%(DataTxtFiles.Extension)').Ticks)" />
<Copy SourceFiles="@(DataPortableFile)" DestinationFolder="$(BinaryOutputDir)" Condition="!Exists('$(BinaryOutputDir)%(Filename)%(DataTxtFiles.Extension)') OR $([System.DateTime]::Parse('%(ModifiedTime)').Ticks) &gt; $([System.IO.File]::GetLastWriteTime('$(BinaryOutputDir)%(RecursiveDir)%(Filename)%(DataTxtFiles.Extension)').Ticks)" />
<Message Text="Copying External .dlls" Importance="High" />
<Copy SourceFiles="@(ExternalDlls)" DestinationFolder="$(BinaryOutputDir)" Condition="!Exists('$(BinaryOutputDir)%(RecursiveDir)%(Filename)%(ExternalDlls.Extension)') OR $([System.DateTime]::Parse('%(ModifiedTime)').Ticks) &gt; $([System.IO.File]::GetLastWriteTime('$(BinaryOutputDir)%(RecursiveDir)%(Filename)%(ExternalDlls.Extension)').Ticks)" />
<Message Text="Copy: @(BinaryFiles) -&gt; $(BinaryOutputDir)" Importance="High" />
<Copy SourceFiles="@(BinaryFiles)" DestinationFolder="$(BinaryOutputDir)" />
</Target>
</Project>
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="DebugFast|x64">
<Configuration>DebugFast</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="ReleasePlayback|x64">
<Configuration>ReleasePlayback</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{1B099EF8-6F87-47A2-A3E7-898A24584F49}</ProjectGuid>
<ProjectName>Dolphin</ProjectName>
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='ReleasePlayback'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='DebugFast'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\..\VSProps\Base.props" />
<Import Project="..\..\VSProps\PCHUse.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LibraryPath>$(DXSDK_DIR)Lib\x64;$(LibraryPath)</LibraryPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LibraryPath>$(DXSDK_DIR)Lib\x64;$(LibraryPath)</LibraryPath>
<TargetName>Slippi $(ProjectName)$(TargetSuffix)</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleasePlayback|x64'">
<LibraryPath>$(DXSDK_DIR)Lib\x64;$(LibraryPath)</LibraryPath>
<TargetName>Slippi $(ProjectName)$(TargetSuffix)</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'">
<LibraryPath>$(DXSDK_DIR)Lib\x64;$(LibraryPath)</LibraryPath>
</PropertyGroup>
<ItemDefinitionGroup>
<Link>
<BaseAddress>0x00400000</BaseAddress>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<FixedBaseAddress>true</FixedBaseAddress>
<AdditionalLibraryDirectories>$(ExternalsDir)SlippiRustExtensions\target\x86_64-pc-windows-msvc\release;$(ExternalsDir)ffmpeg\lib;$(ExternalsDir)OpenAL\$(PlatformName);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>avrt.lib;dsound.lib;iphlpapi.lib;winmm.lib;setupapi.lib;OpenAL32.lib;opengl32.lib;glu32.lib;rpcrt4.lib;comctl32.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;version.lib;qwave.lib;slippi_rust_extensions.dll.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">/NODEFAULTLIB:libcmt /ignore:4281 %(AdditionalOptions)</AdditionalOptions>
<AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">/NODEFAULTLIB:libcmt /ignore:4281 %(AdditionalOptions)</AdditionalOptions>
<AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='ReleasePlayback|x64'">/NODEFAULTLIB:libcmt /ignore:4281 %(AdditionalOptions)</AdditionalOptions>
<AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'">/NODEFAULTLIB:libcmt /ignore:4281 %(AdditionalOptions)</AdditionalOptions>
<SubSystem>Windows</SubSystem>
</Link>
<ResourceCompile>
<AdditionalIncludeDirectories>$(ExternalsDir)wxWidgets3\include;$(ExternalsDir)nlohmann;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ResourceCompile>
<ClCompile />
<ClCompile />
<Manifest>
<EnableDpiAwareness>true</EnableDpiAwareness>
</Manifest>
<ClCompile>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='ReleasePlayback|x64'">IS_PLAYBACK;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<PreLinkEvent>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
</Command>
</PreLinkEvent>
<PreLinkEvent>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
</Message>
</PreLinkEvent>
<ClCompile>
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(ExternalsDir)zlib;$(ExternalsDir)SlippiLib;$(ExternalsDir)nlohmann;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="AboutDolphin.cpp" />
<ClCompile Include="Cheats\ActionReplayCodesPanel.cpp" />
<ClCompile Include="Cheats\ARCodeAddEdit.cpp" />
<ClCompile Include="Cheats\CheatSearchTab.cpp" />
<ClCompile Include="Cheats\CheatsWindow.cpp" />
<ClCompile Include="Cheats\CreateCodeDialog.cpp" />
<ClCompile Include="Cheats\GeckoCodeDiag.cpp" />
<ClCompile Include="Config\AdvancedConfigPane.cpp" />
<ClCompile Include="Config\AudioConfigPane.cpp" />
<ClCompile Include="Config\ConfigMain.cpp" />
<ClCompile Include="Config\GameCubeConfigPane.cpp" />
<ClCompile Include="Config\GCAdapterConfigDiag.cpp" />
<ClCompile Include="Config\GeneralConfigPane.cpp" />
<ClCompile Include="Config\InterfaceConfigPane.cpp" />
<ClCompile Include="Config\PathConfigPane.cpp" />
<ClCompile Include="Config\SlippiConfigPane.cpp" />
<ClCompile Include="Config\WiiConfigPane.cpp" />
<ClCompile Include="Debugger\BreakpointDlg.cpp" />
<ClCompile Include="Debugger\BreakpointView.cpp" />
<ClCompile Include="Debugger\BreakpointWindow.cpp" />
<ClCompile Include="Debugger\CodeView.cpp" />
<ClCompile Include="Debugger\CodeWindow.cpp" />
<ClCompile Include="Debugger\CodeWindowFunctions.cpp" />
<ClCompile Include="Debugger\DebuggerPanel.cpp" />
<ClCompile Include="Debugger\DebuggerUIUtil.cpp" />
<ClCompile Include="Debugger\DSPDebugWindow.cpp" />
<ClCompile Include="Debugger\DSPRegisterView.cpp" />
<ClCompile Include="Debugger\JitWindow.cpp" />
<ClCompile Include="Debugger\MemoryCheckDlg.cpp" />
<ClCompile Include="Debugger\MemoryView.cpp" />
<ClCompile Include="Debugger\MemoryWindow.cpp" />
<ClCompile Include="Debugger\RegisterView.cpp" />
<ClCompile Include="Debugger\RegisterWindow.cpp" />
<ClCompile Include="Debugger\WatchView.cpp" />
<ClCompile Include="Debugger\WatchWindow.cpp" />
<ClCompile Include="DolphinSlider.cpp" />
<ClCompile Include="Input\ClassicInputConfigDiag.cpp" />
<ClCompile Include="Input\DrumsInputConfigDiag.cpp" />
<ClCompile Include="Input\GCKeyboardInputConfigDiag.cpp" />
<ClCompile Include="Input\GCPadInputConfigDiag.cpp" />
<ClCompile Include="Input\GuitarInputConfigDiag.cpp" />
<ClCompile Include="Input\HotkeyInputConfigDiag.cpp" />
<ClCompile Include="Input\InputConfigDiag.cpp" />
<ClCompile Include="Input\InputConfigDiagBitmaps.cpp" />
<ClCompile Include="Input\MicButtonConfigDiag.cpp" />
<ClCompile Include="Input\NunchukInputConfigDiag.cpp" />
<ClCompile Include="Input\TurntableInputConfigDiag.cpp" />
<ClCompile Include="Input\WiimoteInputConfigDiag.cpp" />
<ClCompile Include="ISOProperties\FileSystemPanel.cpp" />
<ClCompile Include="ISOProperties\InfoPanel.cpp" />
<ClCompile Include="ISOProperties\ISOProperties.cpp" />
<ClCompile Include="NetPlay\ChangeGameDialog.cpp" />
<ClCompile Include="NetPlay\MD5Dialog.cpp" />
<ClCompile Include="NetPlay\NetPlayLauncher.cpp" />
<ClCompile Include="NetPlay\NetPlaySetupFrame.cpp" />
<ClCompile Include="NetPlay\NetWindow.cpp" />
<ClCompile Include="FifoPlayerDlg.cpp" />
<ClCompile Include="Frame.cpp" />
<ClCompile Include="FrameAui.cpp" />
<ClCompile Include="FrameTools.cpp" />
<ClCompile Include="GameListCtrl.cpp" />
<ClCompile Include="ISOFile.cpp" />
<ClCompile Include="LogConfigWindow.cpp" />
<ClCompile Include="LogWindow.cpp" />
<ClCompile Include="Main.cpp" />
<ClCompile Include="MainMenuBar.cpp" />
<ClCompile Include="MainNoGUI.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="MainToolBar.cpp" />
<ClCompile Include="MemcardManager.cpp" />
<ClCompile Include="NetPlay\PadMapDialog.cpp" />
<ClCompile Include="PatchAddEdit.cpp" />
<ClCompile Include="SoftwareVideoConfigDialog.cpp" />
<ClCompile Include="TASInputDlg.cpp" />
<ClCompile Include="VideoConfigDiag.cpp" />
<ClCompile Include="PostProcessingConfigDiag.cpp" />
<ClCompile Include="ControllerConfigDiag.cpp" />
<ClCompile Include="WxEventUtils.cpp" />
<ClCompile Include="WXInputBase.cpp" />
<ClCompile Include="WxUtils.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="Cheats\ActionReplayCodesPanel.h" />
<ClInclude Include="Cheats\ARCodeAddEdit.h" />
<ClInclude Include="Config\AdvancedConfigPane.h" />
<ClInclude Include="Config\AudioConfigPane.h" />
<ClInclude Include="Config\GameCubeConfigPane.h" />
<ClInclude Include="Config\GCAdapterConfigDiag.h" />
<ClInclude Include="Config\GeneralConfigPane.h" />
<ClInclude Include="Config\InterfaceConfigPane.h" />
<ClInclude Include="Config\PathConfigPane.h" />
<ClInclude Include="Config\SlippiConfigPane.h" />
<ClInclude Include="Config\WiiConfigPane.h" />
<ClInclude Include="DolphinSlider.h" />
<ClInclude Include="Input\ClassicInputConfigDiag.h" />
<ClInclude Include="Input\DrumsInputConfigDiag.h" />
<ClInclude Include="Input\GCKeyboardInputConfigDiag.h" />
<ClInclude Include="Input\GCPadInputConfigDiag.h" />
<ClInclude Include="Input\GuitarInputConfigDiag.h" />
<ClInclude Include="Input\HotkeyInputConfigDiag.h" />
<ClInclude Include="Input\InputConfigDiag.h" />
<ClInclude Include="Input\MicButtonConfigDiag.h" />
<ClInclude Include="Input\NunchukInputConfigDiag.h" />
<ClInclude Include="Input\TurntableInputConfigDiag.h" />
<ClInclude Include="Input\WiimoteInputConfigDiag.h" />
<ClInclude Include="ISOProperties\FileSystemPanel.h" />
<ClInclude Include="ISOProperties\InfoPanel.h" />
<ClInclude Include="ISOProperties\ISOProperties.h" />
<ClInclude Include="NetPlay\ChangeGameDialog.h" />
<ClInclude Include="NetPlay\MD5Dialog.h" />
<ClInclude Include="NetPlay\NetPlayLauncher.h" />
<ClInclude Include="NetPlay\NetPlaySetupFrame.h" />
<ClInclude Include="NetPlay\PadMapDialog.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="AboutDolphin.h" />
<ClInclude Include="Cheats\CheatSearchTab.h" />
<ClInclude Include="Cheats\CheatsWindow.h" />
<ClInclude Include="Cheats\CreateCodeDialog.h" />
<ClInclude Include="Cheats\GeckoCodeDiag.h" />
<ClInclude Include="Config\ConfigMain.h" />
<ClInclude Include="Debugger\BreakpointDlg.h" />
<ClInclude Include="Debugger\BreakpointView.h" />
<ClInclude Include="Debugger\BreakpointWindow.h" />
<ClInclude Include="Debugger\CodeView.h" />
<ClInclude Include="Debugger\CodeWindow.h" />
<ClInclude Include="Debugger\DebuggerPanel.h" />
<ClInclude Include="Debugger\DebuggerUIUtil.h" />
<ClInclude Include="Debugger\DSPDebugWindow.h" />
<ClInclude Include="Debugger\DSPRegisterView.h" />
<ClInclude Include="Debugger\JitWindow.h" />
<ClInclude Include="Debugger\MemoryCheckDlg.h" />
<ClInclude Include="Debugger\MemoryView.h" />
<ClInclude Include="Debugger\MemoryWindow.h" />
<ClInclude Include="Debugger\RegisterView.h" />
<ClInclude Include="Debugger\RegisterWindow.h" />
<ClInclude Include="Debugger\WatchView.h" />
<ClInclude Include="Debugger\WatchWindow.h" />
<ClInclude Include="NetPlay\NetWindow.h" />
<ClInclude Include="FifoPlayerDlg.h" />
<ClInclude Include="Frame.h" />
<ClInclude Include="GameListCtrl.h" />
<ClInclude Include="Globals.h" />
<ClInclude Include="ISOFile.h" />
<ClInclude Include="LogConfigWindow.h" />
<ClInclude Include="LogWindow.h" />
<ClInclude Include="Main.h" />
<ClInclude Include="MainMenuBar.h" />
<ClInclude Include="MainToolBar.h" />
<ClInclude Include="MemcardManager.h" />
<ClInclude Include="PatchAddEdit.h" />
<ClInclude Include="SoftwareVideoConfigDialog.h" />
<ClInclude Include="TASInputDlg.h" />
<ClInclude Include="VideoConfigDiag.h" />
<ClInclude Include="PostProcessingConfigDiag.h" />
<ClInclude Include="ControllerConfigDiag.h" />
<ClInclude Include="WxEventUtils.h" />
<ClInclude Include="WXInputBase.h" />
<ClInclude Include="WxUtils.h" />
</ItemGroup>
<ItemGroup>
<Text Include="CMakeLists.txt" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="DolphinWX.rc" />
</ItemGroup>
<ItemGroup>
<Image Include="$(CoreDir)..\..\Installer\Dolphin.ico" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(ExternalsDir)Bochs_disasm\Bochs_disasm.vcxproj">
<Project>{CD3D4C3C-1027-4D33-B047-AEC7B56D0BF6}</Project>
</ProjectReference>
<ProjectReference Include="$(ExternalsDir)libpng\png\png.vcxproj">
<Project>{01573C36-AC6E-49F6-94BA-572517EB9740}</Project>
</ProjectReference>
<ProjectReference Include="$(ExternalsDir)LZO\LZO.vcxproj">
<Project>{D8890B98-26F7-4CFF-BBFB-B95F371B5F20}</Project>
</ProjectReference>
<ProjectReference Include="$(ExternalsDir)SFML\build\vc2010\SFML_Network.vcxproj">
<Project>{93D73454-2512-424E-9CDA-4BB357FE13DD}</Project>
</ProjectReference>
<ProjectReference Include="$(ExternalsDir)wxWidgets3\build\msw\wx_base.vcxproj">
<Project>{1C8436C9-DBAF-42BE-83BC-CF3EC9175ABE}</Project>
</ProjectReference>
<ProjectReference Include="$(ExternalsDir)zlib\zlib.vcxproj">
<Project>{3E1339F5-9311-4122-9442-369702E8FCAD}</Project>
</ProjectReference>
<ProjectReference Include="$(CoreDir)VideoBackends\DX11\DX11.vcxproj">
<Project>{9A4C733C-BADE-4AC6-B58A-6E274395E90E}</Project>
</ProjectReference>
<ProjectReference Include="$(CoreDir)VideoBackends\DX9\DX9.vcxproj">
<Project>{DC7D7AF4-CE47-49E8-8B63-265CB6233A49}</Project>
</ProjectReference>
<ProjectReference Include="$(CoreDir)VideoBackends\OGL\OGL.vcxproj">
<Project>{1909CD2D-1707-456F-86CA-0DF42A727C99}</Project>
</ProjectReference>
<ProjectReference Include="$(CoreDir)AudioCommon\AudioCommon.vcxproj">
<Project>{37D007BD-D66C-4EAF-B56C-BD1AAC340A05}</Project>
</ProjectReference>
<ProjectReference Include="$(CoreDir)Common\Common.vcxproj">
<Project>{C87A4178-44F6-49B2-B7AA-C79AF1B8C534}</Project>
</ProjectReference>
<ProjectReference Include="$(CoreDir)Common\SCMRevGen.vcxproj">
<Project>{41279555-f94f-4ebc-99de-af863c10c5c4}</Project>
</ProjectReference>
<ProjectReference Include="$(CoreDir)Core\Core.vcxproj">
<Project>{8C60E805-0DA5-4E25-8F84-038DB504BB0D}</Project>
</ProjectReference>
<ProjectReference Include="$(CoreDir)DiscIO\DiscIO.vcxproj">
<Project>{B6398059-EBB6-4C34-B547-95F365B71FF4}</Project>
</ProjectReference>
<ProjectReference Include="$(CoreDir)InputCommon\InputCommon.vcxproj">
<Project>{B39AC394-5DB5-4DA9-9D98-09D46CA3701F}</Project>
</ProjectReference>
<ProjectReference Include="$(CoreDir)VideoBackends\Vulkan\Vulkan.vcxproj">
<Project>{29F29A19-F141-45AD-9679-5A2923B49DA3}</Project>
</ProjectReference>
<ProjectReference Include="$(CoreDir)VideoCommon\VideoCommon.vcxproj">
<Project>{3E5C4E02-1BA9-4776-BDBE-E3F91FFA34CF}</Project>
</ProjectReference>
<ProjectReference Include="..\UICommon\UICommon.vcxproj">
<Project>{604c8368-f34a-4d55-82c8-cc92a0c13254}</Project>
</ProjectReference>
<ProjectReference Include="..\VideoBackends\D3D12\D3D12.vcxproj">
<Project>{570215b7-e32f-4438-95ae-c8d955f9fca3}</Project>
</ProjectReference>
<ProjectReference Include="..\VideoBackends\Software\Software.vcxproj">
<Project>{9e9da440-e9ad-413c-b648-91030e792211}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\Externals\nlohmann\nlohmann.vcxproj">
<Project>{732d2110-06a3-4aa1-9634-7bb5a4f75b82}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
<!--Copy Exe, Data directory and DLLs which should be located in the executable directory-->
<ItemGroup>
<DataSysFiles Include="$(DolphinRootDir)Data\**\Sys\**\*.*" />
<DataLicenseFile Include="$(DolphinRootDir)Data\license.txt" />
<DataPortableFile Include="$(DolphinRootDir)Data\portable.txt" />
<ExternalDlls Include="$(ExternalsDir)OpenAL\$(PlatformName)\*.dll;$(ExternalsDir)wxWidgets3\dlls\Webview2\$(PlatformName)\*.dll" />
<RustDlls Include="$(ExternalsDir)SlippiRustExtensions\target\x86_64-pc-windows-msvc\release\slippi*.dll;$(ExternalsDir)SlippiRustExtensions\target\x86_64-pc-windows-msvc\release\slippi*.lib" />
<BinaryFiles Include="$(TargetPath)" />
<AllInputFiles Include="@(DataSysFiles);@(DataTxtFiles);@(ExternalDlls);@(BinaryFiles)" />
</ItemGroup>
<Target Name="AfterBuild" Inputs="@(AllInputFiles)" Outputs="@(AllInputFiles -> '$(BinaryOutputDir)%(RecursiveDir)%(Filename)%(Extension)')">
<Message Text="Copying Data directory..." Importance="High" />
<Copy SourceFiles="@(DataSysFiles)" DestinationFolder="$(BinaryOutputDir)%(RecursiveDir)" Condition="!Exists('$(BinaryOutputDir)%(RecursiveDir)%(Filename)%(DataSysFiles.Extension)') OR $([System.DateTime]::Parse('%(ModifiedTime)').Ticks) &gt; $([System.IO.File]::GetLastWriteTime('$(BinaryOutputDir)%(RecursiveDir)%(Filename)%(DataSysFiles.Extension)').Ticks)" />
<Copy SourceFiles="@(DataLicenseFile)" DestinationFolder="$(BinaryOutputDir)" Condition="!Exists('$(BinaryOutputDir)%(Filename)%(DataTxtFiles.Extension)') OR $([System.DateTime]::Parse('%(ModifiedTime)').Ticks) &gt; $([System.IO.File]::GetLastWriteTime('$(BinaryOutputDir)%(RecursiveDir)%(Filename)%(DataTxtFiles.Extension)').Ticks)" />
<Copy SourceFiles="@(DataPortableFile)" DestinationFolder="$(BinaryOutputDir)" Condition="!Exists('$(BinaryOutputDir)%(Filename)%(DataTxtFiles.Extension)') OR $([System.DateTime]::Parse('%(ModifiedTime)').Ticks) &gt; $([System.IO.File]::GetLastWriteTime('$(BinaryOutputDir)%(RecursiveDir)%(Filename)%(DataTxtFiles.Extension)').Ticks)" />
<Message Text="Copying External .dlls" Importance="High" />
<Copy SourceFiles="@(ExternalDlls)" DestinationFolder="$(BinaryOutputDir)" Condition="!Exists('$(BinaryOutputDir)%(RecursiveDir)%(Filename)%(ExternalDlls.Extension)') OR $([System.DateTime]::Parse('%(ModifiedTime)').Ticks) &gt; $([System.IO.File]::GetLastWriteTime('$(BinaryOutputDir)%(RecursiveDir)%(Filename)%(ExternalDlls.Extension)').Ticks)" />
<Message Text="Copying SlippiRustExtensions .dlls" Importance="High" />
<Copy SourceFiles="@(RustDlls)" DestinationFolder="$(BinaryOutputDir)" Condition="!Exists('$(BinaryOutputDir)%(RecursiveDir)%(Filename)%(RustDlls.Extension)') OR $([System.DateTime]::Parse('%(ModifiedTime)').Ticks) &gt; $([System.IO.File]::GetLastWriteTime('$(BinaryOutputDir)%(RecursiveDir)%(Filename)%(RustDlls.Extension)').Ticks)" />
<Message Text="Copy: @(BinaryFiles) -&gt; $(BinaryOutputDir)" Importance="High" />
<Copy SourceFiles="@(BinaryFiles)" DestinationFolder="$(BinaryOutputDir)" />
</Target>
</Project>