Repository: allkern/iris
Branch: master
Commit: 90dae6afaa05
Files: 245
Total size: 2.4 MB
Directory structure:
gitextract_3bqhuqc4/
├── .github/
│ ├── FUNDING.yml
│ └── workflows/
│ ├── linux.yml
│ ├── macos.yml
│ ├── release.yml
│ └── windows.yml
├── .gitignore
├── .gitmodules
├── AppImage.cmake
├── CMakeLists.txt
├── Info.plist
├── LICENSE
├── MoltenVK_icd.json
├── README.md
├── compat.txt
├── frontend/
│ ├── arcade.hpp
│ ├── audio.cpp
│ ├── config.hpp
│ ├── elf.cpp
│ ├── emu.cpp
│ ├── handlers.cpp
│ ├── imgui.cpp
│ ├── input.cpp
│ ├── iris.cpp
│ ├── iris.hpp
│ ├── notifications.cpp
│ ├── platform/
│ │ ├── stub.cpp
│ │ └── windows.cpp
│ ├── render.cpp
│ ├── res/
│ │ └── IconsMaterialSymbols.h
│ ├── settings.cpp
│ ├── shaders.cpp
│ ├── ui/
│ │ ├── about.cpp
│ │ ├── bios_setting.cpp
│ │ ├── breakpoints.cpp
│ │ ├── control.cpp
│ │ ├── dma.cpp
│ │ ├── gs.cpp
│ │ ├── intc.cpp
│ │ ├── logs.cpp
│ │ ├── memory.cpp
│ │ ├── memory_card_tool.cpp
│ │ ├── memory_search.cpp
│ │ ├── memory_viewer.h
│ │ ├── menubar.cpp
│ │ ├── modules.cpp
│ │ ├── overlay.cpp
│ │ ├── pad.cpp
│ │ ├── settings.cpp
│ │ ├── spu2.cpp
│ │ ├── state.cpp
│ │ ├── statusbar.cpp
│ │ ├── symbols.cpp
│ │ ├── threads.cpp
│ │ └── vu_disassembly.cpp
│ └── vulkan.cpp
├── main.cpp
├── res/
│ ├── iris.icns
│ ├── iris.rc
│ └── iris.res
├── shaders/
│ ├── curvature.frag
│ ├── curvature.spv
│ ├── decoder.frag
│ ├── decoder.spv
│ ├── default.frag
│ ├── default.vert
│ ├── encoder.frag
│ ├── encoder.spv
│ ├── fragment.spv
│ ├── noise.frag
│ ├── noise.spv
│ ├── scanlines.frag
│ ├── scanlines.spv
│ ├── shader.spv
│ ├── shader.vert
│ └── vertex.spv
└── src/
├── dev/
│ ├── ds.c
│ ├── ds.h
│ ├── guncon.c
│ ├── guncon.h
│ ├── mcd.c
│ ├── mcd.h
│ ├── mtap.c
│ ├── mtap.h
│ ├── ps1_mcd.c
│ └── ps1_mcd.h
├── ee/
│ ├── bus.c
│ ├── bus.h
│ ├── bus_decl.h
│ ├── dmac.c
│ ├── dmac.h
│ ├── ee.h
│ ├── ee_cached.cpp
│ ├── ee_def.hpp
│ ├── ee_dis.c
│ ├── ee_dis.h
│ ├── ee_uncached.c
│ ├── ee_uncached.h
│ ├── gif.c
│ ├── gif.h
│ ├── intc.c
│ ├── intc.h
│ ├── syscall.h
│ ├── timers.c
│ ├── timers.h
│ ├── vif.c
│ ├── vif.h
│ ├── vu.h
│ ├── vu_cached.cpp
│ ├── vu_def.hpp
│ ├── vu_dis.c
│ ├── vu_dis.h
│ └── vu_uncached.c
├── elf.h
├── gs/
│ ├── gs.c
│ ├── gs.h
│ └── renderer/
│ ├── config.hpp
│ ├── hardware.cpp
│ ├── hardware.hpp
│ ├── null.cpp
│ ├── null.hpp
│ ├── renderer.cpp
│ ├── renderer.hpp
│ ├── software_thread.cpp
│ └── software_thread.hpp
├── iop/
│ ├── bus.c
│ ├── bus.h
│ ├── bus_decl.h
│ ├── cdvd.c
│ ├── cdvd.h
│ ├── disc/
│ │ ├── bin.c
│ │ ├── bin.h
│ │ ├── chd.c
│ │ ├── chd.h
│ │ ├── ciso.c
│ │ ├── ciso.h
│ │ ├── cue.c
│ │ ├── cue.h
│ │ ├── iso.c
│ │ └── iso.h
│ ├── disc.c
│ ├── disc.h
│ ├── dma.c
│ ├── dma.h
│ ├── fw.c
│ ├── fw.h
│ ├── hle/
│ │ ├── ioman.cpp
│ │ ├── ioman.h
│ │ ├── loadcore.c
│ │ ├── loadcore.h
│ │ ├── sysmem.c
│ │ └── sysmem.h
│ ├── intc.c
│ ├── intc.h
│ ├── iop.c
│ ├── iop.h
│ ├── iop_dis.c
│ ├── iop_dis.h
│ ├── iop_export.c
│ ├── iop_export.h
│ ├── rpc.c
│ ├── rpc.h
│ ├── sio2.c
│ ├── sio2.h
│ ├── spu2.c
│ ├── spu2.h
│ ├── timers.c
│ ├── timers.h
│ ├── usb.c
│ └── usb.h
├── ipu/
│ ├── chromtable.cpp
│ ├── chromtable.hpp
│ ├── codedblockpattern.cpp
│ ├── codedblockpattern.hpp
│ ├── dct_coeff.cpp
│ ├── dct_coeff.hpp
│ ├── dct_coeff_table0.cpp
│ ├── dct_coeff_table0.hpp
│ ├── dct_coeff_table1.cpp
│ ├── dct_coeff_table1.hpp
│ ├── ipu.cpp
│ ├── ipu.h
│ ├── ipu.hpp
│ ├── ipu_fifo.cpp
│ ├── ipu_fifo.hpp
│ ├── lumtable.cpp
│ ├── lumtable.hpp
│ ├── mac_addr_inc.cpp
│ ├── mac_addr_inc.hpp
│ ├── mac_b_pic.cpp
│ ├── mac_b_pic.hpp
│ ├── mac_i_pic.cpp
│ ├── mac_i_pic.hpp
│ ├── mac_p_pic.cpp
│ ├── mac_p_pic.hpp
│ ├── motioncode.cpp
│ ├── motioncode.hpp
│ ├── vlc_table.cpp
│ └── vlc_table.hpp
├── list.c
├── list.h
├── md5.c
├── md5.h
├── ps2.c
├── ps2.h
├── ps2_elf.c
├── ps2_elf.h
├── ps2_iso9660.c
├── ps2_iso9660.h
├── queue.c
├── queue.h
├── rom.c
├── rom.h
├── s14x/
│ ├── aiboard.c
│ ├── aiboard.h
│ ├── ioboard.c
│ ├── ioboard.h
│ ├── link.c
│ ├── link.h
│ ├── nand.c
│ ├── nand.h
│ ├── sram.c
│ ├── sram.h
│ ├── syscon.c
│ └── syscon.h
├── scheduler.c
├── scheduler.h
├── shared/
│ ├── bios.c
│ ├── bios.h
│ ├── dev9.c
│ ├── dev9.h
│ ├── ram.c
│ ├── ram.h
│ ├── sbus.c
│ ├── sbus.h
│ ├── sif.c
│ ├── sif.h
│ ├── speed/
│ │ ├── ata.c
│ │ ├── ata.h
│ │ ├── eeprom.c
│ │ ├── eeprom.h
│ │ ├── flash.c
│ │ └── flash.h
│ ├── speed.c
│ └── speed.h
└── u128.h
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
polar: # Replace with a single Polar username
buy_me_a_coffee: allkern
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
================================================
FILE: .github/workflows/linux.yml
================================================
name: Ubuntu CI
on:
push:
branches: [ "master" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
submodules: 'recursive'
fetch-depth: 0
- name: Install deps
run: |
sudo add-apt-repository universe
sudo apt-get install build-essential git make \
pkg-config cmake ninja-build gnome-desktop-testing libasound2-dev libpulse-dev \
libaudio-dev libjack-dev libsndio-dev libx11-dev libxext-dev \
libxrandr-dev libxcursor-dev libxfixes-dev libxi-dev libxss-dev libxtst-dev \
libxkbcommon-dev libdrm-dev libgbm-dev libgl1-mesa-dev libgles2-mesa-dev \
libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev libudev-dev libfuse2t64
- name: Build and pack Iris
run: |
git fetch --all --tags
cmake -S . -B build
cmake --build build -j8
sudo cmake --install build
mkdir dist
mv ./build/iris ./dist
mkdir appimage
mv ./build/*.AppImage ./appimage
- uses: actions/upload-artifact@v7
with:
name: iris-latest-linux
path: ./dist
- uses: actions/upload-artifact@v7
with:
name: iris-latest-linux-appimage
path: ./appimage
================================================
FILE: .github/workflows/macos.yml
================================================
name: macOS CI
on:
push:
branches: [ "master" ]
jobs:
macos-universal-build:
runs-on: macos-latest
steps:
- uses: actions/checkout@v6
with:
submodules: 'recursive'
fetch-depth: 0
- name: Install Vulkan SDK
uses: jakoch/install-vulkan-sdk-action@v1
with:
optional_components: com.lunarg.vulkan.volk
install_runtime: true
cache: true
stripdown: true
- name: Build and pack Iris
run: |
git fetch --all --tags
cmake -S . -B build -G "Unix Makefiles"
cmake --build build -j8
sudo cmake --install build
mkdir -p ./build/dist
cp -R ./build/iris.app ./build/dist
- uses: actions/upload-artifact@v7
with:
name: iris-latest-macos-universal
path: ./build/dist
================================================
FILE: .github/workflows/release.yml
================================================
name: Release CI
on:
push:
tags:
- '0.*'
jobs:
windows-build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v6
with:
submodules: 'recursive'
fetch-depth: 0
- name: Build and pack Iris
run: |
git fetch --all --tags
cmake -S . -B build -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=Release
cmake --build build -j8
New-Item -Path "dist" -ItemType Directory
Copy-Item -Path ".\build\iris.exe" "dist"
Copy-Item ".\build\*.dll" "dist"
Copy-Item "C:\mingw64\bin\*.dll" "dist"
- name: Generate artifact name
id: generate-name
run: |
echo "::set-output name=artifact::iris-${{ github.ref_name }}-windows"
- name: Zip artifact
run: |
$artifactName = "${{ steps.generate-name.outputs.artifact }}"
Compress-Archive -Path "dist\*" -DestinationPath "$artifactName.zip"
- name: Upload to release
uses: softprops/action-gh-release@v2.6.1
with:
files: |
${{ steps.generate-name.outputs.artifact }}.zip
linux-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
submodules: 'recursive'
fetch-depth: 0
- name: Install deps
run: |
sudo add-apt-repository universe
sudo apt-get install build-essential git make \
pkg-config cmake ninja-build gnome-desktop-testing libasound2-dev libpulse-dev \
libaudio-dev libjack-dev libsndio-dev libx11-dev libxext-dev \
libxrandr-dev libxcursor-dev libxfixes-dev libxi-dev libxss-dev libxtst-dev \
libxkbcommon-dev libdrm-dev libgbm-dev libgl1-mesa-dev libgles2-mesa-dev \
libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev libudev-dev libfuse2t64
- name: Build and pack Iris
run: |
git fetch --all --tags
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j8
sudo cmake --install build
mkdir dist
mv ./build/iris ./dist
mkdir appimage
mv ./build/*.AppImage ./appimage
- name: Generate artifact name
id: generate-name
run: |
echo "::set-output name=artifact::iris-${{ github.ref_name }}-linux"
- name: Generate AppImage artifact name
id: generate-name-appimage
run: |
echo "::set-output name=artifact::iris-${{ github.ref_name }}-linux-appimage"
- name: Zip artifact
run: |
zip -rj ${{ steps.generate-name.outputs.artifact }}.zip dist
- name: Zip AppImage
run: |
zip -rj ${{ steps.generate-name-appimage.outputs.artifact }}.zip appimage
- name: Upload normal binary to release
uses: softprops/action-gh-release@vv2.6.1
with:
files: |
${{ steps.generate-name.outputs.artifact }}.zip
- name: Upload AppImage to release
uses: softprops/action-gh-release@v2.6.1
with:
files: |
${{ steps.generate-name-appimage.outputs.artifact }}.zip
macos-universal-build:
runs-on: macos-latest
steps:
- uses: actions/checkout@v6
with:
submodules: 'recursive'
fetch-depth: 0
- name: Install Vulkan SDK
uses: jakoch/install-vulkan-sdk-action@v1
with:
optional_components: com.lunarg.vulkan.volk
install_runtime: true
cache: true
stripdown: true
- name: Build and pack Iris
run: |
git fetch --all --tags
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j8
sudo cmake --install build
- name: Generate artifact name
id: generate-name
run: |
echo "::set-output name=artifact::iris-${{ github.ref_name }}-macos-universal"
- name: Zip artifact
run: |
cd build; zip -r ../${{ steps.generate-name.outputs.artifact }}.zip ./iris.app; cd ..
- name: Upload to release
uses: softprops/action-gh-release@v2.6.1
with:
files: |
${{ steps.generate-name.outputs.artifact }}.zip
================================================
FILE: .github/workflows/windows.yml
================================================
name: Windows CI
on:
push:
branches: [ "master" ]
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v6
with:
submodules: 'recursive'
fetch-depth: 0
- name: Build and pack Iris
run: |
git fetch --all --tags
cmake -S . -B build -G "MinGW Makefiles"
cmake --build build -j8
New-Item -Path "dist" -ItemType Directory
Copy-Item -Path ".\build\iris.exe" "dist"
Copy-Item ".\build\*.dll" "dist"
Copy-Item "C:\mingw64\bin\*.dll" "dist"
- name: Upload artifact
uses: actions/upload-artifact@v7
with:
name: iris-latest-windows
path: dist/
================================================
FILE: .gitignore
================================================
SDL2-2.32.0/
.vscode/
build/
bin/
tools/
elf/
roms/
.vs/
ac/
screens/
main2.cpp
compare
compare.cpp
imgui.ini
*.AppImage
*.expected
*.dump
*.cue
*.bin
*.elf
*.exe
*.iso
*.mec
*.nvm
*.mcd
*.wav
*.irx
*.o
.DS_Store
================================================
FILE: .gitmodules
================================================
[submodule "imgui"]
path = deps/imgui
url = https://github.com/ocornut/imgui
branch = docking
[submodule "tomlplusplus"]
path = deps/tomlplusplus
url = https://github.com/marzer/tomlplusplus
[submodule "SDL"]
path = deps/SDL
url = https://github.com/libsdl-org/SDL
branch = release-3.2.24
[submodule "incbin"]
path = deps/incbin
url = https://github.com/graphitemaster/incbin
[submodule "implot"]
path = deps/implot
url = https://github.com/epezent/implot
[submodule "parallel-gs"]
path = deps/parallel-gs
url = https://github.com/Arntzen-software/parallel-gs
[submodule "libchdr"]
path = deps/libchdr
url = https://github.com/rtissera/libchdr
[submodule "stb"]
path = deps/stb
url = https://github.com/nothings/stb
[submodule "portable-file-dialogs"]
path = deps/portable-file-dialogs
url = https://github.com/samhocevar/portable-file-dialogs
[submodule "deps/libdeflate"]
path = deps/libdeflate
url = https://github.com/ebiggers/libdeflate
[submodule "deps/lz4"]
path = deps/lz4
url = https://github.com/lz4/lz4
[submodule "deps/SDL_GameControllerDB"]
path = deps/SDL_GameControllerDB
url = https://github.com/mdqinc/SDL_GameControllerDB
[submodule "deps/asmjit"]
path = deps/asmjit
url = https://github.com/asmjit/asmjit
================================================
FILE: AppImage.cmake
================================================
function(make_appimage)
set(optional)
set(args EXE NAME DIR_ICON ICON OUTPUT_NAME)
set(list_args ASSETS)
cmake_parse_arguments(
PARSE_ARGV 0
ARGS
"${optional}"
"${args}"
"${list_args}"
)
if(${ARGS_UNPARSED_ARGUMENTS})
message(WARNING "Unparsed arguments: ${ARGS_UNPARSED_ARGUMENTS}")
endif()
# download AppImageTool if needed (TODO: non-x86 build machine?)
SET(AIT_PATH "${CMAKE_BINARY_DIR}/AppImageTool.AppImage" CACHE INTERNAL "")
if (NOT EXISTS "${AIT_PATH}")
file(DOWNLOAD https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage "${AIT_PATH}")
execute_process(COMMAND chmod +x ${AIT_PATH})
endif()
# make the AppDir
set(APPDIR "${CMAKE_BINARY_DIR}/AppDir")
file(REMOVE_RECURSE "${APPDIR}") # remove if leftover
file(MAKE_DIRECTORY "${APPDIR}")
# copy executable to appdir
file(COPY "${ARGS_EXE}" DESTINATION "${APPDIR}" FOLLOW_SYMLINK_CHAIN)
get_filename_component(EXE_NAME "${ARGS_EXE}" NAME)
# create the script that will launch the AppImage
file(WRITE "${APPDIR}/AppRun"
"#!/bin/sh
cd \"$(dirname \"$0\")\";
./${EXE_NAME} $@"
)
execute_process(COMMAND chmod +x "${APPDIR}/AppRun")
# copy assets to appdir
file(COPY ${ARGS_ASSETS} DESTINATION "${APPDIR}")
# copy icon thumbnail
file(COPY ${ARGS_DIR_ICON} DESTINATION "${APPDIR}")
get_filename_component(THUMB_NAME "${ARGS_DIR_ICON}" NAME)
file(RENAME "${APPDIR}/${THUMB_NAME}" "${APPDIR}/.DirIcon")
# copy icon highres
file(COPY ${ARGS_ICON} DESTINATION "${APPDIR}")
get_filename_component(ICON_NAME "${ARGS_ICON}" NAME)
get_filename_component(ICON_EXT "${ARGS_ICON}" EXT)
file(RENAME "${APPDIR}/${ICON_NAME}" "${APPDIR}/${ARGS_NAME}${ICON_EXT}")
# Create the .desktop file
file(WRITE "${APPDIR}/${ARGS_NAME}.desktop"
"[Desktop Entry]
Type=Application
Name=${ARGS_NAME}
Icon=${ARGS_NAME}
Categories=X-None;"
)
# Invoke AppImageTool
execute_process(COMMAND ${AIT_PATH} ${APPDIR} ${ARGS_OUTPUT_NAME})
file(REMOVE_RECURSE "${APPDIR}")
endfunction()
================================================
FILE: CMakeLists.txt
================================================
cmake_minimum_required(VERSION 3.21)
project(iris LANGUAGES C CXX)
set(WIN_ICON ${CMAKE_CURRENT_SOURCE_DIR}/res/iris.rc)
set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64")
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15")
set(OSX_ICON ${CMAKE_CURRENT_SOURCE_DIR}/res/iris.icns)
set_source_files_properties(${OSX_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
# Statically link SDL3 for Linux targets
set(SDL_STATIC ON)
set(ASMJIT_STATIC ON)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
find_package(Git QUIET)
if (GIT_FOUND)
execute_process(
COMMAND ${GIT_EXECUTABLE} describe --tags --always --dirty --match "0.*"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
OUTPUT_VARIABLE GIT_VERSION_STRING
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
)
if(NOT GIT_VERSION_STRING)
# Fallback if git describe fails (e.g., no tags in the history)
set(GIT_VERSION_STRING "unknown-version")
endif()
else()
set(GIT_VERSION_STRING "git-not-found")
endif()
message(STATUS "Project Version: ${GIT_VERSION_STRING}")
# You can then use GIT_VERSION_STRING in your project, e.g., to generate a header file:
# configure_file(
# ${CMAKE_CURRENT_SOURCE_DIR}/version.h.in
# ${CMAKE_CURRENT_BINARY_DIR}/version.h
# @ONLY
# )
include(CheckIPOSupported)
check_ipo_supported(RESULT LTO_SUPPORTED OUTPUT LTO_ERROR)
set(BUILD_SHARED_LIBS OFF)
# And this part tells CMake where to find and install the file itself
add_executable(iris MACOSX_BUNDLE ${OSX_ICON} ${WIN_ICON})
target_compile_definitions(iris PUBLIC IMGUI_IMPL_VULKAN_NO_PROTOTYPES)
target_compile_definitions(iris PUBLIC IMGUI_IMPL_VULKAN_USE_VOLK)
target_compile_options(iris PUBLIC "-Wno-deprecated-declarations")
# target_compile_definitions(iris PUBLIC _DEBUG)
set(PARALLEL_GS_STANDALONE ON CACHE BOOL "" FORCE)
add_subdirectory(deps/asmjit EXCLUDE_FROM_ALL)
add_subdirectory(deps/tomlplusplus EXCLUDE_FROM_ALL)
add_subdirectory(deps/libdeflate EXCLUDE_FROM_ALL)
add_subdirectory(deps/parallel-gs EXCLUDE_FROM_ALL)
add_subdirectory(deps/libchdr EXCLUDE_FROM_ALL)
add_subdirectory(deps/SDL EXCLUDE_FROM_ALL)
if (CMAKE_SYSTEM_PROCESSOR MATCHES "AMD64")
target_compile_options(iris PRIVATE -D_EE_USE_INTRINSICS -mssse3 -msse4.1)
endif()
if (X11_API)
target_compile_definitions(granite-volk PUBLIC VK_USE_PLATFORM_XLIB_KHR)
endif()
if (WAYLAND_API)
target_compile_definitions(granite-volk PUBLIC VK_USE_PLATFORM_WAYLAND_KHR)
endif()
if (WIN32)
target_compile_definitions(granite-volk PUBLIC VK_USE_PLATFORM_WIN32_KHR)
endif()
if (NOT CMAKE_SYSTEM_NAME MATCHES "Windows")
if (LTO_SUPPORTED)
message(STATUS "IPO/LTO enabled")
set_property(TARGET iris PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
else()
message(STATUS "IPO/LTO not supported: ${LTO_ERROR}")
endif()
endif()
set_property(TARGET iris PROPERTY CXX_STANDARD 20)
add_definitions("-D_IRIS_VERSION=${GIT_VERSION_STRING}")
target_sources(iris PRIVATE
main.cpp
frontend/audio.cpp
frontend/handlers.cpp
frontend/vulkan.cpp
frontend/imgui.cpp
frontend/emu.cpp
frontend/render.cpp
frontend/shaders.cpp
frontend/input.cpp
frontend/iris.cpp
frontend/elf.cpp
frontend/notifications.cpp
frontend/settings.cpp
frontend/ui/about.cpp
frontend/ui/bios_setting.cpp
frontend/ui/breakpoints.cpp
frontend/ui/control.cpp
frontend/ui/dma.cpp
frontend/ui/gs.cpp
frontend/ui/intc.cpp
frontend/ui/logs.cpp
frontend/ui/memory.cpp
frontend/ui/memory_card_tool.cpp
frontend/ui/memory_search.cpp
frontend/ui/menubar.cpp
frontend/ui/modules.cpp
frontend/ui/overlay.cpp
frontend/ui/pad.cpp
frontend/ui/settings.cpp
frontend/ui/spu2.cpp
frontend/ui/state.cpp
frontend/ui/statusbar.cpp
frontend/ui/symbols.cpp
frontend/ui/threads.cpp
frontend/ui/vu_disassembly.cpp
src/ps2.c
src/ps2_elf.c
src/ps2_iso9660.c
src/queue.c
src/rom.c
src/md5.c
src/list.c
src/scheduler.c
src/dev/ds.c
src/dev/guncon.c
src/dev/mcd.c
src/dev/mtap.c
src/dev/ps1_mcd.c
src/dev/ps1_mcd.c
src/ee/ee_cached.cpp
src/ee/bus.c
src/ee/dmac.c
src/ee/ee_dis.c
src/ee/gif.c
src/ee/intc.c
src/ee/timers.c
src/ee/vif.c
src/ee/vu_cached.cpp
src/ee/vu_dis.c
src/gs/gs.c
src/gs/renderer/null.cpp
src/gs/renderer/renderer.cpp
src/gs/renderer/hardware.cpp
src/iop/bus.c
src/iop/cdvd.c
src/iop/disc.c
src/iop/dma.c
src/iop/fw.c
src/iop/intc.c
src/iop/iop.c
src/iop/iop_dis.c
src/iop/iop_export.c
src/iop/rpc.c
src/iop/sio2.c
src/iop/spu2.c
src/iop/timers.c
src/iop/usb.c
src/iop/disc/bin.c
src/iop/disc/cue.c
src/iop/disc/chd.c
src/iop/disc/ciso.c
src/iop/disc/iso.c
src/iop/hle/ioman.cpp
src/iop/hle/loadcore.c
src/iop/hle/sysmem.c
src/ipu/chromtable.cpp
src/ipu/codedblockpattern.cpp
src/ipu/dct_coeff.cpp
src/ipu/dct_coeff_table0.cpp
src/ipu/dct_coeff_table1.cpp
src/ipu/ipu.cpp
src/ipu/ipu_fifo.cpp
src/ipu/lumtable.cpp
src/ipu/mac_addr_inc.cpp
src/ipu/mac_b_pic.cpp
src/ipu/mac_i_pic.cpp
src/ipu/mac_p_pic.cpp
src/ipu/motioncode.cpp
src/ipu/vlc_table.cpp
src/shared/bios.c
src/shared/dev9.c
src/shared/ram.c
src/shared/sbus.c
src/shared/sif.c
src/shared/speed.c
src/shared/speed/ata.c
src/shared/speed/eeprom.c
src/shared/speed/flash.c
src/s14x/nand.c
src/s14x/syscon.c
src/s14x/sram.c
src/s14x/link.c
src/s14x/ioboard.c
src/s14x/aiboard.c
deps/imgui/imgui.cpp
deps/imgui/imgui_demo.cpp
deps/imgui/imgui_draw.cpp
deps/imgui/imgui_tables.cpp
deps/imgui/imgui_widgets.cpp
deps/imgui/backends/imgui_impl_sdl3.cpp
deps/imgui/backends/imgui_impl_vulkan.cpp
deps/implot/implot_demo.cpp
deps/implot/implot_items.cpp
deps/implot/implot.cpp
deps/lz4/lib/lz4.c
)
target_include_directories(iris PRIVATE
deps/asmjit
deps/imgui
deps/imgui/backends
deps/implot
deps/SDL/include
deps/incbin
deps/parallel-gs
deps/libchdr/include
deps/lz4/lib
deps/stb
deps/portable-file-dialogs
frontend
src
res
)
target_link_libraries(iris PUBLIC
asmjit::asmjit
libdeflate::libdeflate_static
tomlplusplus::tomlplusplus
SDL3::SDL3-static
parallel-gs
chdr-static
)
if (CMAKE_SYSTEM_NAME MATCHES "Darwin")
find_package(Vulkan COMPONENTS MoltenVK)
message(STATUS "VulkanSDK path: $ENV{VULKAN_SDK}")
find_library(MOLTENVK_LIBRARY
NAMES libMoltenVK.dylib
PATHS ENV VULKAN_SDK
PATH_SUFFIXES /lib
)
target_link_libraries(iris PUBLIC
Vulkan::Vulkan
${MOLTENVK_LIBRARY}
)
endif()
if (WIN32)
target_link_libraries(iris PRIVATE dwmapi)
target_sources(iris PRIVATE frontend/platform/windows.cpp)
else()
target_sources(iris PRIVATE frontend/platform/stub.cpp)
endif()
set_target_properties(iris PROPERTIES
# On macOS, make a proper .app bundle instead of a bare executable
MACOSX_BUNDLE TRUE
# Set the Info.plist file for Apple Mobile platforms. Without this file, your app
# will not launch.
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist"
MACOSX_BUNDLE_ICON_FILE iris.icns
# in Xcode, create a Scheme in the schemes dropdown for the app.
XCODE_GENERATE_SCHEME TRUE
# Identification for Xcode
XCODE_ATTRIBUTE_BUNDLE_IDENTIFIER "com.allkern.iris"
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "com.allkern.iris"
XCODE_ATTRIBUTE_CURRENTYEAR "${CURRENTYEAR}"
RESOURCE "${RESOURCE_FILES}"
)
# on Visual Studio, set our app as the default project
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT "${EXECUTABLE_NAME}")
# On macOS Platforms, ensure that the bundle is valid for distribution by calling fixup_bundle.
# note that fixup_bundle does not work on iOS, so you will want to use static libraries
# or manually copy dylibs and set rpaths
message(STATUS "CMake System Name: ${CMAKE_SYSTEM_NAME}")
if (CMAKE_SYSTEM_NAME MATCHES "Darwin")
# tell Install about the target, otherwise fixup won't know about the transitive dependencies
install(TARGETS iris
BUNDLE DESTINATION ./install COMPONENT Runtime
RUNTIME DESTINATION ./install/bin COMPONENT Runtime
)
set(BUNDLE_PATH "${CMAKE_BINARY_DIR}/iris.app") # where to look for dependencies when fixing up
file(GLOB VULKAN_DYLIBS $ENV{VULKAN_SDK}/lib/libvulkan.*.dylib)
set(VULKAN_DYLIBS ${VULKAN_DYLIBS} $ENV{VULKAN_SDK}/lib/libMoltenVK.dylib)
install(CODE "
execute_process(COMMAND echo \"Preparing to bundle iris...\")
execute_process(COMMAND echo \"Bundle path: ${BUNDLE_PATH}\")
execute_process(COMMAND echo \"Vulkan dylibs: ${VULKAN_DYLIBS}\")
execute_process(COMMAND echo \"Adding local RPATH...\")
execute_process(
COMMAND install_name_tool -add_rpath
\"@executable_path/../Frameworks\"
\"${BUNDLE_PATH}/Contents/MacOS/iris\"
)
execute_process(COMMAND echo \"Creating ICD JSON directory...\")
make_directory(${BUNDLE_PATH}/Contents/Resources/vulkan/icd.d)
execute_process(COMMAND echo \"Creating Frameworks directory...\")
make_directory(${BUNDLE_PATH}/Contents/Frameworks)
execute_process(COMMAND echo \"Copying files...\")
file(COPY
${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK_icd.json
DESTINATION ${BUNDLE_PATH}/Contents/Resources/vulkan/icd.d/
)
file(COPY
${VULKAN_DYLIBS}
DESTINATION ${BUNDLE_PATH}/Contents/Frameworks/
)
execute_process(COMMAND echo \"Signing app bundle...\")
execute_process(COMMAND
codesign --force --deep --sign - ${BUNDLE_PATH}
RESULT_VARIABLE codesign_result
OUTPUT_VARIABLE codesign_output
)
if (codesign_result)
execute_process(COMMAND echo \"${codesign_output}\")
endif()
")
set(CPACK_GENERATOR "DragNDrop")
include(CPack)
endif()
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
install(CODE
"include(${CMAKE_CURRENT_SOURCE_DIR}/AppImage.cmake)
make_appimage(
EXE \"${CMAKE_CURRENT_SOURCE_DIR}/build/iris\"
NAME \"Iris\"
ICON \"${CMAKE_CURRENT_SOURCE_DIR}/res/iris.png\"
DIR_ICON \"${CMAKE_CURRENT_SOURCE_DIR}/res/iris.png\"
OUTPUT_NAME \"${CMAKE_CURRENT_SOURCE_DIR}/build/Iris-${GIT_VERSION_STRING}.AppImage\"
)
"
COMPONENT Runtime
)
endif()
================================================
FILE: Info.plist
================================================
CFBundleDevelopmentRegion
en
CFBundleExecutable
iris
CFBundleIdentifier
iris.app.0
CFBundleInfoDictionaryVersion
6.0
CFBundleName
Iris
CFBundlePackageType
APPL
CFBundleShortVersionString
0.10-alpha
CFBundleSignature
iris
CFBundleVersion
0.10-alpha
LSApplicationCategoryType
public.app-category.games
LSMinimumSystemVersion
10.15
NSHighResolutionCapable
NSPrincipalClass
NSApplication
CFBundleIconFile
iris
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2025 Allkern/Lisandro Alarcon
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.
================================================
FILE: MoltenVK_icd.json
================================================
{
"file_format_version" : "1.0.0",
"ICD": {
"library_path": "../../../Frameworks/libMoltenVK.dylib",
"api_version" : "1.4.0",
"is_portability_driver": true
}
}
================================================
FILE: README.md
================================================
# 🐣 Iris
Sony PlayStation 2 emulator for Windows, Linux and macOS
## Screenshots
## Usage
> [!WARNING]
> This emulator is under development, most games WILL run at very low/unplayable framerates.
### GUI
Navigate over to `Iris > Open...` and choose a disc image or ELF executable, drag-and-drop is also supported
### CLI
```
Usage: iris [OPTION]...
-b, --bios Specify a PlayStation 2 BIOS dump file
--rom1 Specify a DVD player dump file
--rom2 Specify a ROM2 dump file
-d, --boot Specify a direct kernel boot path
-i, --disc Specify a path to a disc image file
-x, --executable Specify a path to an ELF executable to be
loaded on system startup
--slot1 Specify a path to a memory card file to
be inserted on slot 1
--slot2 Specify a path to a memory card file to
be inserted on slot 2
-h, --help Display this help and exit
-v, --version Output version information and exit
```
## Features
- Support for ISO, BIN/CUE, CHD and CSO/ZSO disc image formats
- Hardware-accelerated Vulkan GS renderer with support for up to 16x SSAA
- Feature-packed debugger
- Easy to use graphical interface
- Game controller support with input remapping
- Support for post-processing shaders
## Building
> [!WARNING]
> Building requires CMake and the Vulkan SDK on all supported platforms
### Linux
Building on Linux requires installing SDL3 dependencies and FUSE if you wish to generate AppImages.
```
sudo apt update
sudo apt upgrade
sudo add-apt-repository universe
sudo apt-get install build-essential git make \
pkg-config cmake ninja-build gnome-desktop-testing libasound2-dev libpulse-dev \
libaudio-dev libjack-dev libsndio-dev libx11-dev libxext-dev \
libxrandr-dev libxcursor-dev libxfixes-dev libxi-dev libxss-dev libxtst-dev \
libxkbcommon-dev libdrm-dev libgbm-dev libgl1-mesa-dev libgles2-mesa-dev \
libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev libudev-dev libfuse2t64
```
Then clone the repository and run CMake:
```
git clone https://github.com/allkern/iris --recursive
cd iris
cmake -S . -B build
cmake --build build -j8
```
Optionally run `cmake --install build` to generate an AppImage.
### Windows
We currently only support GCC as a compiler on Windows, this is because MSVC doesn't have an inline assembler, which we need to embed resources into the executable. This might eventually be fixed though!
```
git clone https://github.com/allkern/iris --recursive
cd iris
cmake -S . -B build -G "MinGW Makefiles"
cmake --build build -j8
```
### macOS
```
git clone https://github.com/allkern/iris --recursive
cd iris
cmake -S . -B build
cmake --build build -j8
```
Optionally run `sudo cmake --install build` to generate a macOS App Bundle
## Progress/Insights
Iris can boot/run a fairly large number of commercial games, playability may be all over the place though, some games run fairly smoothly, while others can't break the 1 digit FPS mark, this is due to the lack of EE/VU JITs which will be addressed soon.
The PlayStation 2 can have up to three processors running simultaneously at ~300 MHz, plus the IOP running at 33 MHz, not counting all the different peripherals/chips doing their own work, such as the GS rendering massive amounts of graphics, the IPU decoding MPEG-2 video, the SPU2 rendering up to 48 ADPCM audio channels at 48 KHz, and more. You can see how emulating this system can become a pretty complex task once you factor in all the processing that's done per-frame.
In order to alleviate the struggles of emulating the PS2, we have a number of optimization techniques at our disposal, some of which have already been implemented:
- Scheduling (done)
- Software fastmem (done)
- EE interpreter caching (done)
- Hardware-accelerated GS rendering (done)
- EE JIT/Dynarec (coming up)
- VU JIT/Dynarec (soon)
- Hardware fastmem (eventually)
- etc.
Integrating Parallel-GS was a big milestone for Iris, now we need to work on JITing the EE. Once that's done, I'd expect the emulator to start running a lot more games at playable speeds.
## Preservation
Iris aims to emulate not only the retail PlayStation 2, but also other systems based on the PS2, such as the PSX DESR (Japanese PS2/DVR hybrid) and all or most of the PS2-based arcade systems, work towards this goal is ongoing, in fact, Iris was the first PS2 emulator to boot [the PSX DESR BIOS/bootrom](https://www.youtube.com/watch?v=YtsoRjofYKA).
In order to emulate these systems, a pile of extra hardware needs to be implemented, such as the onboard flash memory on the PSX DESR, and the NAND storage on all of the Namco boards. This is not trivial and the lack of documentation makes it a pretty daunting task, but we're working on it.
It's worth to mention that PSX DESR support has been merged to the main branch, which means you (our beloved user) should be able to dump the BIOS out of your PSX DESR machine and run it on Iris. It won't boot past some error screens after running the boot animation but I'd say it's still pretty cool.
In addition to emulating other systems based on the PS2, Iris also aims to support **all** the features/capabilities of the retail system, this includes the often overlooked DVD player, PSBBN, Linux, and eventually all of the available external USB/SIO peripherals and input/output devices.
# Special thanks and acknowledgements
I would like to thank the emudev Discord server, Ziemas, Nelson (ncarrillo), cakehonolulu, PSI-rockin, noumi and the PCSX2 team for their kind support.
This project makes use of the following third-party libraries:
- [ImGui](https://github.com/ocornut/imgui)
- [ImPlot](https://github.com/epezent/implot)
- [SDL3](https://github.com/libsdl-org/SDL)
- [SDL_GameControllerDB](https://github.com/mdqinc/SDL_GameControllerDB)
- [incbin](https://github.com/graphitemaster/incbin)
- [Parallel-GS](https://github.com/Arntzen-software/parallel-gs)
- [libchdr](https://github.com/rtissera/libchdr)
- [libdeflate](https://github.com/ebiggers/libdeflate)
- [lz4](https://github.com/lz4/lz4)
- [toml++](https://marzer.github.io/tomlplusplus/)
- [Portable File Dialogs](https://github.com/samhocevar/portable-file-dialogs)
- [stb_image](https://github.com/nothings/stb)
Credit goes out to the developers of these libraries, Iris wouldn't have been possible without your outstanding work.
### Components
This console is significantly more complex compared to the PS1, here's a rough list of components:
```
🟡 EE (R5900) CPU
- 🟡 FPU
- 🟡 MMI (SIMD)
- 🟡 TLB
- 🟡 DMAC
- 🟢 INTC
- 🟡 Timers
- 🟢 GIF
- 🟡 GS
- 🟡 VU0
= 🟡 Macro mode
= 🟡 Micro mode
= 🟡 VIF0
- 🟡 VU1 (always micro mode)
= 🟡 VIF1
- 🟡 IPU
🟢 IOP (R3000) CPU
- 🟡 DMAC
- 🟢 INTC
- 🟡 Timers
- 🟢 CDVD
- 🟢 SIO2 (controllers and Memory Cards)
- 🟢 SPU2
- 🟡 DEV9
- 🟡 USB/FireWire?
- 🔴 Ethernet
- 🔴 PS1 backcompat (PS1 hardware)
🟢 SIF
```
================================================
FILE: compat.txt
================================================
".hack - Infection (USA)" - Boots to menus, runs very slow
"007 - Nightfire (USA)" - Hangs trying to play an FMV at the very beginning
"Arcana Heart (USA)" - REGRESSION! Seems to trash IOP memory with what looks like ADPCM data
"Babe (Australia)" - Boots, won't boot with FMV skipping enabled though
"Batman - Vengeance (USA)" - Requires MFIFO
"Big Mutha Truckers (USA)" - Prints "CD Error 0xfd, ditching this request", reads sectors 1eb02,1eb12,1eb22 repeatedly
"Buffy the Vampire Slayer - Chaos Bleeds (USA)" - Gets stuck waiting for GIF_STAT.8-9 to be non-zero
"Bully (USA)" - Boots in-game, runs incredibly slow, textures broken, image looks yellow tinted (similar to GoW)
"Dead or Alive 2 (Japan)" - Nothing, prints "Read Time Out 15000(msec)" to the terminal, possibly CDVD-related?
"Final Fantasy XII (USA)" - Enables MFIFO through an 8-bit write to CTRL, uses transfer "mode 3" (actually just chain mode)
"Gallop Racer 2006 (USA)" - Requires proper EE timings (at most 2x underclock)
"Klonoa 2 - Lunatea's Veil (USA)" - Doesn't work on Release builds (!), requires 16-bit DMAC writes
"Namco Museum 50th Anniversary (USA)" - Requires MFIFO
"R-Type Final (Japan)" - Requires MFIFO
"Sega Ages 2500 Series Vol. 23 - Sega Memorial Selection (Japan)" - GIF DMA read from NULL
"Sega Genesis Collection (USA)" - Works, "forgets" to change the videomode causing wrong graphics at startup (needs 24-bit 640x448 interlaced?)
"Simpsons, The - Hit & Run (USA)" - Needs MFIFO
"Tekken 4 (USA)" - Needs MFIFO
"Tekken Tag Tournament (USA) (v1.00)" - Needs MFIFO
"Thunder Force VI (Japan)" - Crashes on an invalid GIF DMA address when starting up (compare against Dobie)
"Virtua Fighter 4 - Evolution (USA)" - Works, uses filling mode
"Virtua Fighter - Cyber Generation - Judgment Six no Yabou (Japan)" - PMTHL unimplemented
"We Love Katamari (USA)" - Uses filling mode, gets stuck trying to play an FMV after the Namco logo?
================================================
FILE: frontend/arcade.hpp
================================================
#include
#include "ps2.h"
const toml::table g_arcade_definitions = toml::table {
{ "pacmanap", toml::table {
{ "system", PS2_SYSTEM_NAMCO_S147 },
{ "name", "Pac-Man's Arcade Party" },
{ "nand", "kp007a_k9k8g08u0b_pmaam12-na-c.ic26" },
{ "bios", "common_system147b_bootrom.ic1" },
{ "boot", "atfile0:PMAAC.elf" }
}},
{ "pacmanbr", toml::table {
{ "system", PS2_SYSTEM_NAMCO_S147 },
{ "name", "Pac-Man: Battle Royale" },
{ "nand", "pbr102-2-na-mpro-a13_kp006b.ic26" },
{ "bios", "common_system147b_bootrom.ic1" },
{ "boot", "atfile0:pacmanBR.elf" }
}},
{ "akaiser", toml::table {
{ "system", PS2_SYSTEM_NAMCO_S147 },
{ "name", "Animal Kaiser: The King of Animals" },
{ "nand", "kp005a_ana1004-na-b.ic26" },
{ "bios", "common_system147b_bootrom.ic1" },
{ "boot", "atfile0:main.elf" },
{ "ioboard_mode", 1 }
}},
{ "akaievo", toml::table {
{ "system", PS2_SYSTEM_NAMCO_S147 },
{ "name", "Animal Kaiser Evolution" },
{ "nand", "kp012b_k9k8g08u0b.ic31" },
{ "bios", "common_system147b_bootrom.ic1" },
{ "boot", "atfile0:main.elf" },
{ "ioboard_mode", 1 }
}},
{ "umilucky", toml::table {
{ "system", PS2_SYSTEM_NAMCO_S148 },
{ "name", "Umimonogatari Lucky Marine Theater" },
{ "nand", "uls100-1-na-mpro-b01_kp008a.ic31" },
{ "bios", "common_system148_bootrom.ic1" },
{ "boot", "atfile0:prog.elf" }
}}
};
================================================
FILE: frontend/audio.cpp
================================================
#include
#include
#include "iris.hpp"
namespace iris::audio {
void update(void* userdata, SDL_AudioStream* stream, int additional_amount, int total_amount) {
iris::instance* iris = (iris::instance*)userdata;
if (iris->pause)
std::this_thread::sleep_for(std::chrono::milliseconds(1));
if (iris->pause || !additional_amount)
return;
iris->audio_buf.resize(additional_amount);
for (int i = 0; i < additional_amount; i++) {
iris->audio_buf[i] = ps2_spu2_get_sample(iris->ps2->spu2, !iris->mute_adma);
iris->audio_buf[i].s16[0] *= iris->mute ? 0.0f : iris->volume;
iris->audio_buf[i].s16[1] *= iris->mute ? 0.0f : iris->volume;
}
SDL_PutAudioStreamData(stream, (void*)iris->audio_buf.data(), additional_amount * sizeof(spu2_sample));
}
bool init(iris::instance* iris) {
SDL_AudioSpec spec;
spec.channels = 2;
spec.format = SDL_AUDIO_S16;
spec.freq = 48000;
iris->stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec, iris::audio::update, iris);
if (!iris->stream) {
fprintf(stderr, "audio: Failed to open audio device\n");
return false;
}
/* SDL_OpenAudioDeviceStream starts the device paused. You have to tell it to start! */
SDL_ResumeAudioStreamDevice(iris->stream);
return true;
}
void close(iris::instance* iris) {
if (!iris->stream) {
return;
}
SDL_PauseAudioStreamDevice(iris->stream);
SDL_DestroyAudioStream(iris->stream);
iris->stream = nullptr;
}
bool mute(iris::instance* iris) {
iris->prev_mute = iris->mute;
iris->mute = true;
return iris->prev_mute;
}
void unmute(iris::instance* iris) {
iris->mute = iris->prev_mute;
}
}
================================================
FILE: frontend/config.hpp
================================================
#pragma once
#ifndef _IRIS_VERSION
#define _IRIS_VERSION latest
#endif
#ifndef _IRIS_COMMIT
#define _IRIS_COMMIT latest
#endif
#ifndef _IRIS_OSVERSION
#define _IRIS_OSVERSION unknown
#endif
#define STR1(m) #m
#define STR(m) STR1(m)
#define IRIS_TITLE "Iris (" STR(_IRIS_VERSION) ")"
#define IRIS_VULKAN_API_VERSION VK_API_VERSION_1_2
================================================
FILE: frontend/elf.cpp
================================================
#include
#include
#include
#include "iris.hpp"
#ifdef __linux__
#include
#else
#include "elf.h"
#endif
namespace iris::elf {
void load_symbols_from_memory(iris::instance* iris, char* buf) {
if (!buf)
return;
// Clear previous symbols
iris->symbols.clear();
iris->strtab.clear();
Elf32_Ehdr* ehdr = (Elf32_Ehdr*)buf;
// Parse ELF header
if (strncmp((char*)ehdr->e_ident, "\x7f" "ELF", 4) != 0) {
printf("elf: Invalid ELF magic number\n");
return;
}
// Read symbol table header
Elf32_Shdr* symtab = nullptr;
for (int i = 0; i < ehdr->e_shnum; i++) {
Elf32_Shdr* shdr = (Elf32_Shdr*)(buf + ehdr->e_shoff + (i * ehdr->e_shentsize));
if ((shdr->sh_type == SHT_STRTAB) && (i != ehdr->e_shstrndx)) {
printf("elf: Loading string table size=%x offset=%x\n", shdr->sh_size, shdr->sh_offset);
// Get string table
iris->strtab.resize(shdr->sh_size);
memcpy(iris->strtab.data(), buf + shdr->sh_offset, shdr->sh_size);
}
if (shdr->sh_type == SHT_SYMTAB) {
symtab = shdr;
printf("elf: Found symbol table size=%x offset=%x\n", symtab->sh_size, symtab->sh_offset);
}
}
// No symbol table present
if (!symtab) {
printf("elf: No symbol table found\n");
return;
}
if (!symtab->sh_entsize) {
printf("elf: Invalid symbol table entry size\n");
return;
}
if (!symtab->sh_size) {
printf("elf: Symbol table is empty\n");
return;
}
size_t symbol_count = symtab->sh_size / symtab->sh_entsize;
printf("elf: Found symbol table with %d symbols\n", symbol_count);
// Read symbol table
Elf32_Sym* sym;
for (int i = 0; i < symbol_count; i++) {
sym = (Elf32_Sym*)(buf + symtab->sh_offset + (i * symtab->sh_entsize));
if (ELF32_ST_TYPE(sym->st_info) != STT_FUNC)
continue;
elf_symbol symbol;
symbol.name = (char*)(iris->strtab.data() + sym->st_name);
symbol.addr = sym->st_value;
symbol.size = sym->st_size;
// printf("symbol: %s at 0x%08x\n", symbol.name, symbol.addr);
iris->symbols.push_back(symbol);
}
}
bool load_symbols_from_disc(iris::instance* iris) {
if (!iris->ps2 || !iris->ps2->cdvd || !iris->ps2->cdvd->disc) {
printf("elf: No disc loaded\n");
return false;
}
char* elf = disc_read_boot_elf(iris->ps2->cdvd->disc, 0);
load_symbols_from_memory(iris, elf);
free(elf);
return true;
}
bool load_symbols_from_file(iris::instance* iris, std::string path) {
if (path.empty()) {
printf("elf: No file path provided\n");
return false;
}
FILE* file = fopen(path.c_str(), "rb");
if (!file) {
printf("elf: Failed to open file %s\n", path.c_str());
return false;
}
fseek(file, 0, SEEK_END);
size_t size = ftell(file);
fseek(file, 0, SEEK_SET);
char* buf = new char[size];
fread(buf, 1, size, file);
fclose(file);
load_symbols_from_memory(iris, buf);
delete[] buf;
return true;
}
}
================================================
FILE: frontend/emu.cpp
================================================
#include "iris.hpp"
#include "arcade.hpp"
#include
#include
namespace iris::emu {
bool init(iris::instance* iris) {
// Initialize our emulator state
iris->ps2 = ps2_create();
ps2_init(iris->ps2);
ps2_init_tty_handler(iris->ps2, PS2_TTY_EE, iris::handle_ee_tty_event, iris);
ps2_init_tty_handler(iris->ps2, PS2_TTY_IOP, iris::handle_iop_tty_event, iris);
ps2_init_tty_handler(iris->ps2, PS2_TTY_SYSMEM, iris::handle_sysmem_tty_event, iris);
iris->ds[0] = ds_attach(iris->ps2->sio2, 0);
return true;
}
void destroy(iris::instance* iris) {
if (iris->ps2) ps2_destroy(iris->ps2);
}
const char* get_extension(const char* path) {
const char* dot = strrchr(path, '.');
if (!dot || dot == path)
return nullptr;
return dot + 1;
}
template std::optional query_arcade_value(std::string arcade_name, std::string key) {
auto it = g_arcade_definitions.find(arcade_name);
if (it == g_arcade_definitions.end())
return {};
auto arcade_table = it->second.as_table();
auto key_it = arcade_table->find(key);
if (key_it == arcade_table->end())
return {};
if constexpr (std::is_same_v) {
return key_it->second.as_string()->get();
} else if constexpr (std::is_integral_v) {
return key_it->second.as_integer()->get();
} else if constexpr (std::is_same_v) {
return key_it->second.as_boolean()->get();
} else if constexpr (std::is_floating_point_v) {
return key_it->second.as_floating_point()->get();
} else if constexpr (std::is_array_v) {
return key_it->second.as_array();
} else {
return {};
}
return {};
}
bool load_arcade(iris::instance* iris, std::string path) {
std::filesystem::path base_path(path);
std::string id = base_path.stem().string();
std::string name = query_arcade_value(id, "name").value_or("");
if (!name.size()) {
return false;
}
printf("emu: Loading arcade game \"%s\"...\n", id.c_str(), name.c_str());
int system = query_arcade_value(id, "system").value_or(PS2_SYSTEM_AUTO);
switch (system) {
case PS2_SYSTEM_NAMCO_S147:
case PS2_SYSTEM_NAMCO_S148: {
std::string bios = query_arcade_value(id, "bios").value_or("");
std::string nand = query_arcade_value(id, "nand").value_or("");
int ioboard_mode = query_arcade_value(id, "ioboard_mode").value_or(0);
std::filesystem::path bios_path = base_path / bios;
std::filesystem::path nand_path = base_path / nand;
std::filesystem::path sram_path = base_path / "sram.bin";
if (!std::filesystem::exists(bios_path)) {
printf("emu: Couldn't find bootrom file \"%s\"\n", bios_path.string().c_str());
push_info(iris, "Couldn't start arcade game (Missing bootrom)");
return false;
}
if (!std::filesystem::exists(nand_path)) {
printf("emu: Couldn't find NAND file \"%s\"\n", nand_path.string().c_str());
push_info(iris, "Couldn't start arcade game (Missing NAND)");
return false;
}
ps2_set_system(iris->ps2, system);
ps2_load_bios(iris->ps2, bios_path.string().c_str());
s14x_nand_load(iris->ps2->s14x_nand, nand_path.string().c_str());
s14x_sram_load(iris->ps2->s14x_sram, sram_path.string().c_str());
if (iris->ps2->s14x_ioboard) {
iris->ps2->s14x_ioboard->mode = ioboard_mode;
}
ps2_reset(iris->ps2);
iris->loaded = name + " (" + id + ")";
if (iris->autostart) {
iris->pause = false;
}
return true;
} break;
default: {
const char* names[] = {
"Auto",
"Retail (Fat)",
"Retail (Slim)",
"PSX DESR",
"TEST unit (DTL-H)",
"TOOL unit (DTL-T)",
"Konami Python",
"Konami Python 2",
"Namco System 147",
"Namco System 148",
"Namco System 246",
"Namco System 256"
};
printf("emu: %s isn't supported yet\n", names[system]);
} break;
}
return false;
}
int attach_memory_card(iris::instance* iris, int slot, const char* path) {
detach_memory_card(iris, slot);
FILE* file = fopen(path, "rb");
if (!file) {
return 0;
}
fseek(file, 0, SEEK_END);
int size = ftell(file);
fclose(file);
if (size < 0x800000) {
struct ps1_mcd_state* mcd = ps1_mcd_attach(iris->ps2->sio2, slot+2, path);
std::string ext = get_extension(path);
if (ext == "psm" || ext == "pocket") {
ps1_mcd_set_type(mcd, 1);
iris->mcd_slot_type[slot] = 3;
} else {
ps1_mcd_set_type(mcd, 0);
iris->mcd_slot_type[slot] = 2;
}
return 1;
}
mcd_attach(iris->ps2->sio2, slot+2, path);
iris->mcd_slot_type[slot] = 1;
return 1;
}
void detach_memory_card(iris::instance* iris, int slot) {
iris->mcd_slot_type[slot] = 0;
ps2_sio2_detach_device(iris->ps2->sio2, slot+2);
}
const char* g_system_names[] = {
"Auto",
"PlayStation 2 (Fat)",
"PlayStation 2 (Slim)",
"PSX DESR",
"TEST Unit",
"TOOL Unit",
"Konami Python",
"Konami Python 2",
"Namco System 147",
"Namco System 148",
"Namco System 246",
"Namco System 256"
};
const char* get_system_name(iris::instance* iris, int system) {
return g_system_names[system];
}
const char* get_current_system_name(iris::instance* iris) {
switch (iris->system) {
case PS2_SYSTEM_AUTO: return get_system_name(iris, iris->ps2->detected_system);
case PS2_SYSTEM_RETAIL:
case PS2_SYSTEM_RETAIL_DECKARD:
case PS2_SYSTEM_DESR:
case PS2_SYSTEM_TEST:
case PS2_SYSTEM_TOOL:
case PS2_SYSTEM_KONAMI_PYTHON:
case PS2_SYSTEM_KONAMI_PYTHON2:
case PS2_SYSTEM_NAMCO_S147:
case PS2_SYSTEM_NAMCO_S148:
case PS2_SYSTEM_NAMCO_S246:
case PS2_SYSTEM_NAMCO_S256:
return g_system_names[iris->system];
default: return "Unknown";
}
}
int get_system_count(iris::instance* iris) {
return sizeof(g_system_names) / sizeof(const char*);
}
}
================================================
FILE: frontend/handlers.cpp
================================================
#include "iris.hpp"
namespace iris {
void handle_ee_tty_event(void* udata, char c) {
iris::instance* iris = (iris::instance*)udata;
if (c == '\r')
return;
if (c == '\n') {
iris->ee_log.push_back("");
} else {
iris->ee_log.back().push_back(c);
}
}
void handle_iop_tty_event(void* udata, char c) {
iris::instance* iris = (iris::instance*)udata;
if (c == '\r')
return;
if (c == '\n') {
iris->iop_log.push_back("");
} else {
iris->iop_log.back().push_back(c);
}
}
void handle_sysmem_tty_event(void* udata, char c) {
iris::instance* iris = (iris::instance*)udata;
if (c == '\r')
return;
if (c == '\n') {
iris->sysmem_log.push_back("");
} else {
iris->sysmem_log.back().push_back(c);
}
}
}
================================================
FILE: frontend/imgui.cpp
================================================
#include "iris.hpp"
#include "imgui.h"
#include "imgui_impl_sdl3.h"
#include "imgui_impl_vulkan.h"
#include "implot.h"
#include
#include
#include
// External includes
#include "res/IconsMaterialSymbols.h"
// INCBIN stuff
#define INCBIN_PREFIX g_
#define INCBIN_STYLE INCBIN_STYLE_SNAKE
#include "incbin.h"
INCBIN(roboto, "../res/Roboto-Regular.ttf");
INCBIN(roboto_black, "../res/Roboto-Black.ttf");
INCBIN(symbols, "../res/MaterialSymbolsRounded.ttf");
INCBIN(firacode, "../res/FiraCode-Regular.ttf");
INCBIN(ps1_memory_card_icon, "../res/ps1_mcd.png");
INCBIN(ps2_memory_card_icon, "../res/ps2_mcd.png");
INCBIN(dualshock2_icon, "../res/ds2.png");
INCBIN(pocketstation_icon, "../res/pocketstation.png");
INCBIN(iris_icon, "../res/iris.png");
INCBIN(vertex_shader, "../shaders/vertex.spv");
INCBIN(fragment_shader, "../shaders/fragment.spv");
#include "stb_image.h"
#define VOLK_IMPLEMENTATION
#include
namespace iris::imgui {
static constexpr uint32_t DESCRIPTOR_SET_RING_SIZE = 8;
static const ImWchar g_icon_range[] = { ICON_MIN_MS, ICON_MAX_16_MS, 0 };
static bool setup_vulkan_window(iris::instance* iris, ImGui_ImplVulkanH_Window* wd, int width, int height, bool vsync) {
wd->Surface = iris->surface;
VkAttachmentDescription attachment = {};
attachment.format = wd->SurfaceFormat.format;
attachment.samples = VK_SAMPLE_COUNT_1_BIT;
attachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachment.initialLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
wd->AttachmentDesc = attachment;
// Check for WSI support
VkBool32 res;
vkGetPhysicalDeviceSurfaceSupportKHR(iris->physical_device, iris->queue_family, wd->Surface, &res);
if (!res) {
fprintf(stderr, "imgui: No WSI support on physical device\n");
return false;
}
// Select Surface Format
const VkFormat requestSurfaceImageFormat[] = {
VK_FORMAT_B8G8R8A8_UNORM,
VK_FORMAT_R8G8B8A8_UNORM,
VK_FORMAT_B8G8R8_UNORM,
VK_FORMAT_R8G8B8_UNORM
};
const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(
iris->physical_device,
wd->Surface,
requestSurfaceImageFormat,
(size_t)IM_ARRAYSIZE(requestSurfaceImageFormat),
requestSurfaceColorSpace
);
// Select Present Mode
std::vector present_modes;
if (vsync) {
present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR);
} else {
present_modes.push_back(VK_PRESENT_MODE_MAILBOX_KHR);
present_modes.push_back(VK_PRESENT_MODE_IMMEDIATE_KHR);
present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR);
}
wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(
iris->physical_device,
wd->Surface,
present_modes.data(),
present_modes.size()
);
// Create SwapChain, RenderPass, Framebuffer, etc.
IM_ASSERT(iris->min_image_count >= 2);
ImGui_ImplVulkanH_CreateOrResizeWindow(
iris->instance,
iris->physical_device,
iris->device,
wd,
iris->queue_family,
VK_NULL_HANDLE,
width, height,
iris->min_image_count,
0
);
return true;
}
void set_vsync(iris::instance* iris, bool vsync) {
std::vector present_modes;
if (vsync) {
present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR);
} else {
present_modes.push_back(VK_PRESENT_MODE_MAILBOX_KHR);
present_modes.push_back(VK_PRESENT_MODE_IMMEDIATE_KHR);
present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR);
}
iris->main_window_data.PresentMode = ImGui_ImplVulkanH_SelectPresentMode(
iris->physical_device,
iris->main_window_data.Surface,
present_modes.data(),
present_modes.size()
);
render::refresh(iris);
}
bool setup_fonts(iris::instance* iris, ImGuiIO& io) {
io.Fonts->AddFontDefault();
ImFontConfig config;
config.MergeMode = true;
config.GlyphMinAdvanceX = 13.0f;
config.GlyphOffset = ImVec2(0.0f, 4.0f);
config.FontDataOwnedByAtlas = false;
ImFontConfig config_no_own;
config_no_own.FontDataOwnedByAtlas = false;
iris->font_small_code = io.Fonts->AddFontFromMemoryTTF((void*)g_firacode_data, g_firacode_size, 12.0F, &config_no_own);
iris->font_code = io.Fonts->AddFontFromMemoryTTF((void*)g_firacode_data, g_firacode_size, 16.0F, &config_no_own);
iris->font_small = io.Fonts->AddFontFromMemoryTTF((void*)g_roboto_data, g_roboto_size, 12.0F, &config_no_own);
iris->font_heading = io.Fonts->AddFontFromMemoryTTF((void*)g_roboto_data, g_roboto_size, 20.0F, &config_no_own);
iris->font_body = io.Fonts->AddFontFromMemoryTTF((void*)g_roboto_data, g_roboto_size, 16.0F, &config_no_own);
iris->font_icons = io.Fonts->AddFontFromMemoryTTF((void*)g_symbols_data, g_symbols_size, 20.0F, &config, g_icon_range);
iris->font_icons_big = io.Fonts->AddFontFromMemoryTTF((void*)g_symbols_data, g_symbols_size, 50.0F, &config_no_own, g_icon_range);
iris->font_black = io.Fonts->AddFontFromMemoryTTF((void*)g_roboto_black_data, g_roboto_black_size, 30.0F, &config_no_own);
if (!iris->font_small_code ||
!iris->font_code ||
!iris->font_small ||
!iris->font_heading ||
!iris->font_body ||
!iris->font_icons ||
!iris->font_icons_big ||
!iris->font_black) {
return false;
}
io.FontDefault = iris->font_icons;
return true;
}
void set_theme(iris::instance* iris, int theme, bool set_bg_color) {
// Init 'Granite' theme
ImGuiStyle& style = ImGui::GetStyle();
style.WindowPadding = ImVec2(8.0, 8.0);
style.FramePadding = ImVec2(5.0, 5.0);
style.ItemSpacing = ImVec2(8.0, 6.0);
style.WindowBorderSize = 0;
style.ChildBorderSize = 0;
style.FrameBorderSize = 1;
style.PopupBorderSize = 0;
style.TabBorderSize = 0;
style.TabBarBorderSize = 0;
style.WindowRounding = 6;
style.ChildRounding = 4;
style.FrameRounding = 4;
style.PopupRounding = 4;
style.ScrollbarRounding = 9;
style.GrabRounding = 2;
style.TabRounding = 4;
style.WindowTitleAlign = ImVec2(0.5, 0.5);
style.DockingSeparatorSize = 0;
style.SeparatorTextBorderSize = 1;
style.SeparatorTextPadding = ImVec2(20, 0);
// Use ImGui's default dark style as a base for our own style
ImGui::StyleColorsDark();
switch (theme) {
case IRIS_THEME_GRANITE: {
ImVec4* colors = style.Colors;
colors[ImGuiCol_Text] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
colors[ImGuiCol_TextDisabled] = ImVec4(0.35f, 0.35f, 0.35f, 1.00f);
colors[ImGuiCol_WindowBg] = ImVec4(0.02f, 0.02f, 0.02f, 1.00f);
colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
colors[ImGuiCol_PopupBg] = ImVec4(0.07f, 0.09f, 0.10f, 1.00f);
colors[ImGuiCol_Border] = ImVec4(0.10f, 0.12f, 0.13f, 1.00f);
colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
colors[ImGuiCol_FrameBg] = ImVec4(0.10f, 0.12f, 0.13f, 0.50f);
colors[ImGuiCol_FrameBgHovered] = ImVec4(0.20f, 0.24f, 0.26f, 0.50f);
colors[ImGuiCol_FrameBgActive] = ImVec4(0.29f, 0.35f, 0.39f, 0.50f);
colors[ImGuiCol_TitleBg] = ImVec4(0.04f, 0.04f, 0.04f, 1.00f);
colors[ImGuiCol_TitleBgActive] = ImVec4(0.16f, 0.20f, 0.22f, 1.00f);
colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.00f, 0.00f, 0.00f, 0.51f);
colors[ImGuiCol_MenuBarBg] = ImVec4(0.14f, 0.14f, 0.14f, 1.00f);
colors[ImGuiCol_ScrollbarBg] = ImVec4(0.02f, 0.02f, 0.02f, 0.53f);
colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.31f, 0.31f, 0.31f, 1.00f);
colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f);
colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.51f, 0.51f, 0.51f, 1.00f);
colors[ImGuiCol_CheckMark] = ImVec4(0.88f, 0.88f, 0.88f, 1.00f);
colors[ImGuiCol_SliderGrab] = ImVec4(0.39f, 0.47f, 0.52f, 0.50f);
colors[ImGuiCol_SliderGrabActive] = ImVec4(0.49f, 0.59f, 0.65f, 0.50f);
colors[ImGuiCol_Button] = ImVec4(0.13f, 0.16f, 0.17f, 0.25f);
colors[ImGuiCol_ButtonHovered] = ImVec4(0.20f, 0.24f, 0.26f, 0.50f);
colors[ImGuiCol_ButtonActive] = ImVec4(0.29f, 0.35f, 0.39f, 0.50f);
colors[ImGuiCol_Header] = ImVec4(0.13f, 0.16f, 0.17f, 0.50f);
colors[ImGuiCol_HeaderHovered] = ImVec4(0.20f, 0.24f, 0.26f, 0.50f);
colors[ImGuiCol_HeaderActive] = ImVec4(0.29f, 0.35f, 0.39f, 0.50f);
colors[ImGuiCol_Separator] = ImVec4(0.23f, 0.28f, 0.30f, 1.00f);
colors[ImGuiCol_SeparatorHovered] = ImVec4(0.33f, 0.39f, 0.43f, 1.00f);
colors[ImGuiCol_SeparatorActive] = ImVec4(0.38f, 0.46f, 0.51f, 1.00f);
colors[ImGuiCol_ResizeGrip] = ImVec4(0.15f, 0.20f, 0.22f, 1.00f);
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.00f, 0.30f, 0.25f, 1.00f);
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.00f, 0.39f, 0.32f, 1.00f);
colors[ImGuiCol_InputTextCursor] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
colors[ImGuiCol_TabHovered] = ImVec4(0.23f, 0.28f, 0.30f, 0.59f);
colors[ImGuiCol_Tab] = ImVec4(0.20f, 0.24f, 0.26f, 0.59f);
colors[ImGuiCol_TabSelected] = ImVec4(0.26f, 0.31f, 0.35f, 0.59f);
colors[ImGuiCol_TabSelectedOverline] = ImVec4(0.00f, 0.39f, 0.32f, 1.00f);
colors[ImGuiCol_TabDimmed] = ImVec4(0.07f, 0.10f, 0.15f, 0.97f);
colors[ImGuiCol_TabDimmedSelected] = ImVec4(0.10f, 0.12f, 0.13f, 1.00f);
colors[ImGuiCol_TabDimmedSelectedOverline] = ImVec4(0.50f, 0.50f, 0.50f, 0.00f);
colors[ImGuiCol_DockingPreview] = ImVec4(0.15f, 0.20f, 0.22f, 1.00f);
colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);
colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
colors[ImGuiCol_TableHeaderBg] = ImVec4(0.19f, 0.19f, 0.20f, 1.00f);
colors[ImGuiCol_TableBorderStrong] = ImVec4(0.31f, 0.31f, 0.35f, 1.00f);
colors[ImGuiCol_TableBorderLight] = ImVec4(0.23f, 0.23f, 0.25f, 1.00f);
colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.06f);
colors[ImGuiCol_TextLink] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.15f, 0.20f, 0.22f, 1.00f);
colors[ImGuiCol_DragDropTarget] = ImVec4(0.29f, 0.38f, 0.42f, 1.00f);
colors[ImGuiCol_NavCursor] = ImVec4(0.15f, 0.20f, 0.22f, 1.00f);
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.35f);
if (!set_bg_color) break;
iris->clear_value.color.float32[0] = 0.11f;
iris->clear_value.color.float32[1] = 0.11f;
iris->clear_value.color.float32[2] = 0.11f;
iris->clear_value.color.float32[3] = 1.00f;
} break;
case IRIS_THEME_IMGUI_DARK: {
ImGui::StyleColorsDark();
if (!set_bg_color) break;
iris->clear_value.color.float32[0] = 0.11f;
iris->clear_value.color.float32[1] = 0.11f;
iris->clear_value.color.float32[2] = 0.11f;
iris->clear_value.color.float32[3] = 1.00f;
} break;
case IRIS_THEME_IMGUI_LIGHT: {
ImGui::StyleColorsLight();
if (!set_bg_color) break;
iris->clear_value.color.float32[0] = 0.89f;
iris->clear_value.color.float32[1] = 0.89f;
iris->clear_value.color.float32[2] = 0.89f;
iris->clear_value.color.float32[3] = 1.00f;
} break;
case IRIS_THEME_IMGUI_CLASSIC: {
ImGui::StyleColorsClassic();
if (!set_bg_color) break;
iris->clear_value.color.float32[0] = 0.11f;
iris->clear_value.color.float32[1] = 0.11f;
iris->clear_value.color.float32[2] = 0.11f;
iris->clear_value.color.float32[3] = 1.00f;
} break;
case IRIS_THEME_CHERRY: {
// cherry colors, 3 intensities
#define HI(v) ImVec4(0.502f, 0.075f, 0.256f, v)
#define MED(v) ImVec4(0.455f, 0.198f, 0.301f, v)
#define LOW(v) ImVec4(0.232f, 0.201f, 0.271f, v)
// backgrounds
#define BG(v) ImVec4(0.200f, 0.220f, 0.270f, v)
// text
#define TEXT(v) ImVec4(0.860f, 0.930f, 0.890f, v)
auto &style = ImGui::GetStyle();
style.Colors[ImGuiCol_Text] = TEXT(0.78f);
style.Colors[ImGuiCol_TextDisabled] = TEXT(0.28f);
style.Colors[ImGuiCol_WindowBg] = ImVec4(0.13f, 0.14f, 0.17f, 1.00f);
style.Colors[ImGuiCol_ChildBg] = BG( 0.58f);
style.Colors[ImGuiCol_PopupBg] = BG( 0.9f);
style.Colors[ImGuiCol_Border] = ImVec4(0.31f, 0.31f, 1.00f, 0.00f);
style.Colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
style.Colors[ImGuiCol_FrameBg] = BG( 1.00f);
style.Colors[ImGuiCol_FrameBgHovered] = MED( 0.78f);
style.Colors[ImGuiCol_FrameBgActive] = MED( 1.00f);
style.Colors[ImGuiCol_TitleBg] = LOW( 1.00f);
style.Colors[ImGuiCol_TitleBgActive] = HI( 1.00f);
style.Colors[ImGuiCol_TitleBgCollapsed] = BG( 0.75f);
style.Colors[ImGuiCol_MenuBarBg] = BG( 0.47f);
style.Colors[ImGuiCol_ScrollbarBg] = BG( 1.00f);
style.Colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.09f, 0.15f, 0.16f, 1.00f);
style.Colors[ImGuiCol_ScrollbarGrabHovered] = MED( 0.78f);
style.Colors[ImGuiCol_ScrollbarGrabActive] = MED( 1.00f);
style.Colors[ImGuiCol_CheckMark] = ImVec4(0.71f, 0.22f, 0.27f, 1.00f);
style.Colors[ImGuiCol_SliderGrab] = ImVec4(0.47f, 0.77f, 0.83f, 0.14f);
style.Colors[ImGuiCol_SliderGrabActive] = ImVec4(0.71f, 0.22f, 0.27f, 1.00f);
style.Colors[ImGuiCol_Button] = ImVec4(0.47f, 0.77f, 0.83f, 0.14f);
style.Colors[ImGuiCol_ButtonHovered] = MED( 0.86f);
style.Colors[ImGuiCol_ButtonActive] = MED( 1.00f);
style.Colors[ImGuiCol_Header] = MED( 0.76f);
style.Colors[ImGuiCol_HeaderHovered] = MED( 0.86f);
style.Colors[ImGuiCol_HeaderActive] = HI( 1.00f);
style.Colors[ImGuiCol_ResizeGrip] = ImVec4(0.47f, 0.77f, 0.83f, 0.04f);
style.Colors[ImGuiCol_ResizeGripHovered] = MED( 0.78f);
style.Colors[ImGuiCol_ResizeGripActive] = MED( 1.00f);
style.Colors[ImGuiCol_PlotLines] = TEXT(0.63f);
style.Colors[ImGuiCol_PlotLinesHovered] = MED( 1.00f);
style.Colors[ImGuiCol_PlotHistogram] = TEXT(0.63f);
style.Colors[ImGuiCol_PlotHistogramHovered] = MED( 1.00f);
style.Colors[ImGuiCol_TextSelectedBg] = MED( 0.43f);
style.Colors[ImGuiCol_ModalWindowDimBg] = BG( 0.73f);
#undef HI
#undef MED
#undef LOW
#undef BG
#undef TEXT
if (!set_bg_color) break;
iris->clear_value.color.float32[0] = 0.20f * 0.5f;
iris->clear_value.color.float32[1] = 0.22f * 0.5f;
iris->clear_value.color.float32[2] = 0.27f * 0.5f;
iris->clear_value.color.float32[3] = 1.00f;
} break;
case IRIS_THEME_SOURCE: {
ImVec4* colors = ImGui::GetStyle().Colors;
colors[ImGuiCol_Text] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
colors[ImGuiCol_TextDisabled] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
colors[ImGuiCol_WindowBg] = ImVec4(0.29f, 0.34f, 0.26f, 1.00f);
colors[ImGuiCol_ChildBg] = ImVec4(0.29f, 0.34f, 0.26f, 1.00f);
colors[ImGuiCol_PopupBg] = ImVec4(0.24f, 0.27f, 0.20f, 1.00f);
colors[ImGuiCol_Border] = ImVec4(0.54f, 0.57f, 0.51f, 0.50f);
colors[ImGuiCol_BorderShadow] = ImVec4(0.14f, 0.16f, 0.11f, 0.52f);
colors[ImGuiCol_FrameBg] = ImVec4(0.24f, 0.27f, 0.20f, 1.00f);
colors[ImGuiCol_FrameBgHovered] = ImVec4(0.27f, 0.30f, 0.23f, 1.00f);
colors[ImGuiCol_FrameBgActive] = ImVec4(0.30f, 0.34f, 0.26f, 1.00f);
colors[ImGuiCol_TitleBg] = ImVec4(0.24f, 0.27f, 0.20f, 1.00f);
colors[ImGuiCol_TitleBgActive] = ImVec4(0.29f, 0.34f, 0.26f, 1.00f);
colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.00f, 0.00f, 0.00f, 0.51f);
colors[ImGuiCol_MenuBarBg] = ImVec4(0.24f, 0.27f, 0.20f, 1.00f);
colors[ImGuiCol_ScrollbarBg] = ImVec4(0.35f, 0.42f, 0.31f, 1.00f);
colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.28f, 0.32f, 0.24f, 1.00f);
colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.25f, 0.30f, 0.22f, 1.00f);
colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.23f, 0.27f, 0.21f, 1.00f);
colors[ImGuiCol_CheckMark] = ImVec4(0.59f, 0.54f, 0.18f, 1.00f);
colors[ImGuiCol_SliderGrab] = ImVec4(0.35f, 0.42f, 0.31f, 1.00f);
colors[ImGuiCol_SliderGrabActive] = ImVec4(0.54f, 0.57f, 0.51f, 0.50f);
colors[ImGuiCol_Button] = ImVec4(0.29f, 0.34f, 0.26f, 0.40f);
colors[ImGuiCol_ButtonHovered] = ImVec4(0.35f, 0.42f, 0.31f, 1.00f);
colors[ImGuiCol_ButtonActive] = ImVec4(0.54f, 0.57f, 0.51f, 0.50f);
colors[ImGuiCol_Header] = ImVec4(0.35f, 0.42f, 0.31f, 1.00f);
colors[ImGuiCol_HeaderHovered] = ImVec4(0.35f, 0.42f, 0.31f, 0.60f);
colors[ImGuiCol_HeaderActive] = ImVec4(0.54f, 0.57f, 0.51f, 0.50f);
colors[ImGuiCol_Separator] = ImVec4(0.14f, 0.16f, 0.11f, 1.00f);
colors[ImGuiCol_SeparatorHovered] = ImVec4(0.54f, 0.57f, 0.51f, 1.00f);
colors[ImGuiCol_SeparatorActive] = ImVec4(0.59f, 0.54f, 0.18f, 1.00f);
colors[ImGuiCol_ResizeGrip] = ImVec4(0.19f, 0.23f, 0.18f, 0.00f);
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.54f, 0.57f, 0.51f, 1.00f);
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.59f, 0.54f, 0.18f, 1.00f);
colors[ImGuiCol_Tab] = ImVec4(0.35f, 0.42f, 0.31f, 1.00f);
colors[ImGuiCol_TabHovered] = ImVec4(0.54f, 0.57f, 0.51f, 0.78f);
colors[ImGuiCol_TabActive] = ImVec4(0.59f, 0.54f, 0.18f, 1.00f);
colors[ImGuiCol_TabUnfocused] = ImVec4(0.24f, 0.27f, 0.20f, 1.00f);
colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.35f, 0.42f, 0.31f, 1.00f);
colors[ImGuiCol_DockingPreview] = ImVec4(0.59f, 0.54f, 0.18f, 1.00f);
colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);
colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
colors[ImGuiCol_PlotLinesHovered] = ImVec4(0.59f, 0.54f, 0.18f, 1.00f);
colors[ImGuiCol_PlotHistogram] = ImVec4(1.00f, 0.78f, 0.28f, 1.00f);
colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.59f, 0.54f, 0.18f, 1.00f);
colors[ImGuiCol_DragDropTarget] = ImVec4(0.73f, 0.67f, 0.24f, 1.00f);
colors[ImGuiCol_NavHighlight] = ImVec4(0.59f, 0.54f, 0.18f, 1.00f);
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
if (!set_bg_color) break;
iris->clear_value.color.float32[0] = 0.13f;
iris->clear_value.color.float32[1] = 0.15f;
iris->clear_value.color.float32[2] = 0.11f;
iris->clear_value.color.float32[3] = 1.00f;
} break;
}
ImPlotStyle& pstyle = ImPlot::GetStyle();
pstyle.MinorGridSize = ImVec2(0.0f, 0.0f);
pstyle.MajorGridSize = ImVec2(0.0f, 0.0f);
pstyle.MinorTickLen = ImVec2(0.0f, 0.0f);
pstyle.MajorTickLen = ImVec2(0.0f, 0.0f);
pstyle.PlotDefaultSize = ImVec2(250.0f, 150.0f);
pstyle.PlotPadding = ImVec2(0.0f, 0.0f);
pstyle.LegendPadding = ImVec2(0.0f, 0.0f);
pstyle.LegendInnerPadding = ImVec2(0.0f, 0.0f);
pstyle.LineWeight = 2.0f;
pstyle.Colors[ImPlotCol_Line] = ImVec4(0.0f, 1.0f, 0.2f, 1.0f);
pstyle.Colors[ImPlotCol_FrameBg] = ImVec4(0.0f, 0.0f, 0.0f, 0.0f);
pstyle.Colors[ImPlotCol_PlotBg] = ImVec4(0.0f, 0.0f, 0.0f, 0.0f);
}
void set_codeview_scheme(iris::instance* iris, int scheme) {
switch (scheme) {
default: case IRIS_CODEVIEW_COLOR_SCHEME_SOLARIZED_DARK: {
iris->codeview_color_text = IM_COL32(131, 148, 150, 255);
iris->codeview_color_comment = IM_COL32(88, 110, 117, 255);
iris->codeview_color_mnemonic = IM_COL32(211, 167, 30, 255);
iris->codeview_color_number = IM_COL32(138, 143, 226, 255);
iris->codeview_color_register = IM_COL32(68, 169, 240, 255);
iris->codeview_color_other = IM_COL32(89, 89, 89, 255);
iris->codeview_color_background = IM_COL32(0, 43, 54, 255);
iris->codeview_color_highlight = IM_COL32(7, 54, 66, 255);
} break;
case IRIS_CODEVIEW_COLOR_SCHEME_SOLARIZED_LIGHT: {
iris->codeview_color_text = IM_COL32(101, 123, 131, 255);
iris->codeview_color_comment = IM_COL32(147, 161, 161, 255);
iris->codeview_color_mnemonic = IM_COL32(147, 101, 21, 255);
iris->codeview_color_number = IM_COL32(101, 123, 179, 255);
iris->codeview_color_register = IM_COL32(38, 139, 210, 255);
iris->codeview_color_other = IM_COL32(88, 110, 117, 255);
iris->codeview_color_background = IM_COL32(253, 246, 227, 255);
iris->codeview_color_highlight = IM_COL32(238, 232, 213, 255);
} break;
case IRIS_CODEVIEW_COLOR_SCHEME_ONE_DARK_PRO: {
iris->codeview_color_text = IM_COL32(171, 178, 191, 255);
iris->codeview_color_comment = IM_COL32(92, 99, 112, 255);
iris->codeview_color_mnemonic = IM_COL32(198, 120, 221, 255);
iris->codeview_color_number = IM_COL32(209, 154, 102, 255);
iris->codeview_color_register = IM_COL32(97, 175, 239, 255);
iris->codeview_color_other = IM_COL32(171, 178, 191, 255);
iris->codeview_color_background = IM_COL32(40, 44, 52, 255);
iris->codeview_color_highlight = IM_COL32(60, 64, 72, 255);
} break;
case IRIS_CODEVIEW_COLOR_SCHEME_CATPPUCCIN_LATTE: {
iris->codeview_color_text = IM_COL32(76, 79, 105, 255);
iris->codeview_color_comment = IM_COL32(124, 127, 147, 255);
iris->codeview_color_mnemonic = IM_COL32(136, 57, 239, 255);
iris->codeview_color_number = IM_COL32(254, 100, 11, 255);
iris->codeview_color_register = IM_COL32(4, 165, 229, 255);
iris->codeview_color_other = IM_COL32(114, 135, 253, 255);
iris->codeview_color_background = IM_COL32(239, 241, 245, 255);
iris->codeview_color_highlight = IM_COL32(204, 208, 218, 255);
} break;
case IRIS_CODEVIEW_COLOR_SCHEME_CATPPUCCIN_FRAPPE: {
iris->codeview_color_text = IM_COL32(198, 208, 245, 255);
iris->codeview_color_comment = IM_COL32(148, 156, 187, 255);
iris->codeview_color_mnemonic = IM_COL32(202, 158, 230, 255);
iris->codeview_color_number = IM_COL32(239, 159, 118, 255);
iris->codeview_color_register = IM_COL32(153, 209, 219, 255);
iris->codeview_color_other = IM_COL32(186, 187, 241, 255);
iris->codeview_color_background = IM_COL32(48, 52, 70, 255);
iris->codeview_color_highlight = IM_COL32(81, 87, 109, 255);
} break;
case IRIS_CODEVIEW_COLOR_SCHEME_CATPPUCCIN_MACCHIATO: {
iris->codeview_color_text = IM_COL32(174, 178, 208, 255);
iris->codeview_color_comment = IM_COL32(134, 138, 162, 255);
iris->codeview_color_mnemonic = IM_COL32(190, 132, 255, 255);
iris->codeview_color_number = IM_COL32(245, 142, 110, 255);
iris->codeview_color_register = IM_COL32(125, 182, 191, 255);
iris->codeview_color_other = IM_COL32(166, 167, 222, 255);
iris->codeview_color_background = IM_COL32(58, 60, 79, 255);
iris->codeview_color_highlight = IM_COL32(97, 100, 120, 255);
} break;
case IRIS_CODEVIEW_COLOR_SCHEME_CATPPUCCIN_MOCHA: {
iris->codeview_color_text = IM_COL32(205, 214, 244, 255);
iris->codeview_color_comment = IM_COL32(145, 151, 181, 255);
iris->codeview_color_mnemonic = IM_COL32(220, 162, 255, 255);
iris->codeview_color_number = IM_COL32(248, 159, 128, 255);
iris->codeview_color_register = IM_COL32(159, 226, 235, 255);
iris->codeview_color_other = IM_COL32(189, 191, 248, 255);
iris->codeview_color_background = IM_COL32(46, 49, 64, 255);
iris->codeview_color_highlight = IM_COL32(76, 80, 100, 255);
} break;
}
}
VkShaderModule create_shader(iris::instance* iris, uint32_t* code, size_t size) {
VkShaderModuleCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
info.pCode = code;
info.codeSize = size;
VkShaderModule shader;
if (vkCreateShaderModule(iris->device, &info, nullptr, &shader) != VK_SUCCESS) {
return VK_NULL_HANDLE;
}
return shader;
}
VkPipeline create_pipeline(iris::instance* iris, VkShaderModule vert_shader, VkShaderModule frag_shader) {
// Create pipeline layout
VkPipelineLayout pipeline_layout = VK_NULL_HANDLE;
VkPipelineLayoutCreateInfo pipeline_layout_info = {};
pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipeline_layout_info.setLayoutCount = 1;
pipeline_layout_info.pSetLayouts = &iris->descriptor_set_layout;
pipeline_layout_info.pushConstantRangeCount = 0;
pipeline_layout_info.pPushConstantRanges = VK_NULL_HANDLE;
if (vkCreatePipelineLayout(iris->device, &pipeline_layout_info, nullptr, &pipeline_layout) != VK_SUCCESS) {
fprintf(stderr, "vulkan: Failed to create pipeline layout\n");
return VK_NULL_HANDLE;
}
iris->pipeline_layout = pipeline_layout;
VkRenderPass render_pass = iris->main_window_data.RenderPass;
// Create graphics pipeline
VkPipelineShaderStageCreateInfo shader_stages[2] = {};
shader_stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shader_stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
shader_stages[0].module = vert_shader;
shader_stages[0].pName = "main";
shader_stages[0].pNext = VK_NULL_HANDLE;
shader_stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shader_stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
shader_stages[1].module = frag_shader;
shader_stages[1].pName = "main";
shader_stages[1].pNext = VK_NULL_HANDLE;
static const VkDynamicState dynamic_states[] = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR
};
VkPipelineDynamicStateCreateInfo dynamic_state_info = {};
dynamic_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamic_state_info.dynamicStateCount = 2;
dynamic_state_info.pDynamicStates = dynamic_states;
const auto binding_description = vertex::get_binding_description();
const auto attribute_descriptions = vertex::get_attribute_descriptions();
VkPipelineVertexInputStateCreateInfo vertex_input_info = {};
vertex_input_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertex_input_info.vertexBindingDescriptionCount = 1;
vertex_input_info.pVertexBindingDescriptions = &binding_description;
vertex_input_info.vertexAttributeDescriptionCount = 2;
vertex_input_info.pVertexAttributeDescriptions = attribute_descriptions.data();
VkPipelineInputAssemblyStateCreateInfo input_assembly_info = {};
input_assembly_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
input_assembly_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
input_assembly_info.primitiveRestartEnable = VK_FALSE;
VkViewport viewport = {};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = (float)iris->main_window_data.Width;
viewport.height = (float)iris->main_window_data.Height;
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
VkExtent2D extent = {};
extent.width = iris->main_window_data.Width;
extent.height = iris->main_window_data.Height;
VkRect2D scissor = {};
scissor.offset = {0, 0};
scissor.extent = extent;
VkPipelineViewportStateCreateInfo viewport_state_info = {};
viewport_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewport_state_info.viewportCount = 1;
viewport_state_info.pViewports = &viewport;
viewport_state_info.scissorCount = 1;
viewport_state_info.pScissors = &scissor;
VkPipelineRasterizationStateCreateInfo rasterizer_info = {};
rasterizer_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizer_info.depthClampEnable = VK_FALSE;
rasterizer_info.rasterizerDiscardEnable = VK_FALSE;
rasterizer_info.polygonMode = VK_POLYGON_MODE_FILL;
rasterizer_info.lineWidth = 1.0f;
rasterizer_info.cullMode = VK_CULL_MODE_NONE;
rasterizer_info.frontFace = VK_FRONT_FACE_CLOCKWISE;
rasterizer_info.depthBiasEnable = VK_FALSE;
VkPipelineColorBlendAttachmentState blend_attachment_state = {};
blend_attachment_state.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
blend_attachment_state.blendEnable = VK_FALSE;
VkPipelineColorBlendStateCreateInfo blend_state_info{};
blend_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
blend_state_info.logicOpEnable = VK_FALSE;
blend_state_info.attachmentCount = 1;
blend_state_info.pAttachments = &blend_attachment_state;
VkPipelineMultisampleStateCreateInfo multisampling_state_info = {};
multisampling_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampling_state_info.sampleShadingEnable = VK_FALSE;
multisampling_state_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
VkGraphicsPipelineCreateInfo pipeline_info = {};
pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipeline_info.stageCount = 2;
pipeline_info.pStages = shader_stages;
pipeline_info.pVertexInputState = &vertex_input_info;
pipeline_info.pInputAssemblyState = &input_assembly_info;
pipeline_info.pViewportState = &viewport_state_info;
pipeline_info.pRasterizationState = &rasterizer_info;
pipeline_info.pMultisampleState = &multisampling_state_info;
pipeline_info.pDepthStencilState = nullptr; // Optional
pipeline_info.pColorBlendState = &blend_state_info;
pipeline_info.pDynamicState = &dynamic_state_info;
pipeline_info.layout = pipeline_layout;
pipeline_info.renderPass = render_pass;
pipeline_info.subpass = 0;
pipeline_info.pTessellationState = VK_NULL_HANDLE;
pipeline_info.basePipelineHandle = VK_NULL_HANDLE; // Optional
pipeline_info.basePipelineIndex = -1; // Optional
VkPipeline pipeline = VK_NULL_HANDLE;
if (vkCreateGraphicsPipelines(iris->device, VK_NULL_HANDLE, 1, &pipeline_info, nullptr, &pipeline) != VK_SUCCESS) {
return VK_NULL_HANDLE;
}
vkDestroyShaderModule(iris->device, frag_shader, nullptr);
vkDestroyShaderModule(iris->device, vert_shader, nullptr);
return pipeline;
}
bool init(iris::instance* iris) {
VkDescriptorSetLayoutBinding sampler_layout_binding = {};
sampler_layout_binding.binding = 0;
sampler_layout_binding.descriptorCount = 1;
sampler_layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
sampler_layout_binding.pImmutableSamplers = nullptr;
sampler_layout_binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
// VkDescriptorBindingFlags flags = {};
// flags = VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT;
// VkDescriptorSetLayoutBindingFlagsCreateInfo binding_flags = {};
// binding_flags.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO;
// binding_flags.pNext = nullptr;
// binding_flags.pBindingFlags = &flags;
// binding_flags.bindingCount = 1;
VkDescriptorSetLayoutCreateInfo layout_info = {};
layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
layout_info.bindingCount = 1;
layout_info.pBindings = &sampler_layout_binding;
// layout_info.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT;
// layout_info.pNext = &binding_flags;
if (vkCreateDescriptorSetLayout(iris->device, &layout_info, nullptr, &iris->descriptor_set_layout) != VK_SUCCESS) {
fprintf(stderr, "imgui: Failed to create descriptor set layout\n");
return false;
}
std::vector layouts(DESCRIPTOR_SET_RING_SIZE, iris->descriptor_set_layout);
iris->descriptor_sets.resize(DESCRIPTOR_SET_RING_SIZE, VK_NULL_HANDLE);
VkDescriptorSetAllocateInfo alloc_info = {};
alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
alloc_info.descriptorPool = iris->descriptor_pool;
alloc_info.descriptorSetCount = DESCRIPTOR_SET_RING_SIZE;
alloc_info.pSetLayouts = layouts.data();
if (vkAllocateDescriptorSets(iris->device, &alloc_info, iris->descriptor_sets.data()) != VK_SUCCESS) {
fprintf(stderr, "imgui: Failed to allocate descriptor sets\n");
return false;
}
iris->descriptor_set = iris->descriptor_sets[0];
if (!SDL_Vulkan_CreateSurface(iris->window, iris->instance, VK_NULL_HANDLE, &iris->surface)) {
printf("imgui: Failed to create Vulkan surface\n");
return false;
}
if (!setup_vulkan_window(iris, &iris->main_window_data, iris->window_width, iris->window_height, iris->vsync)) {
printf("imgui: Failed to setup Vulkan window\n");
return false;
}
iris->ini_path = iris->pref_path + "imgui.ini";
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImPlot::CreateContext();
ImGuiIO& io = ImGui::GetIO(); (void)io;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking
if (iris->imgui_enable_viewports) {
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
io.ConfigViewportsNoDecoration = false;
io.ConfigViewportsNoAutoMerge = true;
}
io.IniFilename = iris->ini_path.c_str();
// Setup scaling
ImGuiStyle& style = ImGui::GetStyle();
style.ScaleAllSizes(iris->main_scale);
style.FontScaleDpi = iris->main_scale;
style.FontScaleMain = iris->ui_scale;
io.ConfigDpiScaleFonts = true;
io.ConfigDpiScaleViewports = true;
// Setup Platform/Renderer backends
if (!ImGui_ImplSDL3_InitForVulkan(iris->window)) {
fprintf(stderr, "imgui: Failed to initialize SDL3/Vulkan backend\n");
return false;
}
ImGui_ImplVulkan_InitInfo init_info = {};
init_info.ApiVersion = IRIS_VULKAN_API_VERSION;
init_info.Instance = iris->instance;
init_info.PhysicalDevice = iris->physical_device;
init_info.Device = iris->device;
init_info.QueueFamily = iris->queue_family;
init_info.Queue = iris->queue;
init_info.PipelineCache = VK_NULL_HANDLE;
init_info.DescriptorPool = iris->descriptor_pool;
init_info.MinImageCount = iris->min_image_count;
init_info.ImageCount = iris->main_window_data.ImageCount;
init_info.Allocator = VK_NULL_HANDLE;
init_info.PipelineInfoMain.RenderPass = iris->main_window_data.RenderPass;
init_info.PipelineInfoMain.Subpass = 0;
init_info.PipelineInfoMain.MSAASamples = VK_SAMPLE_COUNT_1_BIT;
init_info.CheckVkResultFn = VK_NULL_HANDLE;
if (!ImGui_ImplVulkan_Init(&init_info)) {
fprintf(stderr, "imgui: Failed to initialize Vulkan backend\n");
return false;
}
if (!setup_fonts(iris, io)) {
fprintf(stderr, "imgui: Failed to setup fonts\n");
return false;
}
set_theme(iris, iris->theme, false);
set_codeview_scheme(iris, iris->codeview_color_scheme);
// Initialize our pipeline
VkShaderModule vert_shader = create_shader(iris, (uint32_t*)g_vertex_shader_data, g_vertex_shader_size);
VkShaderModule frag_shader = create_shader(iris, (uint32_t*)g_fragment_shader_data, g_fragment_shader_size);
if (!vert_shader || !frag_shader) {
fprintf(stderr, "vulkan: Failed to create shader modules\n");
return false;
}
iris->pipeline = create_pipeline(iris, vert_shader, frag_shader);
if (!iris->pipeline) {
fprintf(stderr, "imgui: Failed to create graphics pipeline\n");
return false;
}
auto load_texture = [iris](const stbi_uc* data, size_t size) -> texture {
int x, y, c;
stbi_uc* buf = stbi_load_from_memory(data, size, &x, &y, &c, 4);
auto tex = vulkan::upload_texture(iris, buf, x, y, c);
stbi_image_free(buf);
return tex;
};
iris->ps1_memory_card_icon = load_texture(g_ps1_memory_card_icon_data, g_ps1_memory_card_icon_size);
iris->ps2_memory_card_icon = load_texture(g_ps2_memory_card_icon_data, g_ps2_memory_card_icon_size);
iris->pocketstation_icon = load_texture(g_pocketstation_icon_data, g_pocketstation_icon_size);
iris->dualshock2_icon = load_texture(g_dualshock2_icon_data, g_dualshock2_icon_size);
iris->iris_icon = load_texture(g_iris_icon_data, g_iris_icon_size);
return true;
}
void cleanup(iris::instance* iris) {
vulkan::wait_idle(iris);
vulkan::free_texture(iris, iris->ps1_memory_card_icon);
vulkan::free_texture(iris, iris->ps2_memory_card_icon);
vulkan::free_texture(iris, iris->pocketstation_icon);
vulkan::free_texture(iris, iris->dualshock2_icon);
vulkan::free_texture(iris, iris->iris_icon);
ImGui_ImplVulkan_Shutdown();
ImGui_ImplSDL3_Shutdown();
ImPlot::DestroyContext();
ImGui::DestroyContext();
ImGui_ImplVulkanH_DestroyWindow(iris->instance, iris->device, &iris->main_window_data, VK_NULL_HANDLE);
iris->instance = NULL;
}
bool render_frame(iris::instance* iris, ImDrawData* draw_data) {
if (iris->swapchain_rebuild)
return true;
ImGui_ImplVulkanH_Window* wd = &iris->main_window_data;
VkSemaphore acquire_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].ImageAcquiredSemaphore;
uint32_t image_index;
VkResult err;
err = vkAcquireNextImageKHR(
iris->device,
wd->Swapchain,
UINT64_MAX,
acquire_semaphore,
VK_NULL_HANDLE,
&image_index
);
VkSemaphore submit_semaphore = wd->FrameSemaphores[image_index].RenderCompleteSemaphore;
if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) {
iris->swapchain_rebuild = true;
return true;
} else if (err != VK_SUCCESS) {
fprintf(stderr, "imgui: Failed to acquire next image\n");
return false;
}
wd->FrameIndex = image_index;
ImGui_ImplVulkanH_Frame* fd = &wd->Frames[wd->FrameIndex];
if (vkWaitForFences(iris->device, 1, &fd->Fence, VK_TRUE, UINT64_MAX) != VK_SUCCESS) {
fprintf(stderr, "imgui: Failed to wait for fence\n");
return false;
}
if (vkResetFences(iris->device, 1, &fd->Fence) != VK_SUCCESS) {
fprintf(stderr, "imgui: Failed to reset fence\n");
return false;
}
if (vkResetCommandPool(iris->device, fd->CommandPool, 0) != VK_SUCCESS) {
fprintf(stderr, "imgui: Failed to reset command pool\n");
return false;
}
VkCommandBufferBeginInfo begin_info = {};
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
begin_info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
if (vkBeginCommandBuffer(fd->CommandBuffer, &begin_info) != VK_SUCCESS) {
fprintf(stderr, "imgui: Failed to begin command buffer\n");
return false;
}
if (iris->instance) {
render::render_frame(iris, fd->CommandBuffer, fd->Framebuffer);
}
{
VkRenderPassBeginInfo render_pass_info = {};
render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
render_pass_info.renderPass = wd->RenderPass;
render_pass_info.framebuffer = fd->Framebuffer;
render_pass_info.renderArea.extent.width = wd->Width;
render_pass_info.renderArea.extent.height = wd->Height;
render_pass_info.clearValueCount = 1;
render_pass_info.pClearValues = &iris->clear_value;
vkCmdBeginRenderPass(fd->CommandBuffer, &render_pass_info, VK_SUBPASS_CONTENTS_INLINE);
}
// Record dear imgui primitives into command buffer
ImGui_ImplVulkan_RenderDrawData(draw_data, fd->CommandBuffer);
// Submit command buffer
vkCmdEndRenderPass(fd->CommandBuffer);
{
VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
VkSubmitInfo submit_info = {};
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submit_info.waitSemaphoreCount = 1;
submit_info.pWaitSemaphores = &acquire_semaphore;
submit_info.pWaitDstStageMask = &wait_stage;
submit_info.commandBufferCount = 1;
submit_info.pCommandBuffers = &fd->CommandBuffer;
submit_info.signalSemaphoreCount = 1;
submit_info.pSignalSemaphores = &submit_semaphore;
if (vkEndCommandBuffer(fd->CommandBuffer) != VK_SUCCESS) {
fprintf(stderr, "imgui: Failed to end command buffer\n");
return false;
}
if (vkQueueSubmit(iris->queue, 1, &submit_info, fd->Fence) != VK_SUCCESS) {
fprintf(stderr, "imgui: Failed to submit queue\n");
return false;
}
}
if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
ImGui::UpdatePlatformWindows();
ImGui::RenderPlatformWindowsDefault();
}
VkPresentInfoKHR present_info = {};
present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
present_info.waitSemaphoreCount = 1;
present_info.pWaitSemaphores = &submit_semaphore;
present_info.swapchainCount = 1;
present_info.pSwapchains = &wd->Swapchain;
present_info.pImageIndices = &image_index;
err = vkQueuePresentKHR(iris->queue, &present_info);
if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) {
iris->swapchain_rebuild = true;
return true;
} else if (err != VK_SUCCESS) {
fprintf(stderr, "imgui: Failed to acquire next image\n");
return false;
}
wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->SemaphoreCount;
return true;
}
bool BeginEx(const char* name, bool* p_open, ImGuiWindowFlags flags) {
ImGui::SetNextWindowSize(ImVec2(600.0, 600.0), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowPos(ImVec2(50.0, 50.0), ImGuiCond_FirstUseEver);
if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
flags |= ImGuiWindowFlags_NoTitleBar;
}
return ImGui::Begin(name, p_open, flags);
}
}
================================================
FILE: frontend/input.cpp
================================================
#include
#include
#include "iris.hpp"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
#define INCBIN_PREFIX g_
#define INCBIN_STYLE INCBIN_STYLE_SNAKE
#include "incbin.h"
INCBIN(gamecontrollerdb, "../deps/SDL_GameControllerDB/gamecontrollerdb.txt");
namespace iris {
void keyboard_device::handle_event(iris::instance* iris, SDL_Event* event) {
auto ievent = input::sdl_event_to_input_event(event);
auto action = input::get_input_action(iris, m_slot, ievent.u64);
if (!action)
return;
input::execute_action(iris, *action, m_slot, event->type == SDL_EVENT_KEY_DOWN ? 1.0f : 0.0f);
}
void gamepad_device::handle_event(iris::instance* iris, SDL_Event* event) {
auto ievent = input::sdl_event_to_input_event(event);
auto action = input::get_input_action(iris, m_slot, ievent.u64);
if (!action)
return;
if (event->type == SDL_EVENT_GAMEPAD_BUTTON_DOWN) {
input::execute_action(iris, *action, m_slot, 1.0f);
} else if (event->type == SDL_EVENT_GAMEPAD_BUTTON_UP) {
input::execute_action(iris, *action, m_slot, 0.0f);
} else if (event->type == SDL_EVENT_GAMEPAD_AXIS_MOTION) {
// Convert from -32768->32767 to -1.0->1.0 and take absolute value
float value = fabs(event->gaxis.value / 32767.0f);
input::execute_action(iris, *action, m_slot, value);
}
}
}
namespace iris::input {
void load_db_default(iris::instance* iris) {
SDL_IOStream* ios = SDL_IOFromConstMem(g_gamecontrollerdb_data, g_gamecontrollerdb_size);
SDL_AddGamepadMappingsFromIO(ios, true);
}
bool load_db_from_file(iris::instance* iris, const char* path) {
if (SDL_AddGamepadMappingsFromFile(path) == -1)
return false;
return true;
}
#define IEVENT(event, id, mod) \
(((uint64_t)event << 32) | (((id & 0xf0000fff) | ((mod & 0xffff) << 12)) & 0xffffffff))
void init_default_mapping(iris::instance* iris, int id) {
mapping& map = iris->input_maps[id];
if (id == 0) {
map.name = "Keyboard (default)";
map.map.clear();
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_X , SDL_KMOD_NONE), IRIS_DS_BT_CROSS);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_A , SDL_KMOD_NONE), IRIS_DS_BT_SQUARE);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_W , SDL_KMOD_NONE), IRIS_DS_BT_TRIANGLE);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_D , SDL_KMOD_NONE), IRIS_DS_BT_CIRCLE);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_RETURN, SDL_KMOD_NONE), IRIS_DS_BT_START);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_S , SDL_KMOD_NONE), IRIS_DS_BT_SELECT);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_UP , SDL_KMOD_NONE), IRIS_DS_BT_UP);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_DOWN , SDL_KMOD_NONE), IRIS_DS_BT_DOWN);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_LEFT , SDL_KMOD_NONE), IRIS_DS_BT_LEFT);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_RIGHT , SDL_KMOD_NONE), IRIS_DS_BT_RIGHT);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_Q , SDL_KMOD_NONE), IRIS_DS_BT_L1);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_E , SDL_KMOD_NONE), IRIS_DS_BT_R1);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_1 , SDL_KMOD_NONE), IRIS_DS_BT_L2);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_3 , SDL_KMOD_NONE), IRIS_DS_BT_R2);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_Z , SDL_KMOD_NONE), IRIS_DS_BT_L3);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_C , SDL_KMOD_NONE), IRIS_DS_BT_R3);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_I , SDL_KMOD_NONE), IRIS_DS_AX_LEFTV_POS);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_J , SDL_KMOD_NONE), IRIS_DS_AX_LEFTH_NEG);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_K , SDL_KMOD_NONE), IRIS_DS_AX_LEFTV_NEG);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_L , SDL_KMOD_NONE), IRIS_DS_AX_LEFTH_POS);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_T , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTV_POS);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_F , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTH_NEG);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_G , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTV_NEG);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_H , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTH_POS);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_0 , SDL_KMOD_NONE), IRIS_S14X_SW_SERVICE);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_9 , SDL_KMOD_NONE), IRIS_S14X_SW_TEST);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_8 , SDL_KMOD_NONE), IRIS_S14X_SW_ENTER);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_7 , SDL_KMOD_NONE), IRIS_S14X_SW_UP);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_6 , SDL_KMOD_NONE), IRIS_S14X_SW_DOWN);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_1 , SDL_KMOD_LSHIFT), IRIS_S14X_SW_P1_START);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_2 , SDL_KMOD_LSHIFT), IRIS_S14X_SW_P2_START);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_3 , SDL_KMOD_LSHIFT), IRIS_S14X_SW_P3_START);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_4 , SDL_KMOD_LSHIFT), IRIS_S14X_SW_P4_START);
} else {
map.name = "Gamepad (default)";
map.map.clear();
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_SOUTH , SDL_KMOD_NONE), IRIS_DS_BT_CROSS);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_WEST , SDL_KMOD_NONE), IRIS_DS_BT_SQUARE);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_NORTH , SDL_KMOD_NONE), IRIS_DS_BT_TRIANGLE);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_EAST , SDL_KMOD_NONE), IRIS_DS_BT_CIRCLE);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_START , SDL_KMOD_NONE), IRIS_DS_BT_START);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_BACK , SDL_KMOD_NONE), IRIS_DS_BT_SELECT);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_DPAD_UP , SDL_KMOD_NONE), IRIS_DS_BT_UP);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_DPAD_DOWN , SDL_KMOD_NONE), IRIS_DS_BT_DOWN);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_DPAD_LEFT , SDL_KMOD_NONE), IRIS_DS_BT_LEFT);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_DPAD_RIGHT , SDL_KMOD_NONE), IRIS_DS_BT_RIGHT);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_LEFT_SHOULDER , SDL_KMOD_NONE), IRIS_DS_BT_L1);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, SDL_KMOD_NONE), IRIS_DS_BT_R1);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_LEFT_STICK , SDL_KMOD_NONE), IRIS_DS_BT_L3);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_RIGHT_STICK , SDL_KMOD_NONE), IRIS_DS_BT_R3);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_POS, SDL_GAMEPAD_AXIS_LEFT_TRIGGER , SDL_KMOD_NONE), IRIS_DS_BT_L2);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_POS, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER , SDL_KMOD_NONE), IRIS_DS_BT_R2);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_POS, SDL_GAMEPAD_AXIS_LEFTY , SDL_KMOD_NONE), IRIS_DS_AX_LEFTV_POS);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_NEG, SDL_GAMEPAD_AXIS_LEFTY , SDL_KMOD_NONE), IRIS_DS_AX_LEFTV_NEG);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_POS, SDL_GAMEPAD_AXIS_LEFTX , SDL_KMOD_NONE), IRIS_DS_AX_LEFTH_POS);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_NEG, SDL_GAMEPAD_AXIS_LEFTX , SDL_KMOD_NONE), IRIS_DS_AX_LEFTH_NEG);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_POS, SDL_GAMEPAD_AXIS_RIGHTY , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTV_POS);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_NEG, SDL_GAMEPAD_AXIS_RIGHTY , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTV_NEG);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_POS, SDL_GAMEPAD_AXIS_RIGHTX , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTH_POS);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_NEG, SDL_GAMEPAD_AXIS_RIGHTX , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTH_NEG);
}
}
bool init(iris::instance* iris) {
if (!iris->gcdb_path.size()) {
fprintf(stdout, "input: Adding default database\n");
load_db_default(iris);
} else {
fprintf(stdout, "input: Adding database from file \'%s\'\n", iris->gcdb_path.c_str());
load_db_from_file(iris, iris->gcdb_path.c_str());
}
iris->input_devices[0] = new iris::keyboard_device();
if (iris->input_maps.size() == 0) {
mapping map;
map.name = "Keyboard (default)";
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_X , SDL_KMOD_NONE), IRIS_DS_BT_CROSS);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_A , SDL_KMOD_NONE), IRIS_DS_BT_SQUARE);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_W , SDL_KMOD_NONE), IRIS_DS_BT_TRIANGLE);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_D , SDL_KMOD_NONE), IRIS_DS_BT_CIRCLE);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_RETURN, SDL_KMOD_NONE), IRIS_DS_BT_START);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_S , SDL_KMOD_NONE), IRIS_DS_BT_SELECT);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_UP , SDL_KMOD_NONE), IRIS_DS_BT_UP);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_DOWN , SDL_KMOD_NONE), IRIS_DS_BT_DOWN);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_LEFT , SDL_KMOD_NONE), IRIS_DS_BT_LEFT);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_RIGHT , SDL_KMOD_NONE), IRIS_DS_BT_RIGHT);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_Q , SDL_KMOD_NONE), IRIS_DS_BT_L1);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_E , SDL_KMOD_NONE), IRIS_DS_BT_R1);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_1 , SDL_KMOD_NONE), IRIS_DS_BT_L2);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_3 , SDL_KMOD_NONE), IRIS_DS_BT_R2);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_Z , SDL_KMOD_NONE), IRIS_DS_BT_L3);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_C , SDL_KMOD_NONE), IRIS_DS_BT_R3);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_I , SDL_KMOD_NONE), IRIS_DS_AX_LEFTV_POS);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_J , SDL_KMOD_NONE), IRIS_DS_AX_LEFTH_NEG);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_K , SDL_KMOD_NONE), IRIS_DS_AX_LEFTV_NEG);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_L , SDL_KMOD_NONE), IRIS_DS_AX_LEFTH_POS);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_T , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTV_POS);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_F , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTH_NEG);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_G , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTV_NEG);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_H , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTH_POS);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_0 , SDL_KMOD_NONE), IRIS_S14X_SW_SERVICE);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_9 , SDL_KMOD_NONE), IRIS_S14X_SW_TEST);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_8 , SDL_KMOD_NONE), IRIS_S14X_SW_ENTER);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_7 , SDL_KMOD_NONE), IRIS_S14X_SW_UP);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_6 , SDL_KMOD_NONE), IRIS_S14X_SW_DOWN);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_1 , SDL_KMOD_LSHIFT), IRIS_S14X_SW_P1_START);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_2 , SDL_KMOD_LSHIFT), IRIS_S14X_SW_P2_START);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_3 , SDL_KMOD_LSHIFT), IRIS_S14X_SW_P3_START);
map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_4 , SDL_KMOD_LSHIFT), IRIS_S14X_SW_P4_START);
iris->input_maps.push_back(map);
map.map.clear();
map = {};
map.name = "Gamepad (default)";
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_SOUTH , SDL_KMOD_NONE), IRIS_DS_BT_CROSS);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_WEST , SDL_KMOD_NONE), IRIS_DS_BT_SQUARE);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_NORTH , SDL_KMOD_NONE), IRIS_DS_BT_TRIANGLE);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_EAST , SDL_KMOD_NONE), IRIS_DS_BT_CIRCLE);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_START , SDL_KMOD_NONE), IRIS_DS_BT_START);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_BACK , SDL_KMOD_NONE), IRIS_DS_BT_SELECT);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_DPAD_UP , SDL_KMOD_NONE), IRIS_DS_BT_UP);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_DPAD_DOWN , SDL_KMOD_NONE), IRIS_DS_BT_DOWN);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_DPAD_LEFT , SDL_KMOD_NONE), IRIS_DS_BT_LEFT);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_DPAD_RIGHT , SDL_KMOD_NONE), IRIS_DS_BT_RIGHT);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_LEFT_SHOULDER , SDL_KMOD_NONE), IRIS_DS_BT_L1);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, SDL_KMOD_NONE), IRIS_DS_BT_R1);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_LEFT_STICK , SDL_KMOD_NONE), IRIS_DS_BT_L3);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_RIGHT_STICK , SDL_KMOD_NONE), IRIS_DS_BT_R3);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_POS, SDL_GAMEPAD_AXIS_LEFT_TRIGGER , SDL_KMOD_NONE), IRIS_DS_BT_L2);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_POS, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER , SDL_KMOD_NONE), IRIS_DS_BT_R2);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_POS, SDL_GAMEPAD_AXIS_LEFTY , SDL_KMOD_NONE), IRIS_DS_AX_LEFTV_POS);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_NEG, SDL_GAMEPAD_AXIS_LEFTY , SDL_KMOD_NONE), IRIS_DS_AX_LEFTV_NEG);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_POS, SDL_GAMEPAD_AXIS_LEFTX , SDL_KMOD_NONE), IRIS_DS_AX_LEFTH_POS);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_NEG, SDL_GAMEPAD_AXIS_LEFTX , SDL_KMOD_NONE), IRIS_DS_AX_LEFTH_NEG);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_POS, SDL_GAMEPAD_AXIS_RIGHTY , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTV_POS);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_NEG, SDL_GAMEPAD_AXIS_RIGHTY , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTV_NEG);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_POS, SDL_GAMEPAD_AXIS_RIGHTX , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTH_POS);
map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_NEG, SDL_GAMEPAD_AXIS_RIGHTX , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTH_NEG);
iris->input_maps.push_back(map);
}
#undef IEVENT
// Ensure default mappings are in the correct order
if (iris->input_maps[0].name == "Gamepad (default)") {
auto map = iris->input_maps[0];
iris->input_maps[0] = iris->input_maps[1];
iris->input_maps[1] = map;
}
// Use keyboard mapping for slot 0 and none for slot 1 by default
if (iris->input_map[0] <= 1) {
iris->input_map[0] = 0;
}
if (iris->input_map[1] <= 1) {
iris->input_map[1] = -1;
}
return true;
}
input_action* get_input_action(iris::instance* iris, int slot, uint64_t input) {
if (iris->input_map[slot] == -1)
return nullptr;
return iris->input_maps[iris->input_map[slot]].map.get_value(input);
}
static inline void change_button(iris::instance* iris, int slot, float value, uint32_t button) {
if (!iris->ds[slot]) return;
if (value > 0.5f) {
ds_button_press(iris->ds[slot], button);
} else {
ds_button_release(iris->ds[slot], button);
}
}
static inline void change_s14x_switch(iris::instance* iris, float value, uint32_t mask) {
if (!iris->ps2->s14x_ioboard)
return;
if (value > 0.5) {
s14x_ioboard_press_switch(iris->ps2->s14x_ioboard, mask);
} else {
s14x_ioboard_release_switch(iris->ps2->s14x_ioboard, mask);
}
}
void execute_action(iris::instance* iris, input_action action, int slot, float value) {
if (!iris->ds[slot])
return;
switch (action) {
case IRIS_DS_BT_SELECT: change_button(iris, slot, value, DS_BT_SELECT); break;
case IRIS_DS_BT_L3: change_button(iris, slot, value, DS_BT_L3); break;
case IRIS_DS_BT_R3: change_button(iris, slot, value, DS_BT_R3); break;
case IRIS_DS_BT_START: change_button(iris, slot, value, DS_BT_START); break;
case IRIS_DS_BT_UP: change_button(iris, slot, value, DS_BT_UP); break;
case IRIS_DS_BT_RIGHT: change_button(iris, slot, value, DS_BT_RIGHT); break;
case IRIS_DS_BT_DOWN: change_button(iris, slot, value, DS_BT_DOWN); break;
case IRIS_DS_BT_LEFT: change_button(iris, slot, value, DS_BT_LEFT); break;
case IRIS_DS_BT_L2: change_button(iris, slot, value, DS_BT_L2); break;
case IRIS_DS_BT_R2: change_button(iris, slot, value, DS_BT_R2); break;
case IRIS_DS_BT_L1: change_button(iris, slot, value, DS_BT_L1); break;
case IRIS_DS_BT_R1: change_button(iris, slot, value, DS_BT_R1); break;
case IRIS_DS_BT_TRIANGLE: change_button(iris, slot, value, DS_BT_TRIANGLE); break;
case IRIS_DS_BT_CIRCLE: change_button(iris, slot, value, DS_BT_CIRCLE); break;
case IRIS_DS_BT_CROSS: change_button(iris, slot, value, DS_BT_CROSS); break;
case IRIS_DS_BT_SQUARE: change_button(iris, slot, value, DS_BT_SQUARE); break;
case IRIS_DS_BT_ANALOG: change_button(iris, slot, value, DS_BT_ANALOG); break;
case IRIS_DS_AX_RIGHTV_POS: ds_analog_change(iris->ds[slot], DS_AX_RIGHT_V, 0x7f + (value * 0x80)); break;
case IRIS_DS_AX_RIGHTV_NEG: ds_analog_change(iris->ds[slot], DS_AX_RIGHT_V, 0x7f - (value * 0x7f)); break;
case IRIS_DS_AX_RIGHTH_POS: ds_analog_change(iris->ds[slot], DS_AX_RIGHT_H, 0x7f + (value * 0x80)); break;
case IRIS_DS_AX_RIGHTH_NEG: ds_analog_change(iris->ds[slot], DS_AX_RIGHT_H, 0x7f - (value * 0x7f)); break;
case IRIS_DS_AX_LEFTV_POS: ds_analog_change(iris->ds[slot], DS_AX_LEFT_V, 0x7f + (value * 0x80)); break;
case IRIS_DS_AX_LEFTV_NEG: ds_analog_change(iris->ds[slot], DS_AX_LEFT_V, 0x7f - (value * 0x7f)); break;
case IRIS_DS_AX_LEFTH_POS: ds_analog_change(iris->ds[slot], DS_AX_LEFT_H, 0x7f + (value * 0x80)); break;
case IRIS_DS_AX_LEFTH_NEG: ds_analog_change(iris->ds[slot], DS_AX_LEFT_H, 0x7f - (value * 0x7f)); break;
case IRIS_S14X_SW_SERVICE: change_s14x_switch(iris, value, S14X_IOBOARD_SW_SERVICE); break;
case IRIS_S14X_SW_TEST: change_s14x_switch(iris, value, S14X_IOBOARD_SW_TEST); break;
case IRIS_S14X_SW_ENTER: change_s14x_switch(iris, value, S14X_IOBOARD_SW_ENTER); break;
case IRIS_S14X_SW_UP: change_s14x_switch(iris, value, S14X_IOBOARD_SW_UP); break;
case IRIS_S14X_SW_DOWN: change_s14x_switch(iris, value, S14X_IOBOARD_SW_DOWN); break;
case IRIS_S14X_SW_P1_START: change_s14x_switch(iris, value, S14X_IOBOARD_SW_P1_START); break;
case IRIS_S14X_SW_P2_START: change_s14x_switch(iris, value, S14X_IOBOARD_SW_P2_START); break;
case IRIS_S14X_SW_P3_START: change_s14x_switch(iris, value, S14X_IOBOARD_SW_P3_START); break;
case IRIS_S14X_SW_P4_START: change_s14x_switch(iris, value, S14X_IOBOARD_SW_P4_START); break;
}
}
input_event sdl_event_to_input_event(SDL_Event* event) {
input_event ievent = {};
switch (event->type) {
case SDL_EVENT_KEY_DOWN:
case SDL_EVENT_KEY_UP: {
ievent.type = IRIS_EVENT_KEYBOARD;
ievent.id = event->key.key;
// Devious hack, we have enough spare bits in the
// SDL_Keycode so we can actually do this
const uint16_t mask =
SDL_KMOD_LSHIFT | SDL_KMOD_RSHIFT |
SDL_KMOD_LCTRL | SDL_KMOD_RCTRL |
SDL_KMOD_LALT | SDL_KMOD_RALT;
ievent.id |= (event->key.mod & mask) << 12;
} break;
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
case SDL_EVENT_GAMEPAD_BUTTON_UP: {
ievent.type = IRIS_EVENT_GAMEPAD_BUTTON;
ievent.id = event->gbutton.button;
} break;
case SDL_EVENT_GAMEPAD_AXIS_MOTION: {
if (event->gaxis.value > 0) {
ievent.type = IRIS_EVENT_GAMEPAD_AXIS_POS;
} else {
ievent.type = IRIS_EVENT_GAMEPAD_AXIS_NEG;
}
ievent.id = event->gaxis.axis;
} break;
}
return ievent;
}
std::string get_default_screenshot_filename(iris::instance* iris) {
SDL_Time t;
SDL_DateTime dt;
SDL_GetCurrentTime(&t);
SDL_TimeToDateTime(t, &dt, true);
char buf[512];
sprintf(buf, "Screenshot-%04d-%02d-%02d_%02d-%02d-%02d-%d",
dt.year, dt.month, dt.day,
dt.hour, dt.minute, dt.second,
iris->screenshot_counter + 1
);
std::string str(buf);
switch (iris->screenshot_format) {
case IRIS_SCREENSHOT_FORMAT_PNG: str += ".png"; break;
case IRIS_SCREENSHOT_FORMAT_BMP: str += ".bmp"; break;
case IRIS_SCREENSHOT_FORMAT_JPG: str += ".jpg"; break;
case IRIS_SCREENSHOT_FORMAT_TGA: str += ".tga"; break;
}
return str;
}
int get_screenshot_jpg_quality(iris::instance* iris) {
switch (iris->screenshot_jpg_quality_mode) {
case IRIS_SCREENSHOT_JPG_QUALITY_MINIMUM: return 1;
case IRIS_SCREENSHOT_JPG_QUALITY_LOW: return 25;
case IRIS_SCREENSHOT_JPG_QUALITY_MEDIUM: return 50;
case IRIS_SCREENSHOT_JPG_QUALITY_HIGH: return 90;
case IRIS_SCREENSHOT_JPG_QUALITY_MAXIMUM: return 100;
case IRIS_SCREENSHOT_JPG_QUALITY_CUSTOM: return iris->screenshot_jpg_quality;
}
return 90;
}
bool save_screenshot(iris::instance* iris, std::string path) {
std::filesystem::path fn(path);
std::string directory = iris->snap_path;
if (iris->snap_path.empty()) {
directory = "snap";
}
std::filesystem::path p(directory);
std::string absolute_path;
std::string filename;
if (path.size()) {
filename = path;
} else {
filename = get_default_screenshot_filename(iris);
}
if (p.is_absolute()) {
absolute_path = p.string();
} else {
absolute_path = iris->pref_path + p.string();
}
absolute_path += "/" + filename;
if (fn.is_absolute()) {
absolute_path = fn.string();
}
void* ptr = nullptr;
int width = 0, height = 0, offset = 0;
if (iris->screenshot_mode == IRIS_SCREENSHOT_MODE_INTERNAL) {
renderer_image* image = iris->screenshot_shader_processing ? &iris->output_image : &iris->image;
ptr = vulkan::read_image(iris,
image->image,
image->format,
image->width,
image->height
);
width = image->width;
height = image->height;
} else {
ptr = vulkan::read_image(iris,
iris->main_window_data.Frames[0].Backbuffer,
iris->main_window_data.SurfaceFormat.format,
iris->main_window_data.Width,
iris->main_window_data.Height
);
width = iris->main_window_data.Width;
height = iris->main_window_data.Height;
if (!iris->fullscreen) {
offset = iris->menubar_height;
height -= iris->menubar_height;
}
}
if (!ptr) {
push_info(iris, "Couldn't save screenshot");
return false;
}
uint32_t* buf = (uint32_t*)malloc((width * 4) * height);
memcpy(buf, ((uint32_t*)ptr) + offset * width, (width * 4) * height);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
buf[x + (y * width)] |= 0xff000000;
}
}
int r = 0;
switch (iris->screenshot_format) {
case IRIS_SCREENSHOT_FORMAT_PNG:
r = stbi_write_png(absolute_path.c_str(), width, height, 4, buf, width * 4);
break;
case IRIS_SCREENSHOT_FORMAT_BMP:
r = stbi_write_bmp(absolute_path.c_str(), width, height, 4, buf);
break;
case IRIS_SCREENSHOT_FORMAT_JPG:
r = stbi_write_jpg(absolute_path.c_str(), width, height, 4, buf, get_screenshot_jpg_quality(iris));
break;
case IRIS_SCREENSHOT_FORMAT_TGA:
r = stbi_write_tga(absolute_path.c_str(), width, height, 4, buf);
break;
}
printf("Saving screenshot to '%s' (%dx%d, %d bpp): %s\n",
absolute_path.c_str(), width, height, 32, r ? "Success" : "Failure"
);
free(ptr);
free(buf);
if (!r) {
push_info(iris, "Couldn't save screenshot");
return false;
}
iris->screenshot_counter++;
push_info(iris, "Screenshot saved as '" + filename + "'");
return true;
}
void handle_keydown_event(iris::instance* iris, SDL_Event* event) {
SDL_Keycode key = event->key.key;
switch (key) {
case SDLK_SPACE: {
iris->pause = !iris->pause;
// vulkan::wait_idle(iris);
} break;
case SDLK_F9: {
vulkan::wait_idle(iris);
bool saved = save_screenshot(iris);
} break;
case SDLK_F11: {
iris->fullscreen = !iris->fullscreen;
SDL_SetWindowFullscreen(iris->window, iris->fullscreen ? true : false);
} break;
case SDLK_F1: {
printf("ps2: Sending poweroff signal\n");
ps2_cdvd_power_off(iris->ps2->cdvd);
} break;
}
iris->last_input_event_read = false;
iris->last_input_event_value = 1.0f;
iris->last_input_event = sdl_event_to_input_event(event);
if (iris->input_devices[0]) iris->input_devices[0]->handle_event(iris, event);
if (iris->input_devices[1]) iris->input_devices[1]->handle_event(iris, event);
}
void handle_keyup_event(iris::instance* iris, SDL_Event* event) {
// Add special keyup handling here if needed
if (iris->input_devices[0]) iris->input_devices[0]->handle_event(iris, event);
if (iris->input_devices[1]) iris->input_devices[1]->handle_event(iris, event);
}
}
================================================
FILE: frontend/iris.cpp
================================================
// Standard includes
#include
#include
#include
#include
#include
#include
// Iris includes
#include "iris.hpp"
#include "config.hpp"
#include "ee/ee_def.hpp"
#include "ee/vu_def.hpp"
// SDL3 includes
#include
// External includes
#include "res/IconsMaterialSymbols.h"
namespace iris {
void add_recent(iris::instance* iris, std::string file, int type) {
auto it = std::find_if(iris->recents.begin(), iris->recents.end(), [file, type](const recent& a) {
return a.type == type && a.path == file;
});
if (it != iris->recents.end()) {
iris->recents.erase(it);
iris->recents.push_front({file, type});
return;
}
iris->recents.push_front({file, type});
if (iris->recents.size() == 11)
iris->recents.pop_back();
}
int open_file(iris::instance* iris, std::string file) {
std::filesystem::path path(file);
std::string ext = path.extension().string();
for (char& c : ext)
c = tolower(c);
// Load disc image
if (ext == ".iso" || ext == ".bin" || ext == ".cue" ||
ext == ".chd" || ext == ".cso" || ext == ".zso") {
if (ps2_cdvd_open(iris->ps2->cdvd, file.c_str(), 0))
return 1;
char* boot_file = disc_get_boot_path(iris->ps2->cdvd->disc);
if (!boot_file)
return 2;
elf::load_symbols_from_disc(iris);
renderer_reset(iris->renderer);
ps2_set_system(iris->ps2, iris->system);
ps2_load_bios(iris->ps2, iris->bios_path.c_str());
ps2_boot_file(iris->ps2, boot_file);
iris->loaded = file;
if (iris->autostart) {
iris->pause = false;
}
return 0;
}
elf::load_symbols_from_file(iris, file);
// Note: We need the trailing whitespaces here because of IOMAN HLE
// Load executable
file = "host: " + file;
renderer_reset(iris->renderer);
ps2_set_system(iris->ps2, iris->system);
ps2_load_bios(iris->ps2, iris->bios_path.c_str());
ps2_boot_file(iris->ps2, file.c_str());
iris->loaded = file;
if (iris->autostart) {
iris->pause = false;
}
return 0;
}
void update_title(iris::instance* iris) {
char buf[512];
std::string base = "";
if (iris->loaded.size()) {
base = std::filesystem::path(iris->loaded).filename().string();
}
sprintf(buf, base.size() ? IRIS_TITLE " | %s" : IRIS_TITLE,
base.c_str()
);
SDL_SetWindowTitle(iris->window, buf);
}
void update_time(iris::instance* iris) {
int t = SDL_GetTicks() - iris->ticks;
if (t < 500)
return;
if (iris->fps == 0.0f) {
iris->fps = (float)iris->frames;
} else {
iris->fps += (float)iris->frames;
iris->fps /= 2.0f;
}
iris->ticks = SDL_GetTicks();
iris->frames = 0;
}
void sleep_limiter(iris::instance* iris) {
uint32_t ticks = (1.0f / iris->fps_cap) * 1000.0f;
std::this_thread::sleep_for(std::chrono::milliseconds(ticks / 2));
// uint32_t now = SDL_GetTicks();
// while ((SDL_GetTicks() - now) < ticks) {
// std::this_thread::sleep_for(std::chrono::milliseconds(ticks / 4));
// }
}
static inline void do_cycle(iris::instance* iris) {
ps2_cycle(iris->ps2);
if (iris->step_out) {
// jr $ra
if (iris->ps2->ee->opcode == 0x03e00008) {
iris->step_out = false;
iris->pause = true;
// Consume the delay slot
ps2_cycle(iris->ps2);
}
}
if (iris->step_over) {
if (iris->ps2->ee->pc == iris->step_over_addr) {
iris->step_over = false;
iris->pause = true;
}
}
for (const iris::breakpoint& b : iris->breakpoints) {
if (b.cpu == iris::BKPT_CPU_EE) {
if (iris->ps2->ee->pc == b.addr) {
iris->pause = true;
}
} else {
if (iris->ps2->iop->pc == b.addr) {
iris->pause = true;
}
}
}
}
void update_window(iris::instance* iris) {
using namespace ImGui;
// Limit FPS to 60 only when paused
if (iris->limit_fps && iris->pause)
sleep_limiter(iris);
update_title(iris);
update_time(iris);
ImGuiIO& io = ImGui::GetIO();
// Start the Dear ImGui frame
if (SDL_GetWindowFlags(iris->window) & SDL_WINDOW_MINIMIZED) {
SDL_Delay(1);
return;
}
// Resize swapchain?
int width, height;
SDL_GetWindowSize(iris->window, &width, &height);
if (width > 0 && height > 0 && (iris->swapchain_rebuild || iris->main_window_data.Width != width || iris->main_window_data.Height != height)) {
ImGui_ImplVulkan_SetMinImageCount(iris->min_image_count);
ImGui_ImplVulkanH_CreateOrResizeWindow(
iris->instance,
iris->physical_device,
iris->device,
&iris->main_window_data,
iris->queue_family,
nullptr,
width, height,
iris->min_image_count,
0
);
iris->main_window_data.FrameIndex = 0;
iris->swapchain_rebuild = false;
}
// Start the Dear ImGui frame
ImGui_ImplVulkan_NewFrame();
ImGui_ImplSDL3_NewFrame();
ImGui::NewFrame();
if (!iris->fullscreen) {
show_main_menubar(iris);
}
DockSpaceOverViewport(0, GetMainViewport(), ImGuiDockNodeFlags_PassthruCentralNode);
// Drop file fade animation
if (iris->drop_file_active) {
iris->drop_file_alpha += iris->drop_file_alpha_delta;
if (iris->drop_file_alpha_delta > 0.0f) {
if (iris->drop_file_alpha >= 1.0f) {
iris->drop_file_alpha = 1.0f;
iris->drop_file_alpha_delta = 0.0f;
}
} else {
if (iris->drop_file_alpha <= 0.0f) {
iris->drop_file_alpha = 0.0f;
iris->drop_file_alpha_delta = 0.0f;
iris->drop_file_active = false;
}
}
GetForegroundDrawList()->AddRectFilled(
ImVec2(0, 0),
ImVec2(width, height),
ImColor(0.0f, 0.0f, 0.0f, iris->drop_file_alpha * 0.35f)
);
ImVec2 text_size = CalcTextSize("Drop file here to launch");
PushFont(iris->font_icons_big);
ImVec2 icon_size = CalcTextSize(ICON_MS_DOWNLOAD);
ImVec2 total_size = ImVec2(
std::max(icon_size.x, text_size.x),
icon_size.y + text_size.y
);
GetForegroundDrawList()->AddText(
ImVec2(width / 2 - icon_size.x / 2, height / 2 - icon_size.y),
ImColor(1.0f, 1.0f, 1.0f, iris->drop_file_alpha),
ICON_MS_DOWNLOAD
);
PopFont();
GetForegroundDrawList()->AddText(
ImVec2(width / 2 - text_size.x / 2, height / 2),
ImColor(1.0f, 1.0f, 1.0f, iris->drop_file_alpha),
"Drop file here to launch"
);
}
if (iris->show_ee_control) show_ee_control(iris);
if (iris->show_ee_state) show_ee_state(iris);
if (iris->show_ee_logs) show_ee_logs(iris);
if (iris->show_ee_interrupts) show_ee_interrupts(iris);
if (iris->show_ee_dmac) show_ee_dmac(iris);
if (iris->show_iop_control) show_iop_control(iris);
if (iris->show_iop_state) show_iop_state(iris);
if (iris->show_iop_logs) show_iop_logs(iris);
if (iris->show_iop_interrupts) show_iop_interrupts(iris);
if (iris->show_iop_modules) show_iop_modules(iris);
if (iris->show_iop_dma) show_iop_dma(iris);
if (iris->show_gs_debugger) show_gs_debugger(iris);
if (iris->show_spu2_debugger) show_spu2_debugger(iris);
if (iris->show_memory_viewer) show_memory_viewer(iris);
if (iris->show_vu_disassembler) show_vu_disassembler(iris);
if (iris->show_status_bar && !iris->fullscreen) show_status_bar(iris);
if (iris->show_breakpoints) show_breakpoints(iris);
if (iris->show_about_window) show_about_window(iris);
if (iris->show_settings) show_settings(iris);
if (iris->show_pad_debugger) show_pad_debugger(iris);
if (iris->show_symbols) show_symbols(iris);
if (iris->show_threads) show_threads(iris);
if (iris->show_sysmem_logs) show_sysmem_logs(iris);
if (iris->show_memory_card_tool) show_memory_card_tool(iris);
if (iris->show_memory_search) show_memory_search(iris);
// if (iris->show_gamelist) show_gamelist(iris);
if (iris->show_imgui_demo) ShowDemoWindow(&iris->show_imgui_demo);
if (iris->show_bios_setting_window) show_bios_setting_window(iris);
if (iris->show_overlay) show_overlay(iris);
// Display little pause icon in the top right corner
if (iris->pause) {
ImVec2 ts = CalcTextSize(ICON_MS_PAUSE);
ImVec2 offset = ImVec2(10.0f, 10.0f);
ImVec2 padding = ImVec2(0.0f, 0.0f);
ts.x -= 1.0f;
int menubar_offset = 0;
if (!iris->fullscreen) {
menubar_offset += iris->menubar_height;
}
// GetBackgroundDrawList()->AddRectFilled(
// ImVec2(width - ts.x - offset.x - padding.x, menubar_offset + offset.y - padding.y),
// ImVec2(width - offset.x + padding.x, menubar_offset + ts.y + offset.y + padding.y),
// GetColorU32(GetStyleColorVec4(ImGuiCol_WindowBg)), 8.0f
// );
GetBackgroundDrawList(GetMainViewport())->AddText(
ImVec2(width - ts.x - offset.x, menubar_offset + offset.y),
GetColorU32(GetStyleColorVec4(ImGuiCol_Text)),
ICON_MS_PAUSE
);
}
handle_animations(iris);
// Rendering
ImGui::Render();
ImDrawData* draw_data = ImGui::GetDrawData();
const bool main_is_minimized = (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f);
iris->main_window_data.ClearValue.color.float32[0] = 0.0f;
iris->main_window_data.ClearValue.color.float32[1] = 0.0f;
iris->main_window_data.ClearValue.color.float32[2] = 0.0f;
iris->main_window_data.ClearValue.color.float32[3] = 1.0f;
if (!main_is_minimized) {
if (!imgui::render_frame(iris, draw_data)) {
printf("iris: Failed to render ImGui frame\n");
}
}
iris->frames++;
}
iris::instance* create() {
return new iris::instance();
}
bool init(iris::instance* iris, int argc, const char* argv[]) {
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_EVENTS | SDL_INIT_GAMEPAD)) {
fprintf(stderr, "iris: Failed to init SDL \'%s\'\n", SDL_GetError());
return false;
}
// Create and check window
iris->main_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay());
// Init preferences path
if (std::filesystem::exists("portable") || std::filesystem::exists("portable.txt")) {
iris->pref_path = "./";
} else {
char* pref = SDL_GetPrefPath("Allkern", "Iris");
iris->pref_path = std::string(pref);
SDL_free(pref);
}
if (!iris::emu::init(iris)) {
fprintf(stderr, "iris: Failed to initialize emulator state\n");
return false;
}
if (!iris::settings::init(iris, argc, argv)) {
fprintf(stderr, "iris: Failed to initialize settings\n");
return false;
}
iris->window = SDL_CreateWindow(
IRIS_TITLE,
iris->window_width, iris->window_height,
SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE |
SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WINDOW_HIDDEN
);
if (!iris->window) {
printf("iris: Failed to create SDL window \'%s\'\n", SDL_GetError());
return false;
}
if (!iris::vulkan::init(iris, iris->vulkan_enable_validation_layers)) {
fprintf(stderr, "iris: Failed to initialize Vulkan\n");
return false;
}
if (!iris::imgui::init(iris)) {
fprintf(stderr, "iris: Failed to initialize ImGui\n");
return false;
}
if (!iris::platform::init(iris)) {
fprintf(stderr, "iris: Failed to initialize platform\n");
return false;
}
if (!iris::audio::init(iris)) {
fprintf(stderr, "iris: Failed to initialize audio\n");
return false;
}
if (!iris::render::init(iris)) {
fprintf(stderr, "iris: Failed to initialize render state\n");
return false;
}
if (!iris::input::init(iris)) {
fprintf(stderr, "iris: Failed to initialize input\n");
return false;
}
for (const std::string& s : iris->shader_passes_pending)
shaders::push(iris, s);
iris->shader_passes_pending.clear();
// Sadly we need to start a frame here to measure menubar height
ImGui_ImplVulkan_NewFrame();
ImGui_ImplSDL3_NewFrame();
ImGui::NewFrame();
iris->menubar_height = ImGui::GetFrameHeight();
ImGui::EndFrame();
if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
ImGui::UpdatePlatformWindows();
ImGui::RenderPlatformWindowsDefault();
}
SDL_SetWindowSize(iris->window, iris->window_width, iris->window_height + get_menubar_height(iris));
SDL_ShowWindow(iris->window);
return true;
}
SDL_AppResult update(iris::instance* iris) {
if (iris->double_click_counter) {
iris->double_click_counter--;
}
if (iris->pause) {
iris->step_out = false;
iris->step_over = false;
if (iris->step) {
ps2_step_ee(iris->ps2);
iris->step = false;
}
iris::update_window(iris);
return SDL_APP_CONTINUE;
}
// Execute until VBlank
while (!ps2_gs_is_vblank(iris->ps2->gs)) {
do_cycle(iris);
if (iris->pause) {
iris::update_window(iris);
return SDL_APP_CONTINUE;
}
}
// Draw frame
iris::update_window(iris);
// Execute until vblank is over
while (ps2_gs_is_vblank(iris->ps2->gs)) {
do_cycle(iris);
if (iris->pause) {
iris::update_window(iris);
return SDL_APP_CONTINUE;
}
}
// float p = ((float)iris->ps2->ee->eenull_counter / (float)(4920115)) * 100.0f;
// printf("ee: Time spent idling: %ld cycles (%.2f%%) INTC reads: %d CSR reads: %d (%.1f fps)\n", iris->ps2->ee->eenull_counter, p, iris->ps2->ee->intc_reads, iris->ps2->ee->csr_reads, 1.0f / ImGui::GetIO().DeltaTime);
iris->ps2->ee->eenull_counter = 0;
iris->ps2->ee->intc_reads = 0;
iris->ps2->ee->csr_reads = 0;
return SDL_APP_CONTINUE;
}
SDL_AppResult handle_events(iris::instance* iris, SDL_Event* event) {
ImGui_ImplSDL3_ProcessEvent(event);
switch (event->type) {
case SDL_EVENT_QUIT: {
return SDL_APP_SUCCESS;
} break;
case SDL_EVENT_MOUSE_BUTTON_DOWN: {
if (ImGui::GetIO().WantCaptureMouse) {
break;
}
if (event->button.button == SDL_BUTTON_LEFT && event->button.windowID == SDL_GetWindowID(iris->window)) {
if (iris->double_click_counter) {
if ((SDL_GetTicks() - iris->double_click_counter) > iris->double_click_interval) {
iris->double_click_counter = SDL_GetTicks();
} else {
iris->fullscreen = !iris->fullscreen;
SDL_SetWindowFullscreen(iris->window, iris->fullscreen);
}
} else {
iris->double_click_counter = SDL_GetTicks();
}
}
} break;
case SDL_EVENT_GAMEPAD_ADDED: {
SDL_Gamepad* gamepad = SDL_OpenGamepad(event->gdevice.which);
if (!gamepad) {
SDL_Log("Failed to open gamepad ID %u: %s", (unsigned int) event->gdevice.which, SDL_GetError());
}
if (iris->ds[0] && ((iris->input_devices[0] == nullptr) || (iris->input_devices[0]->get_type() == 0))) {
if (iris->input_devices[0]) delete iris->input_devices[0];
iris->input_devices[0] = new iris::gamepad_device(event->gdevice.which);
iris->input_devices[0]->set_slot(0);
if (iris->input_map[0] <= 1) {
iris->input_map[0] = 1;
}
push_info(iris, "\'" + std::string(SDL_GetGamepadName(gamepad)) + "\' connected to slot 1");
} else if (iris->ds[1] && ((iris->input_devices[1] == nullptr) || (iris->input_devices[1]->get_type() == 0))) {
if (iris->input_devices[1]) delete iris->input_devices[1];
iris->input_devices[1] = new iris::gamepad_device(event->gdevice.which);
iris->input_devices[1]->set_slot(1);
if (iris->input_map[1] <= 1) {
iris->input_map[1] = 1;
}
push_info(iris, "\'" + std::string(SDL_GetGamepadName(gamepad)) + "\' connected to slot 2");
} else {
push_info(iris, "\'" + std::string(SDL_GetGamepadName(gamepad)) + "\' connected");
}
iris->gamepads[event->gdevice.which] = gamepad;
} break;
case SDL_EVENT_GAMEPAD_REMOVED: {
SDL_Gamepad* gamepad = iris->gamepads[event->gdevice.which];
for (int i = 0; i < 2; i++) {
if (iris->input_devices[i] && iris->input_devices[i]->get_type() == 1) {
iris::gamepad_device* gp = static_cast(iris->input_devices[i]);
if (gp->get_id() == event->gdevice.which) {
delete iris->input_devices[i];
iris->input_devices[i] = new iris::keyboard_device();
if (iris->input_map[i] <= 1) {
iris->input_map[i] = 0;
}
push_info(iris, "\'" + std::string(SDL_GetGamepadName(gamepad)) + "\' in slot " + std::to_string(i + 1) + " disconnected");
}
}
}
if (gamepad) {
SDL_CloseGamepad(gamepad);
iris->gamepads.erase(event->gdevice.which);
}
} break;
case SDL_EVENT_WINDOW_CLOSE_REQUESTED: {
if (event->window.windowID == SDL_GetWindowID(iris->window)) {
return SDL_APP_SUCCESS;
}
} break;
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
case SDL_EVENT_GAMEPAD_BUTTON_UP:
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
case SDL_EVENT_KEY_UP: {
iris->last_input_event_read = false;
iris->last_input_event = input::sdl_event_to_input_event(event);
if (event->type == SDL_EVENT_GAMEPAD_AXIS_MOTION) {
iris->last_input_event_value = fabs(event->gaxis.value / 32767.0f);
} else {
iris->last_input_event_value = 1.0f;
}
if (iris->input_devices[0]) iris->input_devices[0]->handle_event(iris, event);
if (iris->input_devices[1]) iris->input_devices[1]->handle_event(iris, event);
} break;
case SDL_EVENT_KEY_DOWN: {
input::handle_keydown_event(iris, event);
} break;
case SDL_EVENT_DROP_BEGIN: {
iris->drop_file_active = true;
iris->drop_file_alpha = 0.0f;
iris->drop_file_alpha_delta = 1.0f / 10.0f;
iris->drop_file_alpha_target = 1.0f;
} break;
case SDL_EVENT_DROP_COMPLETE: {
iris->drop_file_active = true;
iris->drop_file_alpha = iris->drop_file_alpha_target;
iris->drop_file_alpha_delta = -(1.0f / 10.0f);
iris->drop_file_alpha_target = 0.0f;
} break;
case SDL_EVENT_DROP_FILE: {
if (!event->drop.data)
break;
std::string path(event->drop.data);
std::filesystem::path p(path);
if (std::filesystem::is_regular_file(p)) {
if (open_file(iris, path)) {
push_info(iris, "Failed to open file: " + path);
} else {
add_recent(iris, path, RECENT_TYPE_PS2);
}
} else {
if (emu::load_arcade(iris, path)) {
add_recent(iris, path, RECENT_TYPE_ARCADE);
} else {
push_info(iris, "Failed to boot arcade: " + path);
}
}
// Maybe not needed anymore?
// SDL_free(event->drop.data);
} break;
}
return SDL_APP_CONTINUE;
}
int get_menubar_height(iris::instance* iris) {
if (iris->show_status_bar) {
return iris->menubar_height * 2;
}
return iris->menubar_height;
}
void destroy(iris::instance* iris) {
for (int i = 0; i < 2; i++) {
if (iris->input_devices[i]) {
delete iris->input_devices[i];
iris->input_devices[i] = nullptr;
}
}
if (iris->imgui_enable_viewports) {
iris->show_ee_control = false;
iris->show_ee_state = false;
iris->show_ee_logs = false;
iris->show_ee_interrupts = false;
iris->show_ee_dmac = false;
iris->show_iop_control = false;
iris->show_iop_state = false;
iris->show_iop_logs = false;
iris->show_iop_interrupts = false;
iris->show_iop_modules = false;
iris->show_iop_dma = false;
iris->show_gs_debugger = false;
iris->show_spu2_debugger = false;
iris->show_memory_viewer = false;
iris->show_memory_search = false;
iris->show_vu_disassembler = false;
iris->show_breakpoints = false;
iris->show_threads = false;
iris->show_sysmem_logs = false;
iris->show_imgui_demo = false;
iris->show_overlay = false;
}
if (iris->window) SDL_HideWindow(iris->window);
iris::imgui::cleanup(iris);
iris::audio::close(iris);
iris::settings::close(iris);
iris::render::destroy(iris);
iris::vulkan::cleanup(iris);
iris::platform::destroy(iris);
iris::emu::destroy(iris);
if (iris->window) SDL_DestroyWindow(iris->window);
SDL_Quit();
delete iris;
}
}
================================================
FILE: frontend/iris.hpp
================================================
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include "gs/renderer/renderer.hpp"
#include "gs/renderer/config.hpp"
#include
#include
#include "imgui.h"
#include "imgui_impl_sdl3.h"
#include "imgui_impl_vulkan.h"
#include "ps2.h"
#include "config.hpp"
namespace iris {
#define RENDER_ASPECT_NATIVE 0
#define RENDER_ASPECT_STRETCH 1
#define RENDER_ASPECT_STRETCH_KEEP 2
#define RENDER_ASPECT_4_3 3
#define RENDER_ASPECT_16_9 4
#define RENDER_ASPECT_5_4 5
#define RENDER_ASPECT_AUTO 6
#define IRIS_THEME_GRANITE 0
#define IRIS_THEME_IMGUI_DARK 1
#define IRIS_THEME_IMGUI_LIGHT 2
#define IRIS_THEME_IMGUI_CLASSIC 3
#define IRIS_THEME_CHERRY 4
#define IRIS_THEME_SOURCE 5
#define IRIS_SCREENSHOT_FORMAT_PNG 0
#define IRIS_SCREENSHOT_FORMAT_BMP 1
#define IRIS_SCREENSHOT_FORMAT_JPG 2
#define IRIS_SCREENSHOT_FORMAT_TGA 3
#define IRIS_SCREENSHOT_MODE_INTERNAL 0
#define IRIS_SCREENSHOT_MODE_DISPLAY 1
#define IRIS_SCREENSHOT_JPG_QUALITY_MINIMUM 0
#define IRIS_SCREENSHOT_JPG_QUALITY_LOW 1
#define IRIS_SCREENSHOT_JPG_QUALITY_MEDIUM 2
#define IRIS_SCREENSHOT_JPG_QUALITY_HIGH 3
#define IRIS_SCREENSHOT_JPG_QUALITY_MAXIMUM 4
#define IRIS_SCREENSHOT_JPG_QUALITY_CUSTOM 5
#define IRIS_CODEVIEW_COLOR_SCHEME_SOLARIZED_DARK 0
#define IRIS_CODEVIEW_COLOR_SCHEME_SOLARIZED_LIGHT 1
#define IRIS_CODEVIEW_COLOR_SCHEME_ONE_DARK_PRO 2
#define IRIS_CODEVIEW_COLOR_SCHEME_CATPPUCCIN_LATTE 3
#define IRIS_CODEVIEW_COLOR_SCHEME_CATPPUCCIN_FRAPPE 4
#define IRIS_CODEVIEW_COLOR_SCHEME_CATPPUCCIN_MACCHIATO 5
#define IRIS_CODEVIEW_COLOR_SCHEME_CATPPUCCIN_MOCHA 6
#define IRIS_TITLEBAR_DEFAULT 0
#define IRIS_TITLEBAR_SEAMLESS 1
class instance;
// class widget {
// public:
// virtual bool init(iris::instance* iris) = 0;
// virtual void render(iris::instance* iris) = 0;
// virtual ~widget() = default;
// };
enum : int {
BKPT_CPU_EE,
BKPT_CPU_IOP
};
struct breakpoint {
uint32_t addr;
const char* symbol = nullptr;
int cpu;
bool cond_r, cond_w, cond_x;
int size;
bool enabled;
};
struct move_animation {
int frames;
int frames_remaining;
float source_x, source_y;
float target_x, target_y;
float x, y;
};
struct fade_animation {
int frames;
int frames_remaining;
int source_alpha, target_alpha;
int alpha;
};
struct notification {
int type;
int state;
int frames;
int frames_remaining;
float width, height;
float text_width, text_height;
bool end;
move_animation move;
fade_animation fade;
std::string text;
};
struct elf_symbol {
char* name;
uint32_t addr;
uint32_t size;
};
enum {
RECENT_TYPE_PS2,
RECENT_TYPE_ARCADE
};
enum {
INPUT_CONTROLLER_DUALSHOCK2
// Large To-do list here, we're missing the Namco GunCon
// controllers, JogCon, NegCon, Buzz! Buzzer, the Train
// controllers, Taiko Drum Master controller, the Dance Dance
// Revolution mat, Guitar Hero controllers, etc.
};
struct input_device {
int m_slot;
void set_slot(int slot) {
this->m_slot = slot;
}
int get_slot() {
return this->m_slot;
}
virtual int get_type() = 0;
virtual void handle_event(iris::instance* iris, SDL_Event* event) = 0;
};
class keyboard_device : public input_device {
public:
int get_type() override {
return 0;
}
void handle_event(iris::instance* iris, SDL_Event* event) override;
};
class gamepad_device : public input_device {
SDL_JoystickID id;
public:
gamepad_device(SDL_JoystickID id) : id(id) {}
int get_type() override {
return 1;
}
SDL_JoystickID get_id() {
return id;
}
void handle_event(iris::instance* iris, SDL_Event* event) override;
};
union input_event {
struct {
uint32_t id;
uint32_t type;
};
uint64_t u64;
};
enum event {
IRIS_EVENT_KEYBOARD,
IRIS_EVENT_GAMEPAD_BUTTON,
IRIS_EVENT_GAMEPAD_AXIS_POS,
IRIS_EVENT_GAMEPAD_AXIS_NEG
};
enum input_action : uint32_t {
IRIS_DS_BT_CROSS,
IRIS_DS_BT_CIRCLE,
IRIS_DS_BT_SQUARE,
IRIS_DS_BT_TRIANGLE,
IRIS_DS_BT_START,
IRIS_DS_BT_SELECT,
IRIS_DS_BT_ANALOG,
IRIS_DS_BT_UP,
IRIS_DS_BT_DOWN,
IRIS_DS_BT_LEFT,
IRIS_DS_BT_RIGHT,
IRIS_DS_BT_L1,
IRIS_DS_BT_R1,
IRIS_DS_BT_L2,
IRIS_DS_BT_R2,
IRIS_DS_BT_L3,
IRIS_DS_BT_R3,
IRIS_DS_AX_RIGHTV_POS,
IRIS_DS_AX_RIGHTV_NEG,
IRIS_DS_AX_RIGHTH_POS,
IRIS_DS_AX_RIGHTH_NEG,
IRIS_DS_AX_LEFTV_POS,
IRIS_DS_AX_LEFTV_NEG,
IRIS_DS_AX_LEFTH_POS,
IRIS_DS_AX_LEFTH_NEG,
IRIS_S14X_SW_DOWN,
IRIS_S14X_SW_UP,
IRIS_S14X_SW_ENTER,
IRIS_S14X_SW_TEST,
IRIS_S14X_SW_SERVICE,
IRIS_S14X_SW_P1_START,
IRIS_S14X_SW_P2_START,
IRIS_S14X_SW_P3_START,
IRIS_S14X_SW_P4_START,
IRIS_INPUT_ACTION_MAX
};
struct vertex {
struct {
float x, y;
} pos, uv;
static constexpr const VkVertexInputBindingDescription get_binding_description() {
VkVertexInputBindingDescription binding_description = {};
binding_description.binding = 0;
binding_description.stride = sizeof(vertex);
binding_description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
return binding_description;
}
static constexpr const std::array get_attribute_descriptions() {
std::array attribute_descriptions = {};
attribute_descriptions[0].binding = 0;
attribute_descriptions[0].location = 0;
attribute_descriptions[0].format = VK_FORMAT_R32G32_SFLOAT;
attribute_descriptions[0].offset = offsetof(vertex, pos);
attribute_descriptions[1].binding = 0;
attribute_descriptions[1].location = 1;
attribute_descriptions[1].format = VK_FORMAT_R32G32_SFLOAT;
attribute_descriptions[1].offset = offsetof(vertex, uv);
return attribute_descriptions;
}
};
struct texture {
int width = 0, height = 0, stride = 0;
VkDeviceSize image_size = 0;
VkImage image = VK_NULL_HANDLE;
VkImageView image_view = VK_NULL_HANDLE;
VkSampler sampler = VK_NULL_HANDLE;
VkDeviceMemory image_memory = VK_NULL_HANDLE;
VkDescriptorSet descriptor_set = VK_NULL_HANDLE;
};
namespace shaders {
class pass;
}
struct vulkan_gpu {
VkPhysicalDeviceType type = VK_PHYSICAL_DEVICE_TYPE_OTHER;
VkPhysicalDevice device = VK_NULL_HANDLE;
std::string name = "";
uint32_t api_version = 0;
};
template class bidirectional_map {
std::unordered_map m_forward_map;
std::unordered_map m_reverse_map;
public:
void insert(const Key& key, const Value& value) {
m_forward_map[key] = value;
m_reverse_map[value] = key;
}
std::unordered_map & forward_map() {
return m_forward_map;
}
std::unordered_map & reverse_map() {
return m_reverse_map;
}
bool erase_by_key(const Key& key) {
auto it = m_forward_map.find(key);
if (it != m_forward_map.end()) {
Value value = it->second;
m_forward_map.erase(it);
m_reverse_map.erase(value);
return true;
}
return false;
}
bool erase_by_value(const Value& value) {
auto it = m_reverse_map.find(value);
if (it != m_reverse_map.end()) {
Key key = it->second;
m_reverse_map.erase(it);
m_forward_map.erase(key);
return true;
}
return false;
}
void clear() {
m_forward_map.clear();
m_reverse_map.clear();
}
Value* get_value(const Key& key) {
auto it = m_forward_map.find(key);
if (it != m_forward_map.end()) {
return &it->second;
}
return nullptr;
}
Key* get_key(const Value& value) {
auto it = m_reverse_map.find(value);
if (it != m_reverse_map.end()) {
return &it->second;
}
return nullptr;
}
};
struct mapping {
std::string name;
bidirectional_map map;
};
struct recent {
std::string path;
int type;
};
struct instance {
SDL_Window* window = nullptr;
SDL_AudioStream* stream = nullptr;
// Vulkan state
std::vector instance_extensions;
std::vector instance_layers;
std::vector device_extensions;
std::vector device_layers;
std::vector enabled_instance_extensions;
std::vector enabled_instance_layers;
std::vector enabled_device_extensions;
std::vector enabled_device_layers;
std::vector vulkan_gpus;
VkApplicationInfo app_info = {};
VkInstanceCreateInfo instance_create_info = {};
VkDeviceCreateInfo device_create_info = {};
VkInstance instance = VK_NULL_HANDLE;
VkPhysicalDevice physical_device = VK_NULL_HANDLE;
VkPhysicalDeviceFeatures2 device_features = {};
VkDeviceQueueCreateInfo queue_create_info = {};
uint32_t queue_family = (uint32_t)-1;
VkQueue queue = VK_NULL_HANDLE;
VkDevice device = VK_NULL_HANDLE;
VkDescriptorPool descriptor_pool = VK_NULL_HANDLE;
ImGui_ImplVulkanH_Window main_window_data = {};
uint32_t min_image_count = 2;
bool swapchain_rebuild = false;
VkSurfaceKHR surface = VK_NULL_HANDLE;
float main_scale = 1;
VkPhysicalDeviceVulkan11Features vulkan_11_features = {};
VkPhysicalDeviceVulkan12Features vulkan_12_features = {};
VkPhysicalDeviceSubgroupSizeControlFeatures subgroup_size_control_features = {};
VkSampler sampler[3] = { VK_NULL_HANDLE };
bool cubic_supported = false;
VkDescriptorSetLayout descriptor_set_layout = VK_NULL_HANDLE;
VkDescriptorSet descriptor_set = VK_NULL_HANDLE;
std::vector descriptor_sets = {};
VkPipelineLayout pipeline_layout = VK_NULL_HANDLE;
VkRenderPass render_pass = VK_NULL_HANDLE;
VkPipeline pipeline = VK_NULL_HANDLE;
VkClearValue clear_value = { 0.11, 0.11, 0.11, 1.0 };
VkBuffer vertex_buffer = VK_NULL_HANDLE;
VkDeviceMemory vertex_buffer_memory = VK_NULL_HANDLE;
VkBuffer vertex_staging_buffer = VK_NULL_HANDLE;
VkDeviceMemory vertex_staging_buffer_memory = VK_NULL_HANDLE;
VkDeviceSize vertex_buffer_size = 0;
VkBuffer index_buffer = VK_NULL_HANDLE;
VkDeviceMemory index_buffer_memory = VK_NULL_HANDLE;
std::array vertices = {};
std::array indices = {};
renderer_image image = {};
renderer_image output_image = {};
// Multipass shader stuff
std::vector shader_passes_pending;
std::vector shader_passes = {};
VkDescriptorSetLayout shader_descriptor_set_layout = VK_NULL_HANDLE;
VkDescriptorSet shader_descriptor_set = VK_NULL_HANDLE;
std::vector shader_descriptor_sets = {};
VkShaderModule default_vert_shader = VK_NULL_HANDLE;
struct {
VkImage image = VK_NULL_HANDLE;
VkDeviceMemory memory = VK_NULL_HANDLE;
VkImageView view = VK_NULL_HANDLE;
} shader_framebuffers[2];
std::vector > shader_pass_framebuffers = {};
struct ps2_state* ps2 = nullptr;
unsigned int window_width = 960;
unsigned int window_height = 720;
unsigned int render_width = 640;
unsigned int render_height = 480;
unsigned int renderer_backend = RENDERER_BACKEND_HARDWARE;
renderer_state* renderer = nullptr;
texture ps2_memory_card_icon = {};
texture ps1_memory_card_icon = {};
texture pocketstation_icon = {};
texture dualshock2_icon = {};
texture iris_icon = {};
ImFont* font_small_code = nullptr;
ImFont* font_code = nullptr;
ImFont* font_small = nullptr;
ImFont* font_heading = nullptr;
ImFont* font_body = nullptr;
ImFont* font_icons = nullptr;
ImFont* font_icons_big = nullptr;
ImFont* font_black = nullptr;
std::string elf_path = "";
std::string boot_path = "";
std::string bios_path = "";
std::string rom1_path = "";
std::string rom2_path = "";
std::string nvram_path = "";
std::string disc_path = "";
std::string pref_path = "";
std::string mcd0_path = "";
std::string mcd1_path = "";
std::string snap_path = "";
std::string flash_path = "";
std::string ini_path = "";
std::string gcdb_path = "";
uint8_t mac_address[6] = { 0 };
bool core0_mute[24] = { false };
bool core1_mute[24] = { false };
int core0_solo = -1;
int core1_solo = -1;
bool open = false;
bool pause = true;
bool step = false;
bool step_over = false;
bool step_out = false;
uint32_t step_over_addr = 0;
bool show_ee_control = false;
bool show_ee_state = false;
bool show_ee_logs = false;
bool show_ee_interrupts = false;
bool show_ee_dmac = false;
bool show_iop_control = false;
bool show_iop_state = false;
bool show_iop_logs = false;
bool show_iop_interrupts = false;
bool show_iop_modules = false;
bool show_iop_dma = false;
bool show_sysmem_logs = false;
bool show_gs_debugger = false;
bool show_spu2_debugger = false;
bool show_memory_viewer = false;
bool show_status_bar = true;
bool show_breakpoints = false;
bool show_settings = false;
bool show_pad_debugger = false;
bool show_symbols = false;
bool show_threads = false;
bool show_memory_card_tool = false;
bool show_imgui_demo = false;
bool show_vu_disassembler = false;
bool show_overlay = false;
bool show_memory_search = false;
// Special windows
bool show_bios_setting_window = false;
bool show_about_window = false;
bool fullscreen = false;
int aspect_mode = RENDER_ASPECT_AUTO;
int filter = 1;
bool integer_scaling = false;
float scale = 1.5f;
int window_mode = 0;
bool ee_control_follow_pc = true;
bool iop_control_follow_pc = true;
uint32_t ee_control_address = 0;
uint32_t iop_control_address = 0;
bool skip_fmv = false;
int system = PS2_SYSTEM_AUTO;
int theme = IRIS_THEME_GRANITE;
bool enable_shaders = false;
int vulkan_physical_device = -1;
int vulkan_selected_device_index = 0;
bool vulkan_enable_validation_layers = false;
bool imgui_enable_viewports = false;
int codeview_color_scheme = 0;
ImColor codeview_color_text = IM_COL32(131, 148, 150, 255);
ImColor codeview_color_comment = IM_COL32(88, 110, 117, 255);
ImColor codeview_color_mnemonic = IM_COL32(211, 167, 30, 255);
ImColor codeview_color_number = IM_COL32(138, 143, 226, 255);
ImColor codeview_color_register = IM_COL32(68, 169, 240, 255);
ImColor codeview_color_other = IM_COL32(89, 89, 89, 255);
ImColor codeview_color_background = IM_COL32(30, 30, 30, 255);
ImColor codeview_color_highlight = IM_COL32(75, 75, 75, 255);
float codeview_font_scale = 1.0f;
bool codeview_use_theme_background = true;
bool autostart = true;
int angle = 0;
bool flip_x = false;
bool flip_y = false;
uint64_t double_click_interval = 500;
uint64_t double_click_counter = 0;
std::deque recents;
bool dump_to_file = true;
std::string settings_path = "";
std::string mappings_path = "";
int frames = 0;
float fps = 0.0f;
unsigned int ticks = 0;
int menubar_height = 0;
bool mute = false;
bool prev_mute = false;
float volume = 1.0f;
int timescale = 8;
bool mute_adma = true;
bool vsync = true;
float ui_scale = 1.0f;
int screenshot_format = IRIS_SCREENSHOT_FORMAT_PNG;
int screenshot_jpg_quality_mode = IRIS_SCREENSHOT_JPG_QUALITY_MAXIMUM;
int screenshot_jpg_quality = 50;
int screenshot_mode = IRIS_SCREENSHOT_MODE_INTERNAL;
int docking_mode = 0;
bool screenshot_shader_processing = false;
input_device* input_devices[2] = { nullptr };
std::unordered_map gamepads;
std::vector input_maps = {};
int input_map[2] = { -1, -1 };
input_event last_input_event = {};
bool last_input_event_read = true;
float last_input_event_value = 0.0f;
bool limit_fps = true;
float fps_cap = 60.0f;
std::string loaded = "";
std::vector ee_log = { "" };
std::vector iop_log = { "" };
std::vector sysmem_log = { "" };
std::vector breakpoints = {};
std::deque notifications = {};
struct ds_state* ds[2] = { nullptr };
struct mcd_state* mcd[2] = { nullptr };
int mcd_slot_type[2] = { 0 };
// input_device* device[2];
float drop_file_alpha = 0.0f;
float drop_file_alpha_delta = 0.0f;
float drop_file_alpha_target = 0.0f;
bool drop_file_active = false;
// Debug
std::vector symbols;
std::vector strtab;
std::vector audio_buf;
float avg_fps;
float avg_frames;
int screenshot_counter = 0;
// Renderer configs
hardware_config hardware_backend_config;
// Windows-specific settings
#ifdef _WIN32
int windows_titlebar_style = IRIS_TITLEBAR_DEFAULT;
bool windows_enable_borders = true;
bool windows_dark_mode = true;
#endif
};
struct push_constants {
float resolution[2];
int frame;
};
namespace audio {
bool init(iris::instance* iris);
void close(iris::instance* iris);
void update(void* udata, SDL_AudioStream* stream, int additional_amount, int total_amount);
bool mute(iris::instance* iris);
void unmute(iris::instance* iris);
}
namespace settings {
bool init(iris::instance* iris, int argc, const char* argv[]);
bool check_for_quick_exit(int argc, const char* argv[]);
void close(iris::instance* iris);
}
namespace shaders {
class pass {
VkPipelineLayout m_pipeline_layout = VK_NULL_HANDLE;
VkPipeline m_pipeline = VK_NULL_HANDLE;
VkRenderPass m_render_pass = VK_NULL_HANDLE;
VkImageView m_input = VK_NULL_HANDLE;
VkShaderModule m_vert_shader = VK_NULL_HANDLE;
VkShaderModule m_frag_shader = VK_NULL_HANDLE;
iris::instance* m_iris = nullptr;
std::string m_id;
public:
void destroy();
bool init(iris::instance* iris, const void* data, size_t size, std::string id);
void swap(pass& rhs) {
VkPipelineLayout pipeline_layout = m_pipeline_layout;
VkPipeline pipeline = m_pipeline;
VkRenderPass render_pass = m_render_pass;
VkImageView input = m_input;
VkShaderModule vert_shader = m_vert_shader;
VkShaderModule frag_shader = m_frag_shader;
iris::instance* iris = m_iris;
std::string id = m_id;
m_pipeline_layout = rhs.m_pipeline_layout;
m_pipeline = rhs.m_pipeline;
m_render_pass = rhs.m_render_pass;
m_input = rhs.m_input;
m_vert_shader = rhs.m_vert_shader;
m_frag_shader = rhs.m_frag_shader;
m_iris = rhs.m_iris;
m_id = rhs.m_id;
rhs.m_pipeline_layout = pipeline_layout;
rhs.m_pipeline = pipeline;
rhs.m_render_pass = render_pass;
rhs.m_input = input;
rhs.m_vert_shader = vert_shader;
rhs.m_frag_shader = frag_shader;
rhs.m_iris = iris;
rhs.m_id = id;
}
pass(iris::instance* iris, const void* data, size_t size, std::string id);
pass(pass&& other);
pass() = default;
~pass();
pass& operator=(pass&& other);
VkPipelineLayout& get_pipeline_layout();
VkPipeline& get_pipeline();
VkRenderPass& get_render_pass();
VkImageView& get_input();
VkShaderModule& get_vert_shader();
VkShaderModule& get_frag_shader();
std::string get_id() const;
bool bypass = false;
bool ready();
bool rebuild();
};
void push(iris::instance* iris, void* data, size_t size, std::string id);
void push(iris::instance* iris, std::string id);
void pop(iris::instance* iris);
void insert(iris::instance* iris, int i, void* data, size_t size, std::string id);
void insert(iris::instance* iris, std::string id);
void erase(iris::instance* iris, int i);
pass* at(iris::instance* iris, int i);
void swap(iris::instance* iris, int i1, int i2);
pass* front(iris::instance* iris);
pass* back(iris::instance* iris);
size_t count(iris::instance* iris);
void clear(iris::instance* iris);
std::vector & vector(iris::instance* iris);
}
namespace imgui {
bool init(iris::instance* iris);
void set_theme(iris::instance* iris, int theme, bool set_bg_color = true);
void set_codeview_scheme(iris::instance* iris, int scheme);
bool render_frame(iris::instance* iris, ImDrawData* draw_data);
void cleanup(iris::instance* iris);
void set_vsync(iris::instance* iris, bool vsync);
// Wrapper for ImGui::Begin that sets a default size
bool BeginEx(const char* name, bool* p_open, ImGuiWindowFlags flags = 0);
}
namespace vulkan {
bool init(iris::instance* iris, bool enable_validation = false);
void cleanup(iris::instance* iris);
texture upload_texture(iris::instance* iris, void* pixels, int width, int height, int stride);
void free_texture(iris::instance* iris, texture& tex);
void* read_image(iris::instance* iris, VkImage image, VkFormat format, int width, int height);
void wait_idle(iris::instance* iris);
}
namespace platform {
bool init(iris::instance* iris);
bool apply_settings(iris::instance* iris);
void destroy(iris::instance* iris);
}
namespace elf {
bool load_symbols_from_disc(iris::instance* iris);
bool load_symbols_from_file(iris::instance* iris, std::string path);
}
namespace emu {
bool init(iris::instance* iris);
void destroy(iris::instance* iris);
bool load_arcade(iris::instance* iris, std::string path);
int attach_memory_card(iris::instance* iris, int slot, const char* path);
void detach_memory_card(iris::instance* iris, int slot);
const char* get_system_name(iris::instance* iris, int system);
const char* get_current_system_name(iris::instance* iris);
int get_system_count(iris::instance* iris);
}
namespace render {
bool init(iris::instance* iris);
void destroy(iris::instance* iris);
bool render_frame(iris::instance* iris, VkCommandBuffer command_buffer, VkFramebuffer framebuffer);
bool save_screenshot(iris::instance* iris, std::string path);
void switch_backend(iris::instance* iris, int backend);
void refresh(iris::instance* iris);
}
namespace input {
bool init(iris::instance* iris);
void init_default_mapping(iris::instance* iris, int id);
void load_db_default(iris::instance* iris);
bool load_db_from_file(iris::instance* iris, const char* path);
input_action* get_input_action(iris::instance* iris, int slot, uint64_t input);
input_event sdl_event_to_input_event(SDL_Event* event);
std::string get_default_screenshot_filename(iris::instance* iris);
void execute_action(iris::instance* iris, input_action action, int slot, float value);
bool save_screenshot(iris::instance* iris, std::string path = "");
void handle_keydown_event(iris::instance* iris, SDL_Event* event);
void handle_keyup_event(iris::instance* iris, SDL_Event* event);
}
iris::instance* create();
bool init(iris::instance* iris, int argc, const char* argv[]);
void destroy(iris::instance* iris);
SDL_AppResult handle_events(iris::instance* iris, SDL_Event* event);
SDL_AppResult update(iris::instance* iris);
void update_window(iris::instance* iris);
int get_menubar_height(iris::instance* iris);
void show_main_menubar(iris::instance* iris);
void show_ee_control(iris::instance* iris);
void show_ee_state(iris::instance* iris);
void show_ee_logs(iris::instance* iris);
void show_ee_interrupts(iris::instance* iris);
void show_ee_dmac(iris::instance* iris);
void show_iop_control(iris::instance* iris);
void show_iop_state(iris::instance* iris);
void show_iop_logs(iris::instance* iris);
void show_iop_interrupts(iris::instance* iris);
void show_iop_modules(iris::instance* iris);
void show_iop_dma(iris::instance* iris);
void show_sysmem_logs(iris::instance* iris);
void show_gs_debugger(iris::instance* iris);
void show_spu2_debugger(iris::instance* iris);
void show_memory_viewer(iris::instance* iris);
void show_vu_disassembler(iris::instance* iris);
void show_status_bar(iris::instance* iris);
void show_breakpoints(iris::instance* iris);
void show_about_window(iris::instance* iris);
void show_settings(iris::instance* iris);
void show_pad_debugger(iris::instance* iris);
void show_symbols(iris::instance* iris);
void show_threads(iris::instance* iris);
void show_overlay(iris::instance* iris);
void show_memory_card_tool(iris::instance* iris);
void show_bios_setting_window(iris::instance* iris);
void show_memory_search(iris::instance* iris);
// void show_gamelist(iris::instance* iris);
void handle_keydown_event(iris::instance* iris, SDL_Event* event);
void handle_keyup_event(iris::instance* iris, SDL_Event* event);
void handle_scissor_event(void* udata);
void handle_drag_and_drop_event(void* udata, const char* path);
void handle_ee_tty_event(void* udata, char c);
void handle_iop_tty_event(void* udata, char c);
void handle_sysmem_tty_event(void* udata, char c);
void handle_animations(iris::instance* iris);
void push_info(iris::instance* iris, std::string text);
void add_recent(iris::instance* iris, std::string file, int type);
int open_file(iris::instance* iris, std::string file);
}
================================================
FILE: frontend/notifications.cpp
================================================
#include "iris.hpp"
#include "res/IconsMaterialSymbols.h"
namespace iris {
enum {
STATE_IDLE,
STATE_MOVING = 1,
STATE_FADING = 2
};
enum {
TYPE_INFO = 0,
};
static inline int seconds_to_frames(float s) {
return s * 60.0f;
}
float ease_in_out(float t) {
if (t <= 0.5f) return 2.0f * t * t;
t -= 0.5f;
return 2.0f * t * (1.0f - t) + 0.5f;
}
void handle_move(iris::notification* notif) {
float f = (float)notif->move.frames_remaining / (float)notif->move.frames;
f = ease_in_out(f);
float fs = f;
float ft = 1.0 - f;
notif->move.x = notif->move.source_x * fs + notif->move.target_x * ft;
notif->move.y = notif->move.source_y * fs + notif->move.target_y * ft;
if (!notif->move.frames_remaining) {
notif->state &= ~STATE_MOVING;
} else {
notif->move.frames_remaining--;
}
}
void handle_fade(iris::notification* notif) {
float f = (float)notif->fade.frames_remaining / (float)notif->fade.frames;
f = ease_in_out(f);
float fs = f;
float ft = 1.0 - f;
notif->fade.alpha = notif->fade.source_alpha * fs + notif->fade.target_alpha * ft;
if (!notif->fade.frames_remaining) {
notif->state &= ~STATE_FADING;
} else {
notif->fade.frames_remaining--;
}
}
void render_notification(iris::instance* iris, iris::notification* notif) {
using namespace ImGui;
ImGuiStyle& style = ImGui::GetStyle();
ImVec4 bg = style.Colors[ImGuiCol_MenuBarBg];
ImVec4 tc = style.Colors[ImGuiCol_Text];
ImVec4 td = style.Colors[ImGuiCol_TextDisabled];
uint32_t alpha = (notif->fade.alpha) << 24;
uint32_t bg_col =
((uint32_t)(bg.x * 255.0) << 0 ) |
((uint32_t)(bg.y * 255.0) << 8 ) |
((uint32_t)(bg.z * 255.0) << 16) |
alpha;
uint32_t text_col =
((uint32_t)(tc.x * 255.0) << 0 ) |
((uint32_t)(tc.y * 255.0) << 8 ) |
((uint32_t)(tc.z * 255.0) << 16) |
alpha;
uint32_t icon_col = 0x799fa7 | alpha;
uint32_t bar_col =
((uint32_t)(td.x * 255.0) << 0 ) |
((uint32_t)(td.y * 255.0) << 8 ) |
((uint32_t)(td.z * 255.0) << 16) |
alpha;
float x = notif->move.x;
float y = notif->move.y;
float width = notif->width;
float height = notif->height;
GetForegroundDrawList()->AddRectFilled(
ImVec2(x, y),
ImVec2(x + width, y + height),
bg_col, 10.0, ImDrawFlags_RoundCornersAll
);
GetForegroundDrawList()->AddText(
ImVec2(x + 10, y + (height / 2) - (notif->text_height / 2)), icon_col, ICON_MS_INFO
);
GetForegroundDrawList()->AddText(
ImVec2(x + 35, y + (height / 2) - (notif->text_height / 2)), text_col, notif->text.c_str()
);
int progress_width = width * (1.0 - ((float)notif->frames_remaining / (float)notif->frames));
GetForegroundDrawList()->AddRectFilled(
ImVec2(x, y + height - 2),
ImVec2(x + progress_width, y + height),
bar_col, 10.0, ImDrawFlags_RoundCornersBottom
);
}
void remove_notification(iris::instance* iris, int i) {
iris::notification& n = iris->notifications.at(i);
for (unsigned int j = i + 1; j < iris->notifications.size(); j++) {
iris::notification& n1 = iris->notifications.at(j);
int target_x = n1.state & STATE_MOVING ? n1.move.target_x : n1.move.x;
int target_y = n1.state & STATE_MOVING ? n1.move.target_y : n1.move.y;
n1.move.source_x = n1.move.x;
n1.move.source_y = n1.move.y;
n1.move.target_x = target_x;
n1.move.target_y = target_y + n.height + 10;
n1.move.frames = seconds_to_frames(0.25);
n1.move.frames_remaining = n.move.frames;
n1.state |= STATE_MOVING;
}
iris->notifications.erase(std::begin(iris->notifications) + i);
}
void handle_animations(iris::instance* iris) {
for (unsigned int i = 0; i < iris->notifications.size(); i++) {
iris::notification& n = iris->notifications.at(i);
render_notification(iris, &n);
if (n.frames_remaining) {
n.frames_remaining--;
if (!n.frames_remaining) {
n.fade.source_alpha = n.fade.alpha;
n.fade.target_alpha = 0;
n.fade.frames = seconds_to_frames(0.25);
n.fade.frames_remaining = n.fade.frames;
n.state |= STATE_FADING;
n.end = true;
}
}
if (n.state == STATE_IDLE) {
if (n.end) {
remove_notification(iris, i);
}
continue;
}
if (n.state & STATE_MOVING) handle_move(&n);
if (n.state & STATE_FADING) handle_fade(&n);
}
}
void push_notification(iris::instance* iris, iris::notification notif) {
for (iris::notification& n : iris->notifications) {
int target_x = n.state & STATE_MOVING ? n.move.target_x : n.move.x;
int target_y = n.state & STATE_MOVING ? n.move.target_y : n.move.y;
n.move.source_x = n.move.x;
n.move.source_y = n.move.y;
n.move.target_x = target_x;
n.move.target_y = target_y - notif.height - 10;
n.move.frames = seconds_to_frames(0.25);
n.move.frames_remaining = n.move.frames;
n.state |= STATE_MOVING;
}
iris->notifications.push_front(notif);
}
void push_info(iris::instance* iris, std::string text) {
using namespace ImGui;
iris::notification notif;
int window_width, window_height;
int statusbar_offset = iris->show_status_bar ? iris->menubar_height : 0;
SDL_GetWindowSizeInPixels(iris->window, &window_width, &window_height);
ImVec2 ts = CalcTextSize(text.c_str());
notif.text = text;
notif.text_width = ts.x;
notif.text_height = ts.y;
notif.width = notif.text_width + 50;
notif.height = notif.text_height + 25;
notif.frames = seconds_to_frames(5.0);
notif.frames_remaining = notif.frames;
notif.state = STATE_MOVING | STATE_FADING;
notif.move.source_x = window_width + 5;
notif.move.source_y = window_height - notif.height - 10 - statusbar_offset;
notif.move.target_x = window_width - notif.width - 10;
notif.move.target_y = notif.move.source_y;
notif.move.frames = seconds_to_frames(0.25);
notif.move.frames_remaining = notif.move.frames;
notif.fade.source_alpha = 0;
notif.fade.target_alpha = 255;
notif.fade.frames = seconds_to_frames(0.25);
notif.fade.frames_remaining = notif.fade.frames;
notif.type = TYPE_INFO;
notif.end = false;
push_notification(iris, notif);
}
}
================================================
FILE: frontend/platform/stub.cpp
================================================
#include "iris.hpp"
// Note: This is a stub implementation for platforms that
// do not need special initialization
namespace iris::platform {
bool init(iris::instance* iris) {
return true;
}
bool apply_settings(iris::instance* iris) {
return true;
}
void destroy(iris::instance* iris) {}
}
================================================
FILE: frontend/platform/windows.cpp
================================================
#include "iris.hpp"
#include "imgui.h"
#include
namespace iris::platform {
bool init(iris::instance* iris) {
apply_settings(iris);
return true;
}
bool apply_settings(iris::instance* iris) {
SDL_PropertiesID props = SDL_GetWindowProperties(iris->window);
HWND hwnd = (HWND)SDL_GetPointerProperty(props, SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL);
COLORREF border_color = iris->windows_enable_borders ? DWMWA_COLOR_DEFAULT : DWMWA_COLOR_NONE;
if (!SUCCEEDED(DwmSetWindowAttribute(
hwnd,
DWMWINDOWATTRIBUTE::DWMWA_BORDER_COLOR,
&border_color,
sizeof(border_color)
))) {
return false;
}
if (iris->windows_titlebar_style == IRIS_TITLEBAR_DEFAULT) {
BOOL dark_mode = iris->windows_dark_mode;
if (!SUCCEEDED(DwmSetWindowAttribute(
hwnd,
DWMWINDOWATTRIBUTE::DWMWA_USE_IMMERSIVE_DARK_MODE,
&dark_mode,
sizeof(BOOL)
))) {
printf("iris: Failed to set immersive dark mode\n");
return false;
}
}
bool result = false;
switch (iris->windows_titlebar_style) {
case IRIS_TITLEBAR_DEFAULT: {
COLORREF color = DWMWA_COLOR_DEFAULT;
result = SUCCEEDED(DwmSetWindowAttribute(
hwnd,
DWMWINDOWATTRIBUTE::DWMWA_CAPTION_COLOR,
&color,
sizeof(color)
));
} break;
case IRIS_TITLEBAR_SEAMLESS: {
COLORREF color = 0;
ImVec4 menubar_color = ImGui::GetStyleColorVec4(ImGuiCol_MenuBarBg);
color = (uint32_t)(menubar_color.x * 255) |
((uint32_t)(menubar_color.y * 255) << 8) |
((uint32_t)(menubar_color.z * 255) << 16);
result = SUCCEEDED(DwmSetWindowAttribute(
hwnd,
DWMWINDOWATTRIBUTE::DWMWA_CAPTION_COLOR,
&color,
sizeof(color)
));
} break;
}
// ShowWindow(hwnd, SW_MINIMIZE);
// ShowWindow(hwnd, SW_RESTORE);
return result;
}
void destroy(iris::instance* iris) {}
}
================================================
FILE: frontend/render.cpp
================================================
#include
#include "iris.hpp"
#define RENDER_MAX_SHADER_PASSES 16
// INCBIN stuff
#define INCBIN_PREFIX g_
#define INCBIN_STYLE INCBIN_STYLE_SNAKE
#include "incbin.h"
INCBIN(default_vertex_shader, "../shaders/shader.spv");
namespace iris::render {
static int frame = 0;
static constexpr uint32_t DESCRIPTOR_SET_RING_SIZE = 8;
bool create_image(iris::instance* iris, uint32_t width, uint32_t height, VkFormat format, VkImageUsageFlags usage, VkImage& image, VkImageView& view, VkDeviceMemory& memory) {
VkImageCreateInfo image_info = {};
image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
image_info.imageType = VK_IMAGE_TYPE_2D;
image_info.extent.width = width;
image_info.extent.height = height;
image_info.extent.depth = 1;
image_info.mipLevels = 1;
image_info.arrayLayers = 1;
image_info.format = format;
image_info.tiling = VK_IMAGE_TILING_OPTIMAL;
image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
image_info.usage = usage;
image_info.samples = VK_SAMPLE_COUNT_1_BIT;
image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
if (vkCreateImage(iris->device, &image_info, nullptr, &image) != VK_SUCCESS) {
return false;
}
VkMemoryRequirements memory_requirements;
vkGetImageMemoryRequirements(iris->device, image, &memory_requirements);
VkMemoryAllocateInfo alloc_info = {};
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
alloc_info.allocationSize = memory_requirements.size;
VkPhysicalDeviceMemoryProperties memory_properties;
vkGetPhysicalDeviceMemoryProperties(iris->physical_device, &memory_properties);
for (uint32_t i = 0; i < memory_properties.memoryTypeCount; i++) {
if ((memory_requirements.memoryTypeBits & (1 << i)) &&
(memory_properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) == VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) {
alloc_info.memoryTypeIndex = i;
break;
}
}
if (vkAllocateMemory(iris->device, &alloc_info, nullptr, &memory) != VK_SUCCESS) {
fprintf(stderr, "render: Failed to allocate image memory\n");
vkDestroyImage(iris->device, image, nullptr);
return false;
}
vkBindImageMemory(iris->device, image, memory, 0);
VkImageViewCreateInfo image_view_info = {};
image_view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
image_view_info.image = image;
image_view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
image_view_info.format = format;
image_view_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
image_view_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
image_view_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
image_view_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
image_view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
image_view_info.subresourceRange.baseMipLevel = 0;
image_view_info.subresourceRange.levelCount = 1;
image_view_info.subresourceRange.baseArrayLayer = 0;
image_view_info.subresourceRange.layerCount = 1;
if (vkCreateImageView(iris->device, &image_view_info, nullptr, &view) != VK_SUCCESS) {
return false;
}
return true;
}
bool rebuild_framebuffers(iris::instance* iris) {
if (!shaders::count(iris))
return true;
vulkan::wait_idle(iris);
for (auto& pass_framebuffers : iris->shader_pass_framebuffers) {
for (VkFramebuffer& framebuffer : pass_framebuffers) {
if (framebuffer) {
vkDestroyFramebuffer(iris->device, framebuffer, nullptr);
framebuffer = VK_NULL_HANDLE;
}
}
}
iris->shader_pass_framebuffers.clear();
for (auto& fb : iris->shader_framebuffers) {
if (fb.view) vkDestroyImageView(iris->device, fb.view, nullptr);
if (fb.image) vkDestroyImage(iris->device, fb.image, nullptr);
if (fb.memory) vkFreeMemory(iris->device, fb.memory, nullptr);
bool res = create_image(iris,
iris->image.width,
iris->image.height,
iris->image.format,
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
fb.image,
fb.view,
fb.memory
);
if (!res) {
fprintf(stderr, "render: Failed to create shader pass framebuffer image\n");
return false;
}
}
const size_t pass_count = shaders::count(iris);
iris->shader_pass_framebuffers.resize(pass_count);
for (size_t pass_index = 0; pass_index < pass_count; pass_index++) {
auto* pass = shaders::at(iris, (int)pass_index);
if (!pass || !pass->ready()) {
iris->shader_pass_framebuffers[pass_index] = { VK_NULL_HANDLE, VK_NULL_HANDLE };
continue;
}
for (int framebuffer_index = 0; framebuffer_index < 2; framebuffer_index++) {
VkFramebufferCreateInfo framebuffer_info = {};
framebuffer_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebuffer_info.renderPass = pass->get_render_pass();
framebuffer_info.attachmentCount = 1;
framebuffer_info.pAttachments = &iris->shader_framebuffers[framebuffer_index].view;
framebuffer_info.width = iris->image.width;
framebuffer_info.height = iris->image.height;
framebuffer_info.layers = 1;
if (vkCreateFramebuffer(iris->device, &framebuffer_info, nullptr, &iris->shader_pass_framebuffers[pass_index][framebuffer_index]) != VK_SUCCESS) {
fprintf(stderr, "render: Failed to create shader pass framebuffer\n");
return false;
}
}
}
return true;
}
bool init(iris::instance* iris) {
// Initialize our renderer
iris->renderer = renderer_create();
renderer_create_info info = {};
info.backend = iris->renderer_backend;
info.gif = iris->ps2->gif;
info.gs = iris->ps2->gs;
info.instance = iris->instance;
info.device = iris->device;
info.physical_device = iris->physical_device;
info.instance_create_info = iris->instance_create_info;
info.device_create_info = iris->device_create_info;
switch (info.backend) {
case RENDERER_BACKEND_HARDWARE: {
info.config = &iris->hardware_backend_config;
} break;
}
if (!renderer_init(iris->renderer, info)) {
fprintf(stderr, "render: Failed to initialize renderer backend\n");
return false;
}
VkSamplerCreateInfo nearest_sampler_info = {};
nearest_sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
nearest_sampler_info.magFilter = VK_FILTER_NEAREST;
nearest_sampler_info.minFilter = VK_FILTER_NEAREST;
nearest_sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
nearest_sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
nearest_sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
nearest_sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
nearest_sampler_info.minLod = -1000;
nearest_sampler_info.maxLod = 1000;
nearest_sampler_info.maxAnisotropy = 1.0f;
if (vkCreateSampler(iris->device, &nearest_sampler_info, VK_NULL_HANDLE, &iris->sampler[0]) != VK_SUCCESS) {
fprintf(stderr, "render: Failed to create nearest texture sampler\n");
return false;
}
VkSamplerCreateInfo bilinear_sampler_info = {};
bilinear_sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
bilinear_sampler_info.magFilter = VK_FILTER_LINEAR;
bilinear_sampler_info.minFilter = VK_FILTER_LINEAR;
bilinear_sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
bilinear_sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
bilinear_sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
bilinear_sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
bilinear_sampler_info.minLod = -1000;
bilinear_sampler_info.maxLod = 1000;
bilinear_sampler_info.maxAnisotropy = 1.0f;
if (vkCreateSampler(iris->device, &bilinear_sampler_info, VK_NULL_HANDLE, &iris->sampler[1]) != VK_SUCCESS) {
fprintf(stderr, "render: Failed to create bilinear texture sampler\n");
return false;
}
if (iris->cubic_supported) {
VkSamplerCreateInfo cubic_sampler_info = {};
cubic_sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
cubic_sampler_info.magFilter = VK_FILTER_LINEAR;
cubic_sampler_info.minFilter = VK_FILTER_LINEAR;
cubic_sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
cubic_sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
cubic_sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
cubic_sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
cubic_sampler_info.minLod = -1000;
cubic_sampler_info.maxLod = 1000;
cubic_sampler_info.maxAnisotropy = 1.0f;
if (vkCreateSampler(iris->device, &cubic_sampler_info, VK_NULL_HANDLE, &iris->sampler[2]) != VK_SUCCESS) {
fprintf(stderr, "render: Failed to create cubic texture sampler\n");
return false;
}
}
VkShaderModuleCreateInfo vert_shader_create_info = {};
vert_shader_create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
vert_shader_create_info.pCode = (const uint32_t*)g_default_vertex_shader_data;
vert_shader_create_info.codeSize = g_default_vertex_shader_size;
if (vkCreateShaderModule(iris->device, &vert_shader_create_info, nullptr, &iris->default_vert_shader) != VK_SUCCESS) {
fprintf(stderr, "render: Failed to create default vertex shader module\n");
return false;
}
// Create descriptor set
VkDescriptorSetLayoutBinding sampler_layout_binding = {};
sampler_layout_binding.binding = 0;
sampler_layout_binding.descriptorCount = 1;
sampler_layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
sampler_layout_binding.pImmutableSamplers = nullptr;
sampler_layout_binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
VkDescriptorSetLayoutCreateInfo layout_info = {};
layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
layout_info.bindingCount = 1;
layout_info.pBindings = &sampler_layout_binding;
if (vkCreateDescriptorSetLayout(iris->device, &layout_info, nullptr, &iris->shader_descriptor_set_layout) != VK_SUCCESS) {
fprintf(stderr, "render: Failed to create descriptor set layout\n");
return false;
}
const uint32_t shader_descriptor_set_count = DESCRIPTOR_SET_RING_SIZE * RENDER_MAX_SHADER_PASSES;
std::vector shader_layouts(shader_descriptor_set_count, iris->shader_descriptor_set_layout);
iris->shader_descriptor_sets.resize(shader_descriptor_set_count, VK_NULL_HANDLE);
VkDescriptorSetAllocateInfo alloc_info = {};
alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
alloc_info.descriptorPool = iris->descriptor_pool;
alloc_info.descriptorSetCount = shader_descriptor_set_count;
alloc_info.pSetLayouts = shader_layouts.data();
if (vkAllocateDescriptorSets(iris->device, &alloc_info, iris->shader_descriptor_sets.data()) != VK_SUCCESS) {
fprintf(stderr, "render: Failed to allocate descriptor sets\n");
return false;
}
iris->shader_descriptor_set = iris->shader_descriptor_sets[0];
return true;
}
static inline VkDescriptorSet get_frame_descriptor_set(iris::instance* iris) {
if (!iris->descriptor_sets.size()) {
return iris->descriptor_set;
}
const uint32_t frame_index = iris->main_window_data.FrameIndex;
return iris->descriptor_sets[frame_index % iris->descriptor_sets.size()];
}
static inline VkDescriptorSet get_frame_shader_descriptor_set(iris::instance* iris, uint32_t pass_index) {
if (!iris->shader_descriptor_sets.size()) {
return iris->shader_descriptor_set;
}
const uint32_t frame_index = iris->main_window_data.FrameIndex % DESCRIPTOR_SET_RING_SIZE;
const uint32_t slot = (frame_index * RENDER_MAX_SHADER_PASSES) + (pass_index % RENDER_MAX_SHADER_PASSES);
return iris->shader_descriptor_sets[slot % iris->shader_descriptor_sets.size()];
}
static inline void update_vertex_buffer(iris::instance* iris, VkCommandBuffer command_buffer) {
SDL_Rect size, rect, display;
const int normalized_angle = ((iris->angle % 360) + 360) % 360;
const bool swap_axes = normalized_angle == 90 || normalized_angle == 270;
float aspect_ratio = 4.0 / 3.0;
if (swap_axes) {
aspect_ratio = 3.0 / 4.0;
}
float aspect_ratio_inv = 1.0f / aspect_ratio;
SDL_GetWindowSize(iris->window, &size.w, &size.h);
display.w = size.w;
display.h = size.h;
display.x = 0;
display.y = 0;
if (!iris->fullscreen) {
display.h -= iris->menubar_height;
display.y += iris->menubar_height;
if (iris->show_status_bar) {
display.h -= iris->menubar_height;
}
}
rect.w = iris->image.width;
rect.h = iris->image.height;
float scale = iris->integer_scaling ? floorf(iris->scale) : iris->scale;
switch (iris->aspect_mode) {
case RENDER_ASPECT_NATIVE: {
rect.w *= scale;
rect.h *= scale;
} break;
case RENDER_ASPECT_4_3: {
rect.w *= scale;
rect.h = (float)rect.w * (3.0f / 4.0f);
} break;
case RENDER_ASPECT_16_9: {
rect.w *= scale;
rect.h = (float)rect.w * (9.0f / 16.0f);
} break;
case RENDER_ASPECT_5_4: {
rect.w *= scale;
rect.h = (float)rect.w * (4.0f / 5.0f);
} break;
case RENDER_ASPECT_STRETCH: {
rect.w = display.w;
rect.h = display.h;
} break;
case RENDER_ASPECT_AUTO:
case RENDER_ASPECT_STRETCH_KEEP: {
if (swap_axes) {
std::swap(rect.w, rect.h);
}
rect.h = display.h;
rect.w = (float)rect.h * aspect_ratio;
// Scale vertically if the rect ends up being bigger
// than our display area
if (rect.w > display.w) {
rect.w = display.w;
rect.h = (float)rect.w * aspect_ratio_inv;
}
} break;
}
if (iris->aspect_mode != RENDER_ASPECT_AUTO && iris->aspect_mode != RENDER_ASPECT_STRETCH_KEEP) {
if (swap_axes) {
std::swap(rect.w, rect.h);
}
}
iris->render_width = rect.w;
iris->render_height = rect.h;
rect.x = display.x + ((display.w / 2) - (rect.w / 2));
rect.y = display.y + ((display.h / 2) - (rect.h / 2));
float x0 = (rect.x / ((float)size.w / 2.0f)) - 1.0f;
float y0 = (rect.y / ((float)size.h / 2.0f)) - 1.0f;
float x1 = ((rect.x + rect.w) / ((float)size.w / 2.0f)) - 1.0f;
float y1 = ((rect.y + rect.h) / ((float)size.h / 2.0f)) - 1.0f;
float uvs[4][2] = {
{0.0f, 1.0f},
{1.0f, 1.0f},
{1.0f, 0.0f},
{0.0f, 0.0f}
};
for (int i = 0; i < 4; i++) {
float u = uvs[i][0];
float v = uvs[i][1];
if (iris->flip_x) {
u = 1.0f - u;
}
if (iris->flip_y) {
v = 1.0f - v;
}
switch (normalized_angle) {
case 90: {
// Rotate clockwise in UV space.
const float prev_u = u;
u = v;
v = 1.0f - prev_u;
} break;
case 180: {
u = 1.0f - u;
v = 1.0f - v;
} break;
case 270: {
// Rotate counter-clockwise in UV space.
const float prev_u = u;
u = 1.0f - v;
v = prev_u;
} break;
}
uvs[i][0] = u;
uvs[i][1] = v;
}
iris->vertices[0] = vertex{ { x0, y0 }, {uvs[0][0], uvs[0][1]} };
iris->vertices[1] = vertex{ { x1, y0 }, {uvs[1][0], uvs[1][1]} };
iris->vertices[2] = vertex{ { x1, y1 }, {uvs[2][0], uvs[2][1]} };
iris->vertices[3] = vertex{ { x0, y1 }, {uvs[3][0], uvs[3][1]} };
void* ptr;
vkMapMemory(iris->device, iris->vertex_staging_buffer_memory, 0, iris->vertex_buffer_size, 0, &ptr);
memcpy(ptr, iris->vertices.data(), (size_t)iris->vertex_buffer_size);
vkUnmapMemory(iris->device, iris->vertex_staging_buffer_memory);
static VkBufferCopy region = {};
region.srcOffset = 0;
region.dstOffset = 0;
region.size = iris->vertex_buffer_size;
vkCmdCopyBuffer(
command_buffer,
iris->vertex_staging_buffer,
iris->vertex_buffer,
1,
®ion
);
}
static inline void update_descriptor_set(iris::instance* iris, VkDescriptorSet set, VkImageView view, VkSampler sampler) {
VkDescriptorImageInfo image_info = {};
image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
image_info.imageView = view;
image_info.sampler = sampler;
VkWriteDescriptorSet descriptor_write = {};
descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptor_write.dstSet = set;
descriptor_write.dstBinding = 0;
descriptor_write.dstArrayElement = 0;
descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptor_write.descriptorCount = 1;
descriptor_write.pImageInfo = &image_info;
vkUpdateDescriptorSets(iris->device, 1, &descriptor_write, 0, nullptr);
}
void render_shader_passes(iris::instance* iris, VkCommandBuffer command_buffer, VkImageView& output_view, VkImage& output_image) {
if (!shaders::count(iris))
return;
if (iris->shader_pass_framebuffers.size() != shaders::count(iris)) {
if (!rebuild_framebuffers(iris)) {
fprintf(stderr, "render: Failed to rebuild shader framebuffers\n");
return;
}
}
int i = 0;
for (size_t pass_index = 0; pass_index < shaders::vector(iris).size(); pass_index++) {
auto& pass = shaders::vector(iris)[pass_index];
if (pass->bypass || !pass->ready())
continue;
const int fb = i & 1;
const VkImageView input_view = i == 0 ? iris->image.view : iris->shader_framebuffers[fb ^ 1].view;
VkFramebuffer framebuffer = iris->shader_pass_framebuffers[pass_index][fb];
if (framebuffer == VK_NULL_HANDLE) {
if (!rebuild_framebuffers(iris)) {
fprintf(stderr, "render: Failed to rebuild shader framebuffers\n");
return;
}
framebuffer = iris->shader_pass_framebuffers[pass_index][fb];
if (framebuffer == VK_NULL_HANDLE) {
fprintf(stderr, "render: Shader framebuffer is null after rebuild\n");
return;
}
}
output_view = iris->shader_framebuffers[fb].view;
output_image = iris->shader_framebuffers[fb].image;
VkRenderPassBeginInfo info = {};
info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
info.renderPass = pass->get_render_pass();
info.framebuffer = framebuffer;
info.renderArea.extent.width = iris->image.width;
info.renderArea.extent.height = iris->image.height;
info.clearValueCount = 1;
info.pClearValues = &iris->clear_value;
VkDescriptorImageInfo image_info = {};
image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
image_info.imageView = input_view;
image_info.sampler = iris->sampler[iris->filter];
VkWriteDescriptorSet descriptor_write = {};
descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
const VkDescriptorSet shader_descriptor_set = get_frame_shader_descriptor_set(iris, i);
descriptor_write.dstSet = shader_descriptor_set;
descriptor_write.dstBinding = 0;
descriptor_write.dstArrayElement = 0;
descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptor_write.descriptorCount = 1;
descriptor_write.pImageInfo = &image_info;
vkUpdateDescriptorSets(iris->device, 1, &descriptor_write, 0, nullptr);
vkCmdBeginRenderPass(command_buffer, &info, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pass->get_pipeline());
VkDeviceSize offsets[] = { 0 };
vkCmdBindIndexBuffer(command_buffer, iris->index_buffer, 0, VK_INDEX_TYPE_UINT16);
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pass->get_pipeline_layout(), 0, 1, &shader_descriptor_set, 0, nullptr);
VkViewport viewport = {};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = iris->image.width;
viewport.height = iris->image.height;
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
vkCmdSetViewport(command_buffer, 0, 1, &viewport);
VkRect2D scissor = {};
scissor.offset = {0, 0};
scissor.extent = { (uint32_t)iris->image.width, (uint32_t)iris->image.height };
vkCmdSetScissor(command_buffer, 0, 1, &scissor);
push_constants constants = {
.resolution = { (float)iris->image.width, (float)iris->image.height },
.frame = frame
};
vkCmdPushConstants(command_buffer, pass->get_pipeline_layout(), VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(push_constants), &constants);
vkCmdDrawIndexed(command_buffer, 6, 1, 0, 0, 0);
vkCmdEndRenderPass(command_buffer);
i++;
}
}
bool render_frame(iris::instance* iris, VkCommandBuffer command_buffer, VkFramebuffer framebuffer) {
renderer_image image;
if (iris->pause) {
image = iris->image;
} else {
image = renderer_get_frame(iris->renderer);
}
bool need_rebuild = image.width != iris->image.width ||
image.height != iris->image.height ||
image.format != iris->image.format;
iris->image = image;
if (need_rebuild && image.view != VK_NULL_HANDLE) {
vulkan::wait_idle(iris);
for (auto& pass : shaders::vector(iris)) {
if (!pass->rebuild()) {
fprintf(stderr, "render: Failed to rebuild shader pass\n");
return false;
}
}
if (!rebuild_framebuffers(iris)) {
fprintf(stderr, "render: Failed to rebuild shader pass framebuffers\n");
return false;
}
}
// Process shader passes here
iris->output_image = iris->image;
if (iris->enable_shaders && iris->output_image.view != VK_NULL_HANDLE) {
render_shader_passes(iris, command_buffer, iris->output_image.view, iris->output_image.image);
}
VkRenderPassBeginInfo info = {};
info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
info.renderPass = iris->main_window_data.RenderPass;
info.framebuffer = framebuffer;
info.renderArea.extent.width = iris->main_window_data.Width;
info.renderArea.extent.height = iris->main_window_data.Height;
info.clearValueCount = 1;
info.pClearValues = &iris->clear_value;
if (iris->output_image.view != VK_NULL_HANDLE) {
const VkDescriptorSet descriptor_set = get_frame_descriptor_set(iris);
update_vertex_buffer(iris, command_buffer);
update_descriptor_set(iris, descriptor_set, iris->output_image.view, iris->sampler[iris->filter]);
}
vkCmdBeginRenderPass(command_buffer, &info, VK_SUBPASS_CONTENTS_INLINE);
VkClearAttachment clear_attachment = {};
clear_attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
clear_attachment.colorAttachment = 0;
clear_attachment.clearValue = iris->clear_value;
VkClearRect clear_rect = {};
clear_rect.rect.offset = { 0, 0 };
clear_rect.rect.extent = { (uint32_t)iris->main_window_data.Width, (uint32_t)iris->main_window_data.Height };
clear_rect.baseArrayLayer = 0;
clear_rect.layerCount = 1;
vkCmdClearAttachments(command_buffer, 1, &clear_attachment, 1, &clear_rect);
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, iris->pipeline);
if (iris->output_image.view != VK_NULL_HANDLE) {
VkDeviceSize offsets[] = { 0 };
const VkDescriptorSet descriptor_set = get_frame_descriptor_set(iris);
vkCmdBindVertexBuffers(command_buffer, 0, 1, &iris->vertex_buffer, offsets);
vkCmdBindIndexBuffer(command_buffer, iris->index_buffer, 0, VK_INDEX_TYPE_UINT16);
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, iris->pipeline_layout, 0, 1, &descriptor_set, 0, nullptr);
}
VkViewport viewport = {};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = iris->main_window_data.Width;
viewport.height = iris->main_window_data.Height;
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
vkCmdSetViewport(command_buffer, 0, 1, &viewport);
VkRect2D scissor = {};
scissor.offset = {0, 0};
scissor.extent = { (uint32_t)iris->main_window_data.Width, (uint32_t)iris->main_window_data.Height };
vkCmdSetScissor(command_buffer, 0, 1, &scissor);
if (iris->output_image.view != VK_NULL_HANDLE) {
vkCmdDrawIndexed(command_buffer, 6, 1, 0, 0, 0);
}
vkCmdEndRenderPass(command_buffer);
if (!iris->pause)
frame++;
return true;
}
void switch_backend(iris::instance* iris, int backend) {
if (iris->renderer_backend == backend)
return;
renderer_destroy(iris->renderer);
iris->renderer = renderer_create();
renderer_create_info info = {};
info.backend = backend;
info.gif = iris->ps2->gif;
info.gs = iris->ps2->gs;
info.instance = iris->instance;
info.device = iris->device;
info.physical_device = iris->physical_device;
info.instance_create_info = iris->instance_create_info;
info.device_create_info = iris->device_create_info;
switch (info.backend) {
case RENDERER_BACKEND_HARDWARE: {
info.config = &iris->hardware_backend_config;
} break;
}
if (!renderer_init(iris->renderer, info)) {
fprintf(stderr, "render: Failed to initialize renderer backend\n");
} else {
iris->renderer_backend = backend;
}
}
void refresh(iris::instance* iris) {
switch (iris->renderer_backend) {
case RENDERER_BACKEND_HARDWARE: {
renderer_set_config(iris->renderer, &iris->hardware_backend_config);
} break;
}
iris->image = renderer_get_frame(iris->renderer);
if (iris->image.view == VK_NULL_HANDLE)
return;
if (shaders::count(iris) == 0)
return;
vulkan::wait_idle(iris);
for (auto& pass : shaders::vector(iris)) {
pass->rebuild();
}
rebuild_framebuffers(iris);
}
void destroy(iris::instance* iris) {
if (!iris->window)
return;
vulkan::wait_idle(iris);
for (auto& pass_framebuffers : iris->shader_pass_framebuffers) {
for (VkFramebuffer& framebuffer : pass_framebuffers) {
if (framebuffer) {
vkDestroyFramebuffer(iris->device, framebuffer, nullptr);
}
}
}
iris->shader_pass_framebuffers.clear();
for (auto& fb : iris->shader_framebuffers) {
if (fb.view) vkDestroyImageView(iris->device, fb.view, nullptr);
if (fb.image) vkDestroyImage(iris->device, fb.image, nullptr);
if (fb.memory) vkFreeMemory(iris->device, fb.memory, nullptr);
}
if (iris->shader_descriptor_set_layout) {
vkDestroyDescriptorSetLayout(iris->device, iris->shader_descriptor_set_layout, nullptr);
}
if (iris->default_vert_shader) {
vkDestroyShaderModule(iris->device, iris->default_vert_shader, nullptr);
}
shaders::clear(iris);
if (iris->renderer) renderer_destroy(iris->renderer);
}
}
================================================
FILE: frontend/res/IconsMaterialSymbols.h
================================================
// Generated by https://github.com/juliettef/IconFontCppHeaders script GenerateIconFontCppHeaders.py
// for C and C++
// from codepoints https://github.com/google/material-design-icons/raw/master/variablefont/MaterialSymbolsOutlined%5BFILL%2CGRAD%2Copsz%2Cwght%5D.codepoints
// for use with font https://github.com/google/material-design-icons/raw/master/variablefont/MaterialSymbolsOutlined%5BFILL,GRAD,opsz,wght%5D.ttf, https://github.com/google/material-design-icons/raw/master/variablefont/MaterialSymbolsRounded%5BFILL,GRAD,opsz,wght%5D.ttf, https://github.com/google/material-design-icons/raw/master/variablefont/MaterialSymbolsSharp%5BFILL,GRAD,opsz,wght%5D.ttf
#pragma once
#define FONT_ICON_FILE_NAME_MSO "MaterialSymbolsOutlined[FILL,GRAD,opsz,wght].ttf"
#define FONT_ICON_FILE_NAME_MSR "MaterialSymbolsRounded[FILL,GRAD,opsz,wght].ttf"
#define FONT_ICON_FILE_NAME_MSS "MaterialSymbolsSharp[FILL,GRAD,opsz,wght].ttf"
#define ICON_MIN_MS 0xe003
#define ICON_MAX_16_MS 0xf8ff
#define ICON_MAX_MS 0xf8ff
#define ICON_MS_10K "\xee\xa5\x91" // U+e951
#define ICON_MS_10MP "\xee\xa5\x92" // U+e952
#define ICON_MS_11MP "\xee\xa5\x93" // U+e953
#define ICON_MS_123 "\xee\xae\x8d" // U+eb8d
#define ICON_MS_12MP "\xee\xa5\x94" // U+e954
#define ICON_MS_13MP "\xee\xa5\x95" // U+e955
#define ICON_MS_14MP "\xee\xa5\x96" // U+e956
#define ICON_MS_15MP "\xee\xa5\x97" // U+e957
#define ICON_MS_16MP "\xee\xa5\x98" // U+e958
#define ICON_MS_17MP "\xee\xa5\x99" // U+e959
#define ICON_MS_18_UP_RATING "\xef\xa3\xbd" // U+f8fd
#define ICON_MS_18MP "\xee\xa5\x9a" // U+e95a
#define ICON_MS_19MP "\xee\xa5\x9b" // U+e95b
#define ICON_MS_1K "\xee\xa5\x9c" // U+e95c
#define ICON_MS_1K_PLUS "\xee\xa5\x9d" // U+e95d
#define ICON_MS_1X_MOBILEDATA "\xee\xbf\x8d" // U+efcd
#define ICON_MS_1X_MOBILEDATA_BADGE "\xef\x9f\xb1" // U+f7f1
#define ICON_MS_20MP "\xee\xa5\x9e" // U+e95e
#define ICON_MS_21MP "\xee\xa5\x9f" // U+e95f
#define ICON_MS_22MP "\xee\xa5\xa0" // U+e960
#define ICON_MS_23MP "\xee\xa5\xa1" // U+e961
#define ICON_MS_24MP "\xee\xa5\xa2" // U+e962
#define ICON_MS_2D "\xee\xbc\xb7" // U+ef37
#define ICON_MS_2K "\xee\xa5\xa3" // U+e963
#define ICON_MS_2K_PLUS "\xee\xa5\xa4" // U+e964
#define ICON_MS_2MP "\xee\xa5\xa5" // U+e965
#define ICON_MS_30FPS "\xee\xbf\x8e" // U+efce
#define ICON_MS_30FPS_SELECT "\xee\xbf\x8f" // U+efcf
#define ICON_MS_360 "\xee\x95\xb7" // U+e577
#define ICON_MS_3D_ROTATION "\xee\xa1\x8d" // U+e84d
#define ICON_MS_3G_MOBILEDATA "\xee\xbf\x90" // U+efd0
#define ICON_MS_3G_MOBILEDATA_BADGE "\xef\x9f\xb0" // U+f7f0
#define ICON_MS_3K "\xee\xa5\xa6" // U+e966
#define ICON_MS_3K_PLUS "\xee\xa5\xa7" // U+e967
#define ICON_MS_3MP "\xee\xa5\xa8" // U+e968
#define ICON_MS_3P "\xee\xbf\x91" // U+efd1
#define ICON_MS_4G_MOBILEDATA "\xee\xbf\x92" // U+efd2
#define ICON_MS_4G_MOBILEDATA_BADGE "\xef\x9f\xaf" // U+f7ef
#define ICON_MS_4G_PLUS_MOBILEDATA "\xee\xbf\x93" // U+efd3
#define ICON_MS_4K "\xee\x81\xb2" // U+e072
#define ICON_MS_4K_PLUS "\xee\xa5\xa9" // U+e969
#define ICON_MS_4MP "\xee\xa5\xaa" // U+e96a
#define ICON_MS_50MP "\xef\x9b\xb3" // U+f6f3
#define ICON_MS_5G "\xee\xbc\xb8" // U+ef38
#define ICON_MS_5G_MOBILEDATA_BADGE "\xef\x9f\xae" // U+f7ee
#define ICON_MS_5K "\xee\xa5\xab" // U+e96b
#define ICON_MS_5K_PLUS "\xee\xa5\xac" // U+e96c
#define ICON_MS_5MP "\xee\xa5\xad" // U+e96d
#define ICON_MS_60FPS "\xee\xbf\x94" // U+efd4
#define ICON_MS_60FPS_SELECT "\xee\xbf\x95" // U+efd5
#define ICON_MS_6_FT_APART "\xef\x88\x9e" // U+f21e
#define ICON_MS_6K "\xee\xa5\xae" // U+e96e
#define ICON_MS_6K_PLUS "\xee\xa5\xaf" // U+e96f
#define ICON_MS_6MP "\xee\xa5\xb0" // U+e970
#define ICON_MS_7K "\xee\xa5\xb1" // U+e971
#define ICON_MS_7K_PLUS "\xee\xa5\xb2" // U+e972
#define ICON_MS_7MP "\xee\xa5\xb3" // U+e973
#define ICON_MS_8K "\xee\xa5\xb4" // U+e974
#define ICON_MS_8K_PLUS "\xee\xa5\xb5" // U+e975
#define ICON_MS_8MP "\xee\xa5\xb6" // U+e976
#define ICON_MS_9K "\xee\xa5\xb7" // U+e977
#define ICON_MS_9K_PLUS "\xee\xa5\xb8" // U+e978
#define ICON_MS_9MP "\xee\xa5\xb9" // U+e979
#define ICON_MS_ABC "\xee\xae\x94" // U+eb94
#define ICON_MS_AC_UNIT "\xee\xac\xbb" // U+eb3b
#define ICON_MS_ACCESS_ALARM "\xee\xa1\x95" // U+e855
#define ICON_MS_ACCESS_ALARMS "\xee\xa1\x95" // U+e855
#define ICON_MS_ACCESS_TIME "\xee\xbf\x96" // U+efd6
#define ICON_MS_ACCESS_TIME_FILLED "\xee\xbf\x96" // U+efd6
#define ICON_MS_ACCESSIBILITY "\xee\xa1\x8e" // U+e84e
#define ICON_MS_ACCESSIBILITY_NEW "\xee\xa4\xac" // U+e92c
#define ICON_MS_ACCESSIBLE "\xee\xa4\x94" // U+e914
#define ICON_MS_ACCESSIBLE_FORWARD "\xee\xa4\xb4" // U+e934
#define ICON_MS_ACCOUNT_BALANCE "\xee\xa1\x8f" // U+e84f
#define ICON_MS_ACCOUNT_BALANCE_WALLET "\xee\xa1\x90" // U+e850
#define ICON_MS_ACCOUNT_BOX "\xee\xa1\x91" // U+e851
#define ICON_MS_ACCOUNT_CHILD "\xee\xa1\x92" // U+e852
#define ICON_MS_ACCOUNT_CHILD_INVERT "\xee\x99\x99" // U+e659
#define ICON_MS_ACCOUNT_CIRCLE "\xef\x88\x8b" // U+f20b
#define ICON_MS_ACCOUNT_CIRCLE_FILLED "\xef\x88\x8b" // U+f20b
#define ICON_MS_ACCOUNT_CIRCLE_OFF "\xef\x9e\xb3" // U+f7b3
#define ICON_MS_ACCOUNT_TREE "\xee\xa5\xba" // U+e97a
#define ICON_MS_ACTION_KEY "\xef\x94\x82" // U+f502
#define ICON_MS_ACTIVITY_ZONE "\xee\x87\xa6" // U+e1e6
#define ICON_MS_ACUTE "\xee\x93\x8b" // U+e4cb
#define ICON_MS_AD "\xee\x99\x9a" // U+e65a
#define ICON_MS_AD_GROUP "\xee\x99\x9b" // U+e65b
#define ICON_MS_AD_GROUP_OFF "\xee\xab\xa5" // U+eae5
#define ICON_MS_AD_OFF "\xef\x9e\xb2" // U+f7b2
#define ICON_MS_AD_UNITS "\xee\xbc\xb9" // U+ef39
#define ICON_MS_ADAPTIVE_AUDIO_MIC "\xef\x93\x8c" // U+f4cc
#define ICON_MS_ADAPTIVE_AUDIO_MIC_OFF "\xef\x93\x8b" // U+f4cb
#define ICON_MS_ADB "\xee\x98\x8e" // U+e60e
#define ICON_MS_ADD "\xee\x85\x85" // U+e145
#define ICON_MS_ADD_A_PHOTO "\xee\x90\xb9" // U+e439
#define ICON_MS_ADD_AD "\xee\x9c\xaa" // U+e72a
#define ICON_MS_ADD_ALARM "\xee\xa1\x96" // U+e856
#define ICON_MS_ADD_ALERT "\xee\x80\x83" // U+e003
#define ICON_MS_ADD_BOX "\xee\x85\x86" // U+e146
#define ICON_MS_ADD_BUSINESS "\xee\x9c\xa9" // U+e729
#define ICON_MS_ADD_CALL "\xef\x82\xb7" // U+f0b7
#define ICON_MS_ADD_CARD "\xee\xae\x86" // U+eb86
#define ICON_MS_ADD_CHART "\xee\xbc\xbc" // U+ef3c
#define ICON_MS_ADD_CIRCLE "\xee\x8e\xba" // U+e3ba
#define ICON_MS_ADD_CIRCLE_OUTLINE "\xee\x8e\xba" // U+e3ba
#define ICON_MS_ADD_COMMENT "\xee\x89\xa6" // U+e266
#define ICON_MS_ADD_DIAMOND "\xef\x92\x9c" // U+f49c
#define ICON_MS_ADD_HOME "\xef\xa3\xab" // U+f8eb
#define ICON_MS_ADD_HOME_WORK "\xef\xa3\xad" // U+f8ed
#define ICON_MS_ADD_IC_CALL "\xef\x82\xb7" // U+f0b7
#define ICON_MS_ADD_LINK "\xee\x85\xb8" // U+e178
#define ICON_MS_ADD_LOCATION "\xee\x95\xa7" // U+e567
#define ICON_MS_ADD_LOCATION_ALT "\xee\xbc\xba" // U+ef3a
#define ICON_MS_ADD_MODERATOR "\xee\xa5\xbd" // U+e97d
#define ICON_MS_ADD_NOTES "\xee\x82\x91" // U+e091
#define ICON_MS_ADD_PHOTO_ALTERNATE "\xee\x90\xbe" // U+e43e
#define ICON_MS_ADD_REACTION "\xee\x87\x93" // U+e1d3
#define ICON_MS_ADD_ROAD "\xee\xbc\xbb" // U+ef3b
#define ICON_MS_ADD_SHOPPING_CART "\xee\xa1\x94" // U+e854
#define ICON_MS_ADD_TASK "\xef\x88\xba" // U+f23a
#define ICON_MS_ADD_TO_DRIVE "\xee\x99\x9c" // U+e65c
#define ICON_MS_ADD_TO_HOME_SCREEN "\xee\x87\xbe" // U+e1fe
#define ICON_MS_ADD_TO_PHOTOS "\xee\x8e\x9d" // U+e39d
#define ICON_MS_ADD_TO_QUEUE "\xee\x81\x9c" // U+e05c
#define ICON_MS_ADD_TRIANGLE "\xef\x92\x8e" // U+f48e
#define ICON_MS_ADDCHART "\xee\xbc\xbc" // U+ef3c
#define ICON_MS_ADF_SCANNER "\xee\xab\x9a" // U+eada
#define ICON_MS_ADJUST "\xee\x8e\x9e" // U+e39e
#define ICON_MS_ADMIN_MEDS "\xee\x92\x8d" // U+e48d
#define ICON_MS_ADMIN_PANEL_SETTINGS "\xee\xbc\xbd" // U+ef3d
#define ICON_MS_ADS_CLICK "\xee\x9d\xa2" // U+e762
#define ICON_MS_AGENDER "\xef\xa2\x88" // U+f888
#define ICON_MS_AGRICULTURE "\xee\xa9\xb9" // U+ea79
#define ICON_MS_AIR "\xee\xbf\x98" // U+efd8
#define ICON_MS_AIR_FRESHENER "\xee\x8b\x8a" // U+e2ca
#define ICON_MS_AIR_PURIFIER "\xee\xa5\xbe" // U+e97e
#define ICON_MS_AIR_PURIFIER_GEN "\xee\xa0\xa9" // U+e829
#define ICON_MS_AIRLINE_SEAT_FLAT "\xee\x98\xb0" // U+e630
#define ICON_MS_AIRLINE_SEAT_FLAT_ANGLED "\xee\x98\xb1" // U+e631
#define ICON_MS_AIRLINE_SEAT_INDIVIDUAL_SUITE "\xee\x98\xb2" // U+e632
#define ICON_MS_AIRLINE_SEAT_LEGROOM_EXTRA "\xee\x98\xb3" // U+e633
#define ICON_MS_AIRLINE_SEAT_LEGROOM_NORMAL "\xee\x98\xb4" // U+e634
#define ICON_MS_AIRLINE_SEAT_LEGROOM_REDUCED "\xee\x98\xb5" // U+e635
#define ICON_MS_AIRLINE_SEAT_RECLINE_EXTRA "\xee\x98\xb6" // U+e636
#define ICON_MS_AIRLINE_SEAT_RECLINE_NORMAL "\xee\x98\xb7" // U+e637
#define ICON_MS_AIRLINE_STOPS "\xee\x9f\x90" // U+e7d0
#define ICON_MS_AIRLINES "\xee\x9f\x8a" // U+e7ca
#define ICON_MS_AIRPLANE_TICKET "\xee\xbf\x99" // U+efd9
#define ICON_MS_AIRPLANEMODE_ACTIVE "\xee\x94\xbd" // U+e53d
#define ICON_MS_AIRPLANEMODE_INACTIVE "\xee\x86\x94" // U+e194
#define ICON_MS_AIRPLAY "\xee\x81\x95" // U+e055
#define ICON_MS_AIRPORT_SHUTTLE "\xee\xac\xbc" // U+eb3c
#define ICON_MS_AIRWARE "\xef\x85\x94" // U+f154
#define ICON_MS_AIRWAVE "\xef\x85\x94" // U+f154
#define ICON_MS_ALARM "\xee\xa1\x95" // U+e855
#define ICON_MS_ALARM_ADD "\xee\xa1\x96" // U+e856
#define ICON_MS_ALARM_OFF "\xee\xa1\x97" // U+e857
#define ICON_MS_ALARM_ON "\xee\xa1\x98" // U+e858
#define ICON_MS_ALARM_SMART_WAKE "\xef\x9a\xb0" // U+f6b0
#define ICON_MS_ALBUM "\xee\x80\x99" // U+e019
#define ICON_MS_ALIGN_CENTER "\xee\x8d\x96" // U+e356
#define ICON_MS_ALIGN_END "\xef\x9e\x97" // U+f797
#define ICON_MS_ALIGN_FLEX_CENTER "\xef\x9e\x96" // U+f796
#define ICON_MS_ALIGN_FLEX_END "\xef\x9e\x95" // U+f795
#define ICON_MS_ALIGN_FLEX_START "\xef\x9e\x94" // U+f794
#define ICON_MS_ALIGN_HORIZONTAL_CENTER "\xee\x80\x8f" // U+e00f
#define ICON_MS_ALIGN_HORIZONTAL_LEFT "\xee\x80\x8d" // U+e00d
#define ICON_MS_ALIGN_HORIZONTAL_RIGHT "\xee\x80\x90" // U+e010
#define ICON_MS_ALIGN_ITEMS_STRETCH "\xef\x9e\x93" // U+f793
#define ICON_MS_ALIGN_JUSTIFY_CENTER "\xef\x9e\x92" // U+f792
#define ICON_MS_ALIGN_JUSTIFY_FLEX_END "\xef\x9e\x91" // U+f791
#define ICON_MS_ALIGN_JUSTIFY_FLEX_START "\xef\x9e\x90" // U+f790
#define ICON_MS_ALIGN_JUSTIFY_SPACE_AROUND "\xef\x9e\x8f" // U+f78f
#define ICON_MS_ALIGN_JUSTIFY_SPACE_BETWEEN "\xef\x9e\x8e" // U+f78e
#define ICON_MS_ALIGN_JUSTIFY_SPACE_EVEN "\xef\x9e\x8d" // U+f78d
#define ICON_MS_ALIGN_JUSTIFY_STRETCH "\xef\x9e\x8c" // U+f78c
#define ICON_MS_ALIGN_SELF_STRETCH "\xef\x9e\x8b" // U+f78b
#define ICON_MS_ALIGN_SPACE_AROUND "\xef\x9e\x8a" // U+f78a
#define ICON_MS_ALIGN_SPACE_BETWEEN "\xef\x9e\x89" // U+f789
#define ICON_MS_ALIGN_SPACE_EVEN "\xef\x9e\x88" // U+f788
#define ICON_MS_ALIGN_START "\xef\x9e\x87" // U+f787
#define ICON_MS_ALIGN_STRETCH "\xef\x9e\x86" // U+f786
#define ICON_MS_ALIGN_VERTICAL_BOTTOM "\xee\x80\x95" // U+e015
#define ICON_MS_ALIGN_VERTICAL_CENTER "\xee\x80\x91" // U+e011
#define ICON_MS_ALIGN_VERTICAL_TOP "\xee\x80\x8c" // U+e00c
#define ICON_MS_ALL_INBOX "\xee\xa5\xbf" // U+e97f
#define ICON_MS_ALL_INCLUSIVE "\xee\xac\xbd" // U+eb3d
#define ICON_MS_ALL_MATCH "\xee\x82\x93" // U+e093
#define ICON_MS_ALL_OUT "\xee\xa4\x8b" // U+e90b
#define ICON_MS_ALLERGIES "\xee\x82\x94" // U+e094
#define ICON_MS_ALLERGY "\xee\x99\x8e" // U+e64e
#define ICON_MS_ALT_ROUTE "\xef\x86\x84" // U+f184
#define ICON_MS_ALTERNATE_EMAIL "\xee\x83\xa6" // U+e0e6
#define ICON_MS_ALTITUDE "\xef\xa1\xb3" // U+f873
#define ICON_MS_AMBIENT_SCREEN "\xef\x9b\x84" // U+f6c4
#define ICON_MS_AMBULANCE "\xef\xa0\x83" // U+f803
#define ICON_MS_AMEND "\xef\xa0\x82" // U+f802
#define ICON_MS_AMP_STORIES "\xee\xa8\x93" // U+ea13
#define ICON_MS_ANALYTICS "\xee\xbc\xbe" // U+ef3e
#define ICON_MS_ANCHOR "\xef\x87\x8d" // U+f1cd
#define ICON_MS_ANDROID "\xee\xa1\x99" // U+e859
#define ICON_MS_ANIMATED_IMAGES "\xef\x92\x9a" // U+f49a
#define ICON_MS_ANIMATION "\xee\x9c\x9c" // U+e71c
#define ICON_MS_ANNOUNCEMENT "\xee\xa1\xbf" // U+e87f
#define ICON_MS_AOD "\xee\xbf\x9a" // U+efda
#define ICON_MS_AOD_TABLET "\xef\xa2\x9f" // U+f89f
#define ICON_MS_AOD_WATCH "\xef\x9a\xac" // U+f6ac
#define ICON_MS_APARTMENT "\xee\xa9\x80" // U+ea40
#define ICON_MS_API "\xef\x86\xb7" // U+f1b7
#define ICON_MS_APK_DOCUMENT "\xef\xa2\x8e" // U+f88e
#define ICON_MS_APK_INSTALL "\xef\xa2\x8f" // U+f88f
#define ICON_MS_APP_BADGING "\xef\x9c\xaf" // U+f72f
#define ICON_MS_APP_BLOCKING "\xee\xbc\xbf" // U+ef3f
#define ICON_MS_APP_PROMO "\xee\xa6\x81" // U+e981
#define ICON_MS_APP_REGISTRATION "\xee\xbd\x80" // U+ef40
#define ICON_MS_APP_SETTINGS_ALT "\xee\xbd\x81" // U+ef41
#define ICON_MS_APP_SHORTCUT "\xee\xab\xa4" // U+eae4
#define ICON_MS_APPAREL "\xee\xbd\xbb" // U+ef7b
#define ICON_MS_APPROVAL "\xee\xa6\x82" // U+e982
#define ICON_MS_APPROVAL_DELEGATION "\xef\xa1\x8a" // U+f84a
#define ICON_MS_APPS "\xee\x97\x83" // U+e5c3
#define ICON_MS_APPS_OUTAGE "\xee\x9f\x8c" // U+e7cc
#define ICON_MS_AQ "\xef\x95\x9a" // U+f55a
#define ICON_MS_AQ_INDOOR "\xef\x95\x9b" // U+f55b
#define ICON_MS_AR_ON_YOU "\xee\xbd\xbc" // U+ef7c
#define ICON_MS_AR_STICKERS "\xee\xa6\x83" // U+e983
#define ICON_MS_ARCHITECTURE "\xee\xa8\xbb" // U+ea3b
#define ICON_MS_ARCHIVE "\xee\x85\x89" // U+e149
#define ICON_MS_AREA_CHART "\xee\x9d\xb0" // U+e770
#define ICON_MS_ARMING_COUNTDOWN "\xee\x9e\x8a" // U+e78a
#define ICON_MS_ARROW_AND_EDGE "\xef\x97\x97" // U+f5d7
#define ICON_MS_ARROW_BACK "\xee\x97\x84" // U+e5c4
#define ICON_MS_ARROW_BACK_IOS "\xee\x97\xa0" // U+e5e0
#define ICON_MS_ARROW_BACK_IOS_NEW "\xee\x8b\xaa" // U+e2ea
#define ICON_MS_ARROW_CIRCLE_DOWN "\xef\x86\x81" // U+f181
#define ICON_MS_ARROW_CIRCLE_LEFT "\xee\xaa\xa7" // U+eaa7
#define ICON_MS_ARROW_CIRCLE_RIGHT "\xee\xaa\xaa" // U+eaaa
#define ICON_MS_ARROW_CIRCLE_UP "\xef\x86\x82" // U+f182
#define ICON_MS_ARROW_COOL_DOWN "\xef\x92\xb6" // U+f4b6
#define ICON_MS_ARROW_DOWNWARD "\xee\x97\x9b" // U+e5db
#define ICON_MS_ARROW_DOWNWARD_ALT "\xee\xa6\x84" // U+e984
#define ICON_MS_ARROW_DROP_DOWN "\xee\x97\x85" // U+e5c5
#define ICON_MS_ARROW_DROP_DOWN_CIRCLE "\xee\x97\x86" // U+e5c6
#define ICON_MS_ARROW_DROP_UP "\xee\x97\x87" // U+e5c7
#define ICON_MS_ARROW_FORWARD "\xee\x97\x88" // U+e5c8
#define ICON_MS_ARROW_FORWARD_IOS "\xee\x97\xa1" // U+e5e1
#define ICON_MS_ARROW_INSERT "\xef\xa0\xb7" // U+f837
#define ICON_MS_ARROW_LEFT "\xee\x97\x9e" // U+e5de
#define ICON_MS_ARROW_LEFT_ALT "\xee\xbd\xbd" // U+ef7d
#define ICON_MS_ARROW_OR_EDGE "\xef\x97\x96" // U+f5d6
#define ICON_MS_ARROW_OUTWARD "\xef\xa3\x8e" // U+f8ce
#define ICON_MS_ARROW_RANGE "\xef\x9a\x9b" // U+f69b
#define ICON_MS_ARROW_RIGHT "\xee\x97\x9f" // U+e5df
#define ICON_MS_ARROW_RIGHT_ALT "\xee\xa5\x81" // U+e941
#define ICON_MS_ARROW_SELECTOR_TOOL "\xef\xa0\xaf" // U+f82f
#define ICON_MS_ARROW_SPLIT "\xee\xa8\x84" // U+ea04
#define ICON_MS_ARROW_TOP_LEFT "\xef\x9c\xae" // U+f72e
#define ICON_MS_ARROW_TOP_RIGHT "\xef\x9c\xad" // U+f72d
#define ICON_MS_ARROW_UPWARD "\xee\x97\x98" // U+e5d8
#define ICON_MS_ARROW_UPWARD_ALT "\xee\xa6\x86" // U+e986
#define ICON_MS_ARROW_WARM_UP "\xef\x92\xb5" // U+f4b5
#define ICON_MS_ARROWS_MORE_DOWN "\xef\xa2\xab" // U+f8ab
#define ICON_MS_ARROWS_MORE_UP "\xef\xa2\xac" // U+f8ac
#define ICON_MS_ARROWS_OUTWARD "\xef\x9c\xac" // U+f72c
#define ICON_MS_ART_TRACK "\xee\x81\xa0" // U+e060
#define ICON_MS_ARTICLE "\xee\xbd\x82" // U+ef42
#define ICON_MS_ARTICLE_SHORTCUT "\xef\x96\x87" // U+f587
#define ICON_MS_ARTIST "\xee\x80\x9a" // U+e01a
#define ICON_MS_ASPECT_RATIO "\xee\xa1\x9b" // U+e85b
#define ICON_MS_ASSESSMENT "\xef\x83\x8c" // U+f0cc
#define ICON_MS_ASSIGNMENT "\xee\xa1\x9d" // U+e85d
#define ICON_MS_ASSIGNMENT_ADD "\xef\xa1\x88" // U+f848
#define ICON_MS_ASSIGNMENT_IND "\xee\xa1\x9e" // U+e85e
#define ICON_MS_ASSIGNMENT_LATE "\xee\xa1\x9f" // U+e85f
#define ICON_MS_ASSIGNMENT_RETURN "\xee\xa1\xa0" // U+e860
#define ICON_MS_ASSIGNMENT_RETURNED "\xee\xa1\xa1" // U+e861
#define ICON_MS_ASSIGNMENT_TURNED_IN "\xee\xa1\xa2" // U+e862
#define ICON_MS_ASSIST_WALKER "\xef\xa3\x95" // U+f8d5
#define ICON_MS_ASSISTANT "\xee\x8e\x9f" // U+e39f
#define ICON_MS_ASSISTANT_DEVICE "\xee\xa6\x87" // U+e987
#define ICON_MS_ASSISTANT_DIRECTION "\xee\xa6\x88" // U+e988
#define ICON_MS_ASSISTANT_NAVIGATION "\xee\xa6\x89" // U+e989
#define ICON_MS_ASSISTANT_ON_HUB "\xef\x9b\x81" // U+f6c1
#define ICON_MS_ASSISTANT_PHOTO "\xef\x83\x86" // U+f0c6
#define ICON_MS_ASSURED_WORKLOAD "\xee\xad\xaf" // U+eb6f
#define ICON_MS_ASTERISK "\xef\x94\xa5" // U+f525
#define ICON_MS_ASTROPHOTOGRAPHY_AUTO "\xef\x87\x99" // U+f1d9
#define ICON_MS_ASTROPHOTOGRAPHY_OFF "\xef\x87\x9a" // U+f1da
#define ICON_MS_ATM "\xee\x95\xb3" // U+e573
#define ICON_MS_ATR "\xee\xaf\x87" // U+ebc7
#define ICON_MS_ATTACH_EMAIL "\xee\xa9\x9e" // U+ea5e
#define ICON_MS_ATTACH_FILE "\xee\x88\xa6" // U+e226
#define ICON_MS_ATTACH_FILE_ADD "\xef\xa1\x81" // U+f841
#define ICON_MS_ATTACH_FILE_OFF "\xef\x93\x99" // U+f4d9
#define ICON_MS_ATTACH_MONEY "\xee\x88\xa7" // U+e227
#define ICON_MS_ATTACHMENT "\xee\x8a\xbc" // U+e2bc
#define ICON_MS_ATTRACTIONS "\xee\xa9\x92" // U+ea52
#define ICON_MS_ATTRIBUTION "\xee\xbf\x9b" // U+efdb
#define ICON_MS_AUDIO_DESCRIPTION "\xef\x96\x8c" // U+f58c
#define ICON_MS_AUDIO_FILE "\xee\xae\x82" // U+eb82
#define ICON_MS_AUDIO_VIDEO_RECEIVER "\xef\x97\x93" // U+f5d3
#define ICON_MS_AUDIOTRACK "\xee\x90\x85" // U+e405
#define ICON_MS_AUTO_ACTIVITY_ZONE "\xef\xa2\xad" // U+f8ad
#define ICON_MS_AUTO_AWESOME "\xee\x99\x9f" // U+e65f
#define ICON_MS_AUTO_AWESOME_MOSAIC "\xee\x99\xa0" // U+e660
#define ICON_MS_AUTO_AWESOME_MOTION "\xee\x99\xa1" // U+e661
#define ICON_MS_AUTO_DELETE "\xee\xa9\x8c" // U+ea4c
#define ICON_MS_AUTO_DETECT_VOICE "\xef\xa0\xbe" // U+f83e
#define ICON_MS_AUTO_DRAW_SOLID "\xee\xa6\x8a" // U+e98a
#define ICON_MS_AUTO_FIX "\xee\x99\xa3" // U+e663
#define ICON_MS_AUTO_FIX_HIGH "\xee\x99\xa3" // U+e663
#define ICON_MS_AUTO_FIX_NORMAL "\xee\x99\xa4" // U+e664
#define ICON_MS_AUTO_FIX_OFF "\xee\x99\xa5" // U+e665
#define ICON_MS_AUTO_GRAPH "\xee\x93\xbb" // U+e4fb
#define ICON_MS_AUTO_LABEL "\xef\x9a\xbe" // U+f6be
#define ICON_MS_AUTO_MEETING_ROOM "\xef\x9a\xbf" // U+f6bf
#define ICON_MS_AUTO_MODE "\xee\xb0\xa0" // U+ec20
#define ICON_MS_AUTO_READ_PAUSE "\xef\x88\x99" // U+f219
#define ICON_MS_AUTO_READ_PLAY "\xef\x88\x96" // U+f216
#define ICON_MS_AUTO_SCHEDULE "\xee\x88\x94" // U+e214
#define ICON_MS_AUTO_STORIES "\xee\x99\xa6" // U+e666
#define ICON_MS_AUTO_TIMER "\xee\xbd\xbf" // U+ef7f
#define ICON_MS_AUTO_TOWING "\xee\x9c\x9e" // U+e71e
#define ICON_MS_AUTO_TRANSMISSION "\xef\x94\xbf" // U+f53f
#define ICON_MS_AUTO_VIDEOCAM "\xef\x9b\x80" // U+f6c0
#define ICON_MS_AUTOFPS_SELECT "\xee\xbf\x9c" // U+efdc
#define ICON_MS_AUTOPAUSE "\xef\x9a\xb6" // U+f6b6
#define ICON_MS_AUTOPAY "\xef\xa1\x8b" // U+f84b
#define ICON_MS_AUTOPLAY "\xef\x9a\xb5" // U+f6b5
#define ICON_MS_AUTORENEW "\xee\xa1\xa3" // U+e863
#define ICON_MS_AUTOSTOP "\xef\x9a\x82" // U+f682
#define ICON_MS_AV1 "\xef\x92\xb0" // U+f4b0
#define ICON_MS_AV_TIMER "\xee\x80\x9b" // U+e01b
#define ICON_MS_AVC "\xef\x92\xaf" // U+f4af
#define ICON_MS_AVG_PACE "\xef\x9a\xbb" // U+f6bb
#define ICON_MS_AVG_TIME "\xef\xa0\x93" // U+f813
#define ICON_MS_AWARD_STAR "\xef\x98\x92" // U+f612
#define ICON_MS_AZM "\xef\x9b\xac" // U+f6ec
#define ICON_MS_BABY_CHANGING_STATION "\xef\x86\x9b" // U+f19b
#define ICON_MS_BACK_HAND "\xee\x9d\xa4" // U+e764
#define ICON_MS_BACK_TO_TAB "\xef\x9c\xab" // U+f72b
#define ICON_MS_BACKGROUND_DOT_LARGE "\xef\x9e\x9e" // U+f79e
#define ICON_MS_BACKGROUND_DOT_SMALL "\xef\x94\x94" // U+f514
#define ICON_MS_BACKGROUND_GRID_SMALL "\xef\x9e\x9d" // U+f79d
#define ICON_MS_BACKGROUND_REPLACE "\xef\x88\x8a" // U+f20a
#define ICON_MS_BACKLIGHT_HIGH "\xef\x9f\xad" // U+f7ed
#define ICON_MS_BACKLIGHT_HIGH_OFF "\xef\x93\xaf" // U+f4ef
#define ICON_MS_BACKLIGHT_LOW "\xef\x9f\xac" // U+f7ec
#define ICON_MS_BACKPACK "\xef\x86\x9c" // U+f19c
#define ICON_MS_BACKSPACE "\xee\x85\x8a" // U+e14a
#define ICON_MS_BACKUP "\xee\xa1\xa4" // U+e864
#define ICON_MS_BACKUP_TABLE "\xee\xbd\x83" // U+ef43
#define ICON_MS_BADGE "\xee\xa9\xa7" // U+ea67
#define ICON_MS_BADGE_CRITICAL_BATTERY "\xef\x85\x96" // U+f156
#define ICON_MS_BAKERY_DINING "\xee\xa9\x93" // U+ea53
#define ICON_MS_BALANCE "\xee\xab\xb6" // U+eaf6
#define ICON_MS_BALCONY "\xee\x96\x8f" // U+e58f
#define ICON_MS_BALLOT "\xee\x85\xb2" // U+e172
#define ICON_MS_BAR_CHART "\xee\x89\xab" // U+e26b
#define ICON_MS_BAR_CHART_4_BARS "\xef\x9a\x81" // U+f681
#define ICON_MS_BARCODE "\xee\x9c\x8b" // U+e70b
#define ICON_MS_BARCODE_READER "\xef\xa1\x9c" // U+f85c
#define ICON_MS_BARCODE_SCANNER "\xee\x9c\x8c" // U+e70c
#define ICON_MS_BAREFOOT "\xef\xa1\xb1" // U+f871
#define ICON_MS_BATCH_PREDICTION "\xef\x83\xb5" // U+f0f5
#define ICON_MS_BATH_OUTDOOR "\xef\x9b\xbb" // U+f6fb
#define ICON_MS_BATH_PRIVATE "\xef\x9b\xba" // U+f6fa
#define ICON_MS_BATH_PUBLIC_LARGE "\xef\x9b\xb9" // U+f6f9
#define ICON_MS_BATHROOM "\xee\xbf\x9d" // U+efdd
#define ICON_MS_BATHTUB "\xee\xa9\x81" // U+ea41
#define ICON_MS_BATTERY_0_BAR "\xee\xaf\x9c" // U+ebdc
#define ICON_MS_BATTERY_1_BAR "\xef\x82\x9c" // U+f09c
#define ICON_MS_BATTERY_20 "\xef\x82\x9c" // U+f09c
#define ICON_MS_BATTERY_2_BAR "\xef\x82\x9d" // U+f09d
#define ICON_MS_BATTERY_30 "\xef\x82\x9d" // U+f09d
#define ICON_MS_BATTERY_3_BAR "\xef\x82\x9e" // U+f09e
#define ICON_MS_BATTERY_4_BAR "\xef\x82\x9f" // U+f09f
#define ICON_MS_BATTERY_50 "\xef\x82\x9e" // U+f09e
#define ICON_MS_BATTERY_5_BAR "\xef\x82\xa0" // U+f0a0
#define ICON_MS_BATTERY_60 "\xef\x82\x9f" // U+f09f
#define ICON_MS_BATTERY_6_BAR "\xef\x82\xa1" // U+f0a1
#define ICON_MS_BATTERY_80 "\xef\x82\xa0" // U+f0a0
#define ICON_MS_BATTERY_90 "\xef\x82\xa1" // U+f0a1
#define ICON_MS_BATTERY_ALERT "\xee\x86\x9c" // U+e19c
#define ICON_MS_BATTERY_CHANGE "\xef\x9f\xab" // U+f7eb
#define ICON_MS_BATTERY_CHARGING_20 "\xef\x82\xa2" // U+f0a2
#define ICON_MS_BATTERY_CHARGING_30 "\xef\x82\xa3" // U+f0a3
#define ICON_MS_BATTERY_CHARGING_50 "\xef\x82\xa4" // U+f0a4
#define ICON_MS_BATTERY_CHARGING_60 "\xef\x82\xa5" // U+f0a5
#define ICON_MS_BATTERY_CHARGING_80 "\xef\x82\xa6" // U+f0a6
#define ICON_MS_BATTERY_CHARGING_90 "\xef\x82\xa7" // U+f0a7
#define ICON_MS_BATTERY_CHARGING_FULL "\xee\x86\xa3" // U+e1a3
#define ICON_MS_BATTERY_ERROR "\xef\x9f\xaa" // U+f7ea
#define ICON_MS_BATTERY_FULL "\xee\x86\xa5" // U+e1a5
#define ICON_MS_BATTERY_FULL_ALT "\xef\x84\xbb" // U+f13b
#define ICON_MS_BATTERY_HORIZ_000 "\xef\xa2\xae" // U+f8ae
#define ICON_MS_BATTERY_HORIZ_050 "\xef\xa2\xaf" // U+f8af
#define ICON_MS_BATTERY_HORIZ_075 "\xef\xa2\xb0" // U+f8b0
#define ICON_MS_BATTERY_LOW "\xef\x85\x95" // U+f155
#define ICON_MS_BATTERY_PLUS "\xef\x9f\xa9" // U+f7e9
#define ICON_MS_BATTERY_PROFILE "\xee\x88\x86" // U+e206
#define ICON_MS_BATTERY_SAVER "\xee\xbf\x9e" // U+efde
#define ICON_MS_BATTERY_SHARE "\xef\x99\xbe" // U+f67e
#define ICON_MS_BATTERY_STATUS_GOOD "\xef\x99\xbd" // U+f67d
#define ICON_MS_BATTERY_STD "\xee\x86\xa5" // U+e1a5
#define ICON_MS_BATTERY_UNKNOWN "\xee\x86\xa6" // U+e1a6
#define ICON_MS_BATTERY_VERT_005 "\xef\xa2\xb1" // U+f8b1
#define ICON_MS_BATTERY_VERT_020 "\xef\xa2\xb2" // U+f8b2
#define ICON_MS_BATTERY_VERT_050 "\xef\xa2\xb3" // U+f8b3
#define ICON_MS_BATTERY_VERY_LOW "\xef\x85\x96" // U+f156
#define ICON_MS_BEACH_ACCESS "\xee\xac\xbe" // U+eb3e
#define ICON_MS_BED "\xee\xbf\x9f" // U+efdf
#define ICON_MS_BEDROOM_BABY "\xee\xbf\xa0" // U+efe0
#define ICON_MS_BEDROOM_CHILD "\xee\xbf\xa1" // U+efe1
#define ICON_MS_BEDROOM_PARENT "\xee\xbf\xa2" // U+efe2
#define ICON_MS_BEDTIME "\xee\xbd\x84" // U+ef44
#define ICON_MS_BEDTIME_OFF "\xee\xad\xb6" // U+eb76
#define ICON_MS_BEENHERE "\xee\x94\xad" // U+e52d
#define ICON_MS_BENTO "\xef\x87\xb4" // U+f1f4
#define ICON_MS_BIA "\xef\x9b\xab" // U+f6eb
#define ICON_MS_BID_LANDSCAPE "\xee\x99\xb8" // U+e678
#define ICON_MS_BID_LANDSCAPE_DISABLED "\xee\xbe\x81" // U+ef81
#define ICON_MS_BIGTOP_UPDATES "\xee\x99\xa9" // U+e669
#define ICON_MS_BIKE_SCOOTER "\xee\xbd\x85" // U+ef45
#define ICON_MS_BIOTECH "\xee\xa8\xba" // U+ea3a
#define ICON_MS_BLANKET "\xee\xa0\xa8" // U+e828
#define ICON_MS_BLENDER "\xee\xbf\xa3" // U+efe3
#define ICON_MS_BLIND "\xef\xa3\x96" // U+f8d6
#define ICON_MS_BLINDS "\xee\x8a\x86" // U+e286
#define ICON_MS_BLINDS_CLOSED "\xee\xb0\x9f" // U+ec1f
#define ICON_MS_BLOCK "\xef\x82\x8c" // U+f08c
#define ICON_MS_BLOOD_PRESSURE "\xee\x82\x97" // U+e097
#define ICON_MS_BLOODTYPE "\xee\xbf\xa4" // U+efe4
#define ICON_MS_BLUETOOTH "\xee\x86\xa7" // U+e1a7
#define ICON_MS_BLUETOOTH_AUDIO "\xee\x98\x8f" // U+e60f
#define ICON_MS_BLUETOOTH_CONNECTED "\xee\x86\xa8" // U+e1a8
#define ICON_MS_BLUETOOTH_DISABLED "\xee\x86\xa9" // U+e1a9
#define ICON_MS_BLUETOOTH_DRIVE "\xee\xbf\xa5" // U+efe5
#define ICON_MS_BLUETOOTH_SEARCHING "\xee\x98\x8f" // U+e60f
#define ICON_MS_BLUR_CIRCULAR "\xee\x8e\xa2" // U+e3a2
#define ICON_MS_BLUR_LINEAR "\xee\x8e\xa3" // U+e3a3
#define ICON_MS_BLUR_MEDIUM "\xee\xa1\x8c" // U+e84c
#define ICON_MS_BLUR_OFF "\xee\x8e\xa4" // U+e3a4
#define ICON_MS_BLUR_ON "\xee\x8e\xa5" // U+e3a5
#define ICON_MS_BLUR_SHORT "\xee\xa3\x8f" // U+e8cf
#define ICON_MS_BODY_FAT "\xee\x82\x98" // U+e098
#define ICON_MS_BODY_SYSTEM "\xee\x82\x99" // U+e099
#define ICON_MS_BOLT "\xee\xa8\x8b" // U+ea0b
#define ICON_MS_BOMB "\xef\x95\xa8" // U+f568
#define ICON_MS_BOOK "\xee\xa1\xae" // U+e86e
#define ICON_MS_BOOK_2 "\xef\x94\xbe" // U+f53e
#define ICON_MS_BOOK_3 "\xef\x94\xbd" // U+f53d
#define ICON_MS_BOOK_4 "\xef\x94\xbc" // U+f53c
#define ICON_MS_BOOK_5 "\xef\x94\xbb" // U+f53b
#define ICON_MS_BOOK_ONLINE "\xef\x88\x97" // U+f217
#define ICON_MS_BOOKMARK "\xee\xa3\xa7" // U+e8e7
#define ICON_MS_BOOKMARK_ADD "\xee\x96\x98" // U+e598
#define ICON_MS_BOOKMARK_ADDED "\xee\x96\x99" // U+e599
#define ICON_MS_BOOKMARK_BORDER "\xee\xa3\xa7" // U+e8e7
#define ICON_MS_BOOKMARK_MANAGER "\xef\x9e\xb1" // U+f7b1
#define ICON_MS_BOOKMARK_REMOVE "\xee\x96\x9a" // U+e59a
#define ICON_MS_BOOKMARKS "\xee\xa6\x8b" // U+e98b
#define ICON_MS_BORDER_ALL "\xee\x88\xa8" // U+e228
#define ICON_MS_BORDER_BOTTOM "\xee\x88\xa9" // U+e229
#define ICON_MS_BORDER_CLEAR "\xee\x88\xaa" // U+e22a
#define ICON_MS_BORDER_COLOR "\xee\x88\xab" // U+e22b
#define ICON_MS_BORDER_HORIZONTAL "\xee\x88\xac" // U+e22c
#define ICON_MS_BORDER_INNER "\xee\x88\xad" // U+e22d
#define ICON_MS_BORDER_LEFT "\xee\x88\xae" // U+e22e
#define ICON_MS_BORDER_OUTER "\xee\x88\xaf" // U+e22f
#define ICON_MS_BORDER_RIGHT "\xee\x88\xb0" // U+e230
#define ICON_MS_BORDER_STYLE "\xee\x88\xb1" // U+e231
#define ICON_MS_BORDER_TOP "\xee\x88\xb2" // U+e232
#define ICON_MS_BORDER_VERTICAL "\xee\x88\xb3" // U+e233
#define ICON_MS_BOTTOM_APP_BAR "\xee\x9c\xb0" // U+e730
#define ICON_MS_BOTTOM_DRAWER "\xee\x9c\xad" // U+e72d
#define ICON_MS_BOTTOM_NAVIGATION "\xee\xa6\x8c" // U+e98c
#define ICON_MS_BOTTOM_PANEL_CLOSE "\xef\x9c\xaa" // U+f72a
#define ICON_MS_BOTTOM_PANEL_OPEN "\xef\x9c\xa9" // U+f729
#define ICON_MS_BOTTOM_RIGHT_CLICK "\xef\x9a\x84" // U+f684
#define ICON_MS_BOTTOM_SHEETS "\xee\xa6\x8d" // U+e98d
#define ICON_MS_BOX "\xef\x96\xa4" // U+f5a4
#define ICON_MS_BOX_ADD "\xef\x96\xa5" // U+f5a5
#define ICON_MS_BOX_EDIT "\xef\x96\xa6" // U+f5a6
#define ICON_MS_BOY "\xee\xad\xa7" // U+eb67
#define ICON_MS_BRAND_AWARENESS "\xee\xa6\x8e" // U+e98e
#define ICON_MS_BRAND_FAMILY "\xef\x93\xb1" // U+f4f1
#define ICON_MS_BRANDING_WATERMARK "\xee\x81\xab" // U+e06b
#define ICON_MS_BREAKFAST_DINING "\xee\xa9\x94" // U+ea54
#define ICON_MS_BREAKING_NEWS "\xee\xa8\x88" // U+ea08
#define ICON_MS_BREAKING_NEWS_ALT_1 "\xef\x82\xba" // U+f0ba
#define ICON_MS_BREASTFEEDING "\xef\xa1\x96" // U+f856
#define ICON_MS_BRIGHTNESS_1 "\xee\x8f\xba" // U+e3fa
#define ICON_MS_BRIGHTNESS_2 "\xef\x80\xb6" // U+f036
#define ICON_MS_BRIGHTNESS_3 "\xee\x8e\xa8" // U+e3a8
#define ICON_MS_BRIGHTNESS_4 "\xee\x8e\xa9" // U+e3a9
#define ICON_MS_BRIGHTNESS_5 "\xee\x8e\xaa" // U+e3aa
#define ICON_MS_BRIGHTNESS_6 "\xee\x8e\xab" // U+e3ab
#define ICON_MS_BRIGHTNESS_7 "\xee\x8e\xac" // U+e3ac
#define ICON_MS_BRIGHTNESS_ALERT "\xef\x97\x8f" // U+f5cf
#define ICON_MS_BRIGHTNESS_AUTO "\xee\x86\xab" // U+e1ab
#define ICON_MS_BRIGHTNESS_EMPTY "\xef\x9f\xa8" // U+f7e8
#define ICON_MS_BRIGHTNESS_HIGH "\xee\x86\xac" // U+e1ac
#define ICON_MS_BRIGHTNESS_LOW "\xee\x86\xad" // U+e1ad
#define ICON_MS_BRIGHTNESS_MEDIUM "\xee\x86\xae" // U+e1ae
#define ICON_MS_BRING_YOUR_OWN_IP "\xee\x80\x96" // U+e016
#define ICON_MS_BROADCAST_ON_HOME "\xef\xa3\xb8" // U+f8f8
#define ICON_MS_BROADCAST_ON_PERSONAL "\xef\xa3\xb9" // U+f8f9
#define ICON_MS_BROKEN_IMAGE "\xee\x8e\xad" // U+e3ad
#define ICON_MS_BROWSE "\xee\xac\x93" // U+eb13
#define ICON_MS_BROWSE_ACTIVITY "\xef\xa2\xa5" // U+f8a5
#define ICON_MS_BROWSE_GALLERY "\xee\xaf\x91" // U+ebd1
#define ICON_MS_BROWSER_NOT_SUPPORTED "\xee\xbd\x87" // U+ef47
#define ICON_MS_BROWSER_UPDATED "\xee\x9f\x8f" // U+e7cf
#define ICON_MS_BRUNCH_DINING "\xee\xa9\xb3" // U+ea73
#define ICON_MS_BRUSH "\xee\x8e\xae" // U+e3ae
#define ICON_MS_BUBBLE "\xee\xbe\x83" // U+ef83
#define ICON_MS_BUBBLE_CHART "\xee\x9b\x9d" // U+e6dd
#define ICON_MS_BUBBLES "\xef\x99\x8e" // U+f64e
#define ICON_MS_BUG_REPORT "\xee\xa1\xa8" // U+e868
#define ICON_MS_BUILD "\xef\xa3\x8d" // U+f8cd
#define ICON_MS_BUILD_CIRCLE "\xee\xbd\x88" // U+ef48
#define ICON_MS_BUNGALOW "\xee\x96\x91" // U+e591
#define ICON_MS_BURST_MODE "\xee\x90\xbc" // U+e43c
#define ICON_MS_BUS_ALERT "\xee\xa6\x8f" // U+e98f
#define ICON_MS_BUSINESS "\xee\x9f\xae" // U+e7ee
#define ICON_MS_BUSINESS_CENTER "\xee\xac\xbf" // U+eb3f
#define ICON_MS_BUSINESS_CHIP "\xef\xa1\x8c" // U+f84c
#define ICON_MS_BUSINESS_MESSAGES "\xee\xbe\x84" // U+ef84
#define ICON_MS_BUTTONS_ALT "\xee\x9c\xaf" // U+e72f
#define ICON_MS_CABIN "\xee\x96\x89" // U+e589
#define ICON_MS_CABLE "\xee\xbf\xa6" // U+efe6
#define ICON_MS_CACHED "\xee\xa1\xaa" // U+e86a
#define ICON_MS_CADENCE "\xef\x92\xb4" // U+f4b4
#define ICON_MS_CAKE "\xee\x9f\xa9" // U+e7e9
#define ICON_MS_CAKE_ADD "\xef\xa1\x9b" // U+f85b
#define ICON_MS_CALCULATE "\xee\xa9\x9f" // U+ea5f
#define ICON_MS_CALENDAR_ADD_ON "\xee\xbe\x85" // U+ef85
#define ICON_MS_CALENDAR_APPS_SCRIPT "\xef\x82\xbb" // U+f0bb
#define ICON_MS_CALENDAR_CLOCK "\xef\x95\x80" // U+f540
#define ICON_MS_CALENDAR_MONTH "\xee\xaf\x8c" // U+ebcc
#define ICON_MS_CALENDAR_TODAY "\xee\xa4\xb5" // U+e935
#define ICON_MS_CALENDAR_VIEW_DAY "\xee\xa4\xb6" // U+e936
#define ICON_MS_CALENDAR_VIEW_MONTH "\xee\xbf\xa7" // U+efe7
#define ICON_MS_CALENDAR_VIEW_WEEK "\xee\xbf\xa8" // U+efe8
#define ICON_MS_CALL "\xef\x83\x94" // U+f0d4
#define ICON_MS_CALL_END "\xef\x82\xbc" // U+f0bc
#define ICON_MS_CALL_END_ALT "\xef\x82\xbc" // U+f0bc
#define ICON_MS_CALL_LOG "\xee\x82\x8e" // U+e08e
#define ICON_MS_CALL_MADE "\xee\x82\xb2" // U+e0b2
#define ICON_MS_CALL_MERGE "\xee\x82\xb3" // U+e0b3
#define ICON_MS_CALL_MISSED "\xee\x82\xb4" // U+e0b4
#define ICON_MS_CALL_MISSED_OUTGOING "\xee\x83\xa4" // U+e0e4
#define ICON_MS_CALL_QUALITY "\xef\x99\x92" // U+f652
#define ICON_MS_CALL_RECEIVED "\xee\x82\xb5" // U+e0b5
#define ICON_MS_CALL_SPLIT "\xee\x82\xb6" // U+e0b6
#define ICON_MS_CALL_TO_ACTION "\xee\x81\xac" // U+e06c
#define ICON_MS_CAMERA "\xee\x8e\xaf" // U+e3af
#define ICON_MS_CAMERA_ALT "\xee\x90\x92" // U+e412
#define ICON_MS_CAMERA_ENHANCE "\xee\xa3\xbc" // U+e8fc
#define ICON_MS_CAMERA_FRONT "\xee\x8e\xb1" // U+e3b1
#define ICON_MS_CAMERA_INDOOR "\xee\xbf\xa9" // U+efe9
#define ICON_MS_CAMERA_OUTDOOR "\xee\xbf\xaa" // U+efea
#define ICON_MS_CAMERA_REAR "\xee\x8e\xb2" // U+e3b2
#define ICON_MS_CAMERA_ROLL "\xee\x8e\xb3" // U+e3b3
#define ICON_MS_CAMERA_VIDEO "\xef\x9e\xa6" // U+f7a6
#define ICON_MS_CAMERASWITCH "\xee\xbf\xab" // U+efeb
#define ICON_MS_CAMPAIGN "\xee\xbd\x89" // U+ef49
#define ICON_MS_CAMPING "\xef\xa2\xa2" // U+f8a2
#define ICON_MS_CANCEL "\xee\xa2\x88" // U+e888
#define ICON_MS_CANCEL_PRESENTATION "\xee\x83\xa9" // U+e0e9
#define ICON_MS_CANCEL_SCHEDULE_SEND "\xee\xa8\xb9" // U+ea39
#define ICON_MS_CANDLE "\xef\x96\x88" // U+f588
#define ICON_MS_CANDLESTICK_CHART "\xee\xab\x94" // U+ead4
#define ICON_MS_CAPTIVE_PORTAL "\xef\x9c\xa8" // U+f728
#define ICON_MS_CAPTURE "\xef\x9c\xa7" // U+f727
#define ICON_MS_CAR_CRASH "\xee\xaf\xb2" // U+ebf2
#define ICON_MS_CAR_RENTAL "\xee\xa9\x95" // U+ea55
#define ICON_MS_CAR_REPAIR "\xee\xa9\x96" // U+ea56
#define ICON_MS_CAR_TAG "\xef\x93\xa3" // U+f4e3
#define ICON_MS_CARD_GIFTCARD "\xee\xa3\xb6" // U+e8f6
#define ICON_MS_CARD_MEMBERSHIP "\xee\xa3\xb7" // U+e8f7
#define ICON_MS_CARD_TRAVEL "\xee\xa3\xb8" // U+e8f8
#define ICON_MS_CARDIO_LOAD "\xef\x92\xb9" // U+f4b9
#define ICON_MS_CARDIOLOGY "\xee\x82\x9c" // U+e09c
#define ICON_MS_CARDS "\xee\xa6\x91" // U+e991
#define ICON_MS_CARPENTER "\xef\x87\xb8" // U+f1f8
#define ICON_MS_CARRY_ON_BAG "\xee\xac\x88" // U+eb08
#define ICON_MS_CARRY_ON_BAG_CHECKED "\xee\xac\x8b" // U+eb0b
#define ICON_MS_CARRY_ON_BAG_INACTIVE "\xee\xac\x8a" // U+eb0a
#define ICON_MS_CARRY_ON_BAG_QUESTION "\xee\xac\x89" // U+eb09
#define ICON_MS_CASES "\xee\xa6\x92" // U+e992
#define ICON_MS_CASINO "\xee\xad\x80" // U+eb40
#define ICON_MS_CAST "\xee\x8c\x87" // U+e307
#define ICON_MS_CAST_CONNECTED "\xee\x8c\x88" // U+e308
#define ICON_MS_CAST_FOR_EDUCATION "\xee\xbf\xac" // U+efec
#define ICON_MS_CAST_PAUSE "\xef\x97\xb0" // U+f5f0
#define ICON_MS_CAST_WARNING "\xef\x97\xaf" // U+f5ef
#define ICON_MS_CASTLE "\xee\xaa\xb1" // U+eab1
#define ICON_MS_CATEGORY "\xee\x95\xb4" // U+e574
#define ICON_MS_CELEBRATION "\xee\xa9\xa5" // U+ea65
#define ICON_MS_CELL_MERGE "\xef\xa0\xae" // U+f82e
#define ICON_MS_CELL_TOWER "\xee\xae\xba" // U+ebba
#define ICON_MS_CELL_WIFI "\xee\x83\xac" // U+e0ec
#define ICON_MS_CENTER_FOCUS_STRONG "\xee\x8e\xb4" // U+e3b4
#define ICON_MS_CENTER_FOCUS_WEAK "\xee\x8e\xb5" // U+e3b5
#define ICON_MS_CHAIR "\xee\xbf\xad" // U+efed
#define ICON_MS_CHAIR_ALT "\xee\xbf\xae" // U+efee
#define ICON_MS_CHALET "\xee\x96\x85" // U+e585
#define ICON_MS_CHANGE_CIRCLE "\xee\x8b\xa7" // U+e2e7
#define ICON_MS_CHANGE_HISTORY "\xee\xa1\xab" // U+e86b
#define ICON_MS_CHARGER "\xee\x8a\xae" // U+e2ae
#define ICON_MS_CHARGING_STATION "\xef\x86\x9d" // U+f19d
#define ICON_MS_CHART_DATA "\xee\x91\xb3" // U+e473
#define ICON_MS_CHAT "\xee\x83\x89" // U+e0c9
#define ICON_MS_CHAT_ADD_ON "\xef\x83\xb3" // U+f0f3
#define ICON_MS_CHAT_APPS_SCRIPT "\xef\x82\xbd" // U+f0bd
#define ICON_MS_CHAT_BUBBLE "\xee\x83\x8b" // U+e0cb
#define ICON_MS_CHAT_BUBBLE_OUTLINE "\xee\x83\x8b" // U+e0cb
#define ICON_MS_CHAT_ERROR "\xef\x9e\xac" // U+f7ac
#define ICON_MS_CHAT_INFO "\xef\x94\xab" // U+f52b
#define ICON_MS_CHAT_PASTE_GO "\xef\x9a\xbd" // U+f6bd
#define ICON_MS_CHECK "\xee\x97\x8a" // U+e5ca
#define ICON_MS_CHECK_BOX "\xee\xa0\xb4" // U+e834
#define ICON_MS_CHECK_BOX_OUTLINE_BLANK "\xee\xa0\xb5" // U+e835
#define ICON_MS_CHECK_CIRCLE "\xef\x82\xbe" // U+f0be
#define ICON_MS_CHECK_CIRCLE_FILLED "\xef\x82\xbe" // U+f0be
#define ICON_MS_CHECK_CIRCLE_OUTLINE "\xef\x82\xbe" // U+f0be
#define ICON_MS_CHECK_IN_OUT "\xef\x9b\xb6" // U+f6f6
#define ICON_MS_CHECK_INDETERMINATE_SMALL "\xef\xa2\x8a" // U+f88a
#define ICON_MS_CHECK_SMALL "\xef\xa2\x8b" // U+f88b
#define ICON_MS_CHECKBOOK "\xee\x9c\x8d" // U+e70d
#define ICON_MS_CHECKED_BAG "\xee\xac\x8c" // U+eb0c
#define ICON_MS_CHECKED_BAG_QUESTION "\xee\xac\x8d" // U+eb0d
#define ICON_MS_CHECKLIST "\xee\x9a\xb1" // U+e6b1
#define ICON_MS_CHECKLIST_RTL "\xee\x9a\xb3" // U+e6b3
#define ICON_MS_CHECKROOM "\xef\x86\x9e" // U+f19e
#define ICON_MS_CHEER "\xef\x9a\xa8" // U+f6a8
#define ICON_MS_CHESS "\xef\x97\xa7" // U+f5e7
#define ICON_MS_CHEVRON_LEFT "\xee\x97\x8b" // U+e5cb
#define ICON_MS_CHEVRON_RIGHT "\xee\x97\x8c" // U+e5cc
#define ICON_MS_CHILD_CARE "\xee\xad\x81" // U+eb41
#define ICON_MS_CHILD_FRIENDLY "\xee\xad\x82" // U+eb42
#define ICON_MS_CHIP_EXTRACTION "\xef\xa0\xa1" // U+f821
#define ICON_MS_CHIPS "\xee\xa6\x93" // U+e993
#define ICON_MS_CHROME_READER_MODE "\xee\xa1\xad" // U+e86d
#define ICON_MS_CHROMECAST_2 "\xef\x85\xbb" // U+f17b
#define ICON_MS_CHROMECAST_DEVICE "\xee\xa0\xbc" // U+e83c
#define ICON_MS_CHRONIC "\xee\xae\xb2" // U+ebb2
#define ICON_MS_CHURCH "\xee\xaa\xae" // U+eaae
#define ICON_MS_CINEMATIC_BLUR "\xef\xa1\x93" // U+f853
#define ICON_MS_CIRCLE "\xee\xbd\x8a" // U+ef4a
#define ICON_MS_CIRCLE_NOTIFICATIONS "\xee\xa6\x94" // U+e994
#define ICON_MS_CIRCLES "\xee\x9f\xaa" // U+e7ea
#define ICON_MS_CIRCLES_EXT "\xee\x9f\xac" // U+e7ec
#define ICON_MS_CLARIFY "\xef\x82\xbf" // U+f0bf
#define ICON_MS_CLASS "\xee\xa1\xae" // U+e86e
#define ICON_MS_CLEAN_HANDS "\xef\x88\x9f" // U+f21f
#define ICON_MS_CLEANING "\xee\xa6\x95" // U+e995
#define ICON_MS_CLEANING_BUCKET "\xef\xa2\xb4" // U+f8b4
#define ICON_MS_CLEANING_SERVICES "\xef\x83\xbf" // U+f0ff
#define ICON_MS_CLEAR "\xee\x97\x8d" // U+e5cd
#define ICON_MS_CLEAR_ALL "\xee\x82\xb8" // U+e0b8
#define ICON_MS_CLEAR_DAY "\xef\x85\x97" // U+f157
#define ICON_MS_CLEAR_NIGHT "\xef\x85\x99" // U+f159
#define ICON_MS_CLIMATE_MINI_SPLIT "\xef\xa2\xb5" // U+f8b5
#define ICON_MS_CLINICAL_NOTES "\xee\x82\x9e" // U+e09e
#define ICON_MS_CLOCK_LOADER_10 "\xef\x9c\xa6" // U+f726
#define ICON_MS_CLOCK_LOADER_20 "\xef\x9c\xa5" // U+f725
#define ICON_MS_CLOCK_LOADER_40 "\xef\x9c\xa4" // U+f724
#define ICON_MS_CLOCK_LOADER_60 "\xef\x9c\xa3" // U+f723
#define ICON_MS_CLOCK_LOADER_80 "\xef\x9c\xa2" // U+f722
#define ICON_MS_CLOCK_LOADER_90 "\xef\x9c\xa1" // U+f721
#define ICON_MS_CLOSE "\xee\x97\x8d" // U+e5cd
#define ICON_MS_CLOSE_FULLSCREEN "\xef\x87\x8f" // U+f1cf
#define ICON_MS_CLOSE_SMALL "\xef\x94\x88" // U+f508
#define ICON_MS_CLOSED_CAPTION "\xee\xa6\x96" // U+e996
#define ICON_MS_CLOSED_CAPTION_ADD "\xef\x92\xae" // U+f4ae
#define ICON_MS_CLOSED_CAPTION_DISABLED "\xef\x87\x9c" // U+f1dc
#define ICON_MS_CLOSED_CAPTION_OFF "\xee\xa6\x96" // U+e996
#define ICON_MS_CLOUD "\xef\x85\x9c" // U+f15c
#define ICON_MS_CLOUD_CIRCLE "\xee\x8a\xbe" // U+e2be
#define ICON_MS_CLOUD_DONE "\xee\x8a\xbf" // U+e2bf
#define ICON_MS_CLOUD_DOWNLOAD "\xee\x8b\x80" // U+e2c0
#define ICON_MS_CLOUD_OFF "\xee\x8b\x81" // U+e2c1
#define ICON_MS_CLOUD_QUEUE "\xef\x85\x9c" // U+f15c
#define ICON_MS_CLOUD_SYNC "\xee\xad\x9a" // U+eb5a
#define ICON_MS_CLOUD_UPLOAD "\xee\x8b\x83" // U+e2c3
#define ICON_MS_CLOUDY "\xef\x85\x9c" // U+f15c
#define ICON_MS_CLOUDY_FILLED "\xef\x85\x9c" // U+f15c
#define ICON_MS_CLOUDY_SNOWING "\xee\xa0\x90" // U+e810
#define ICON_MS_CO2 "\xee\x9e\xb0" // U+e7b0
#define ICON_MS_CO_PRESENT "\xee\xab\xb0" // U+eaf0
#define ICON_MS_CODE "\xee\xa1\xaf" // U+e86f
#define ICON_MS_CODE_BLOCKS "\xef\xa1\x8d" // U+f84d
#define ICON_MS_CODE_OFF "\xee\x93\xb3" // U+e4f3
#define ICON_MS_COFFEE "\xee\xbf\xaf" // U+efef
#define ICON_MS_COFFEE_MAKER "\xee\xbf\xb0" // U+eff0
#define ICON_MS_COGNITION "\xee\x82\x9f" // U+e09f
#define ICON_MS_COLLAPSE_ALL "\xee\xa5\x84" // U+e944
#define ICON_MS_COLLAPSE_CONTENT "\xef\x94\x87" // U+f507
#define ICON_MS_COLLECTIONS "\xee\x8f\x93" // U+e3d3
#define ICON_MS_COLLECTIONS_BOOKMARK "\xee\x90\xb1" // U+e431
#define ICON_MS_COLOR_LENS "\xee\x90\x8a" // U+e40a
#define ICON_MS_COLORIZE "\xee\x8e\xb8" // U+e3b8
#define ICON_MS_COLORS "\xee\xa6\x97" // U+e997
#define ICON_MS_COMEDY_MASK "\xef\x93\x96" // U+f4d6
#define ICON_MS_COMIC_BUBBLE "\xef\x97\x9d" // U+f5dd
#define ICON_MS_COMMENT "\xee\x89\x8c" // U+e24c
#define ICON_MS_COMMENT_BANK "\xee\xa9\x8e" // U+ea4e
#define ICON_MS_COMMENTS_DISABLED "\xee\x9e\xa2" // U+e7a2
#define ICON_MS_COMMIT "\xee\xab\xb5" // U+eaf5
#define ICON_MS_COMMUNICATION "\xee\x89\xbc" // U+e27c
#define ICON_MS_COMMUNITIES "\xee\xac\x96" // U+eb16
#define ICON_MS_COMMUNITIES_FILLED "\xee\xac\x96" // U+eb16
#define ICON_MS_COMMUTE "\xee\xa5\x80" // U+e940
#define ICON_MS_COMPARE "\xee\x8e\xb9" // U+e3b9
#define ICON_MS_COMPARE_ARROWS "\xee\xa4\x95" // U+e915
#define ICON_MS_COMPASS_CALIBRATION "\xee\x95\xbc" // U+e57c
#define ICON_MS_COMPONENT_EXCHANGE "\xef\x87\xa7" // U+f1e7
#define ICON_MS_COMPOST "\xee\x9d\xa1" // U+e761
#define ICON_MS_COMPRESS "\xee\xa5\x8d" // U+e94d
#define ICON_MS_COMPUTER "\xee\x8c\x9e" // U+e31e
#define ICON_MS_CONCIERGE "\xef\x95\xa1" // U+f561
#define ICON_MS_CONDITIONS "\xee\x82\xa0" // U+e0a0
#define ICON_MS_CONFIRMATION_NUMBER "\xee\x98\xb8" // U+e638
#define ICON_MS_CONGENITAL "\xee\x82\xa1" // U+e0a1
#define ICON_MS_CONNECT_WITHOUT_CONTACT "\xef\x88\xa3" // U+f223
#define ICON_MS_CONNECTED_TV "\xee\xa6\x98" // U+e998
#define ICON_MS_CONNECTING_AIRPORTS "\xee\x9f\x89" // U+e7c9
#define ICON_MS_CONSTRUCTION "\xee\xa8\xbc" // U+ea3c
#define ICON_MS_CONTACT_EMERGENCY "\xef\xa3\x91" // U+f8d1
#define ICON_MS_CONTACT_MAIL "\xee\x83\x90" // U+e0d0
#define ICON_MS_CONTACT_PAGE "\xef\x88\xae" // U+f22e
#define ICON_MS_CONTACT_PHONE "\xef\x83\x80" // U+f0c0
#define ICON_MS_CONTACT_PHONE_FILLED "\xef\x83\x80" // U+f0c0
#define ICON_MS_CONTACT_SUPPORT "\xee\xa5\x8c" // U+e94c
#define ICON_MS_CONTACTLESS "\xee\xa9\xb1" // U+ea71
#define ICON_MS_CONTACTLESS_OFF "\xef\xa1\x98" // U+f858
#define ICON_MS_CONTACTS "\xee\x82\xba" // U+e0ba
#define ICON_MS_CONTACTS_PRODUCT "\xee\xa6\x99" // U+e999
#define ICON_MS_CONTENT_COPY "\xee\x85\x8d" // U+e14d
#define ICON_MS_CONTENT_CUT "\xee\x85\x8e" // U+e14e
#define ICON_MS_CONTENT_PASTE "\xee\x85\x8f" // U+e14f
#define ICON_MS_CONTENT_PASTE_GO "\xee\xaa\x8e" // U+ea8e
#define ICON_MS_CONTENT_PASTE_OFF "\xee\x93\xb8" // U+e4f8
#define ICON_MS_CONTENT_PASTE_SEARCH "\xee\xaa\x9b" // U+ea9b
#define ICON_MS_CONTRACT "\xef\x96\xa0" // U+f5a0
#define ICON_MS_CONTRACT_DELETE "\xef\x96\xa2" // U+f5a2
#define ICON_MS_CONTRACT_EDIT "\xef\x96\xa1" // U+f5a1
#define ICON_MS_CONTRAST "\xee\xac\xb7" // U+eb37
#define ICON_MS_CONTRAST_CIRCLE "\xef\x92\x9f" // U+f49f
#define ICON_MS_CONTRAST_RTL_OFF "\xee\xb1\xb2" // U+ec72
#define ICON_MS_CONTRAST_SQUARE "\xef\x92\xa0" // U+f4a0
#define ICON_MS_CONTROL_CAMERA "\xee\x81\xb4" // U+e074
#define ICON_MS_CONTROL_POINT "\xee\x8e\xba" // U+e3ba
#define ICON_MS_CONTROL_POINT_DUPLICATE "\xee\x8e\xbb" // U+e3bb
#define ICON_MS_CONTROLLER_GEN "\xee\xa0\xbd" // U+e83d
#define ICON_MS_CONVERSION_PATH "\xef\x83\x81" // U+f0c1
#define ICON_MS_CONVERSION_PATH_OFF "\xef\x9e\xb4" // U+f7b4
#define ICON_MS_CONVEYOR_BELT "\xef\xa1\xa7" // U+f867
#define ICON_MS_COOKIE "\xee\xaa\xac" // U+eaac
#define ICON_MS_COOKIE_OFF "\xef\x9e\x9a" // U+f79a
#define ICON_MS_COOKING "\xee\x8a\xb6" // U+e2b6
#define ICON_MS_COOL_TO_DRY "\xee\x89\xb6" // U+e276
#define ICON_MS_COPY_ALL "\xee\x8b\xac" // U+e2ec
#define ICON_MS_COPYRIGHT "\xee\xa4\x8c" // U+e90c
#define ICON_MS_CORONAVIRUS "\xef\x88\xa1" // U+f221
#define ICON_MS_CORPORATE_FARE "\xef\x87\x90" // U+f1d0
#define ICON_MS_COTTAGE "\xee\x96\x87" // U+e587
#define ICON_MS_COUNTER_0 "\xef\x9e\x85" // U+f785
#define ICON_MS_COUNTER_1 "\xef\x9e\x84" // U+f784
#define ICON_MS_COUNTER_2 "\xef\x9e\x83" // U+f783
#define ICON_MS_COUNTER_3 "\xef\x9e\x82" // U+f782
#define ICON_MS_COUNTER_4 "\xef\x9e\x81" // U+f781
#define ICON_MS_COUNTER_5 "\xef\x9e\x80" // U+f780
#define ICON_MS_COUNTER_6 "\xef\x9d\xbf" // U+f77f
#define ICON_MS_COUNTER_7 "\xef\x9d\xbe" // U+f77e
#define ICON_MS_COUNTER_8 "\xef\x9d\xbd" // U+f77d
#define ICON_MS_COUNTER_9 "\xef\x9d\xbc" // U+f77c
#define ICON_MS_COUNTERTOPS "\xef\x87\xb7" // U+f1f7
#define ICON_MS_CREATE "\xef\x82\x97" // U+f097
#define ICON_MS_CREATE_NEW_FOLDER "\xee\x8b\x8c" // U+e2cc
#define ICON_MS_CREDIT_CARD "\xee\xa2\xa1" // U+e8a1
#define ICON_MS_CREDIT_CARD_GEAR "\xef\x94\xad" // U+f52d
#define ICON_MS_CREDIT_CARD_HEART "\xef\x94\xac" // U+f52c
#define ICON_MS_CREDIT_CARD_OFF "\xee\x93\xb4" // U+e4f4
#define ICON_MS_CREDIT_SCORE "\xee\xbf\xb1" // U+eff1
#define ICON_MS_CRIB "\xee\x96\x88" // U+e588
#define ICON_MS_CRISIS_ALERT "\xee\xaf\xa9" // U+ebe9
#define ICON_MS_CROP "\xee\x8e\xbe" // U+e3be
#define ICON_MS_CROP_16_9 "\xee\x8e\xbc" // U+e3bc
#define ICON_MS_CROP_3_2 "\xee\x8e\xbd" // U+e3bd
#define ICON_MS_CROP_5_4 "\xee\x8e\xbf" // U+e3bf
#define ICON_MS_CROP_7_5 "\xee\x8f\x80" // U+e3c0
#define ICON_MS_CROP_9_16 "\xef\x95\x89" // U+f549
#define ICON_MS_CROP_DIN "\xee\x8f\x86" // U+e3c6
#define ICON_MS_CROP_FREE "\xee\x8f\x82" // U+e3c2
#define ICON_MS_CROP_LANDSCAPE "\xee\x8f\x83" // U+e3c3
#define ICON_MS_CROP_ORIGINAL "\xee\x8f\xb4" // U+e3f4
#define ICON_MS_CROP_PORTRAIT "\xee\x8f\x85" // U+e3c5
#define ICON_MS_CROP_ROTATE "\xee\x90\xb7" // U+e437
#define ICON_MS_CROP_SQUARE "\xee\x8f\x86" // U+e3c6
#define ICON_MS_CROSSWORD "\xef\x97\xa5" // U+f5e5
#define ICON_MS_CROWDSOURCE "\xee\xac\x98" // U+eb18
#define ICON_MS_CRUELTY_FREE "\xee\x9e\x99" // U+e799
#define ICON_MS_CSS "\xee\xae\x93" // U+eb93
#define ICON_MS_CSV "\xee\x9b\x8f" // U+e6cf
#define ICON_MS_CURRENCY_BITCOIN "\xee\xaf\x85" // U+ebc5
#define ICON_MS_CURRENCY_EXCHANGE "\xee\xad\xb0" // U+eb70
#define ICON_MS_CURRENCY_FRANC "\xee\xab\xba" // U+eafa
#define ICON_MS_CURRENCY_LIRA "\xee\xab\xaf" // U+eaef
#define ICON_MS_CURRENCY_POUND "\xee\xab\xb1" // U+eaf1
#define ICON_MS_CURRENCY_RUBLE "\xee\xab\xac" // U+eaec
#define ICON_MS_CURRENCY_RUPEE "\xee\xab\xb7" // U+eaf7
#define ICON_MS_CURRENCY_YEN "\xee\xab\xbb" // U+eafb
#define ICON_MS_CURRENCY_YUAN "\xee\xab\xb9" // U+eaf9
#define ICON_MS_CURTAINS "\xee\xb0\x9e" // U+ec1e
#define ICON_MS_CURTAINS_CLOSED "\xee\xb0\x9d" // U+ec1d
#define ICON_MS_CUSTOM_TYPOGRAPHY "\xee\x9c\xb2" // U+e732
#define ICON_MS_CUT "\xef\x82\x8b" // U+f08b
#define ICON_MS_CYCLE "\xef\xa1\x94" // U+f854
#define ICON_MS_CYCLONE "\xee\xaf\x95" // U+ebd5
#define ICON_MS_DANGEROUS "\xee\xa6\x9a" // U+e99a
#define ICON_MS_DARK_MODE "\xee\x94\x9c" // U+e51c
#define ICON_MS_DASHBOARD "\xee\xa1\xb1" // U+e871
#define ICON_MS_DASHBOARD_CUSTOMIZE "\xee\xa6\x9b" // U+e99b
#define ICON_MS_DATA_ALERT "\xef\x9f\xb6" // U+f7f6
#define ICON_MS_DATA_ARRAY "\xee\xab\x91" // U+ead1
#define ICON_MS_DATA_CHECK "\xef\x9f\xb2" // U+f7f2
#define ICON_MS_DATA_EXPLORATION "\xee\x9d\xaf" // U+e76f
#define ICON_MS_DATA_INFO_ALERT "\xef\x9f\xb5" // U+f7f5
#define ICON_MS_DATA_LOSS_PREVENTION "\xee\x8b\x9c" // U+e2dc
#define ICON_MS_DATA_OBJECT "\xee\xab\x93" // U+ead3
#define ICON_MS_DATA_SAVER_OFF "\xee\xbf\xb2" // U+eff2
#define ICON_MS_DATA_SAVER_ON "\xee\xbf\xb3" // U+eff3
#define ICON_MS_DATA_TABLE "\xee\xa6\x9c" // U+e99c
#define ICON_MS_DATA_THRESHOLDING "\xee\xae\x9f" // U+eb9f
#define ICON_MS_DATA_USAGE "\xee\xbf\xb2" // U+eff2
#define ICON_MS_DATABASE "\xef\x88\x8e" // U+f20e
#define ICON_MS_DATASET "\xef\xa3\xae" // U+f8ee
#define ICON_MS_DATASET_LINKED "\xef\xa3\xaf" // U+f8ef
#define ICON_MS_DATE_RANGE "\xee\xa4\x96" // U+e916
#define ICON_MS_DEBLUR "\xee\xad\xb7" // U+eb77
#define ICON_MS_DECEASED "\xee\x82\xa5" // U+e0a5
#define ICON_MS_DECIMAL_DECREASE "\xef\xa0\xad" // U+f82d
#define ICON_MS_DECIMAL_INCREASE "\xef\xa0\xac" // U+f82c
#define ICON_MS_DECK "\xee\xa9\x82" // U+ea42
#define ICON_MS_DEHAZE "\xee\x8f\x87" // U+e3c7
#define ICON_MS_DELETE "\xee\xa4\xae" // U+e92e
#define ICON_MS_DELETE_FOREVER "\xee\xa4\xab" // U+e92b
#define ICON_MS_DELETE_HISTORY "\xef\x94\x98" // U+f518
#define ICON_MS_DELETE_OUTLINE "\xee\xa4\xae" // U+e92e
#define ICON_MS_DELETE_SWEEP "\xee\x85\xac" // U+e16c
#define ICON_MS_DEMOGRAPHY "\xee\x92\x89" // U+e489
#define ICON_MS_DENSITY_LARGE "\xee\xae\xa9" // U+eba9
#define ICON_MS_DENSITY_MEDIUM "\xee\xae\x9e" // U+eb9e
#define ICON_MS_DENSITY_SMALL "\xee\xae\xa8" // U+eba8
#define ICON_MS_DENTISTRY "\xee\x82\xa6" // U+e0a6
#define ICON_MS_DEPARTURE_BOARD "\xee\x95\xb6" // U+e576
#define ICON_MS_DEPLOYED_CODE "\xef\x9c\xa0" // U+f720
#define ICON_MS_DEPLOYED_CODE_ACCOUNT "\xef\x94\x9b" // U+f51b
#define ICON_MS_DEPLOYED_CODE_ALERT "\xef\x97\xb2" // U+f5f2
#define ICON_MS_DEPLOYED_CODE_HISTORY "\xef\x97\xb3" // U+f5f3
#define ICON_MS_DEPLOYED_CODE_UPDATE "\xef\x97\xb4" // U+f5f4
#define ICON_MS_DERMATOLOGY "\xee\x82\xa7" // U+e0a7
#define ICON_MS_DESCRIPTION "\xee\xa1\xb3" // U+e873
#define ICON_MS_DESELECT "\xee\xae\xb6" // U+ebb6
#define ICON_MS_DESIGN_SERVICES "\xef\x84\x8a" // U+f10a
#define ICON_MS_DESK "\xef\xa3\xb4" // U+f8f4
#define ICON_MS_DESKPHONE "\xef\x9f\xba" // U+f7fa
#define ICON_MS_DESKTOP_ACCESS_DISABLED "\xee\xa6\x9d" // U+e99d
#define ICON_MS_DESKTOP_MAC "\xee\x8c\x8b" // U+e30b
#define ICON_MS_DESKTOP_WINDOWS "\xee\x8c\x8c" // U+e30c
#define ICON_MS_DESTRUCTION "\xef\x96\x85" // U+f585
#define ICON_MS_DETAILS "\xee\x8f\x88" // U+e3c8
#define ICON_MS_DETECTION_AND_ZONE "\xee\x8a\x9f" // U+e29f
#define ICON_MS_DETECTOR "\xee\x8a\x82" // U+e282
#define ICON_MS_DETECTOR_ALARM "\xee\x87\xb7" // U+e1f7
#define ICON_MS_DETECTOR_BATTERY "\xee\x88\x84" // U+e204
#define ICON_MS_DETECTOR_CO "\xee\x8a\xaf" // U+e2af
#define ICON_MS_DETECTOR_OFFLINE "\xee\x88\xa3" // U+e223
#define ICON_MS_DETECTOR_SMOKE "\xee\x8a\x85" // U+e285
#define ICON_MS_DETECTOR_STATUS "\xee\x87\xa8" // U+e1e8
#define ICON_MS_DEVELOPER_BOARD "\xee\x8c\x8d" // U+e30d
#define ICON_MS_DEVELOPER_BOARD_OFF "\xee\x93\xbf" // U+e4ff
#define ICON_MS_DEVELOPER_GUIDE "\xee\xa6\x9e" // U+e99e
#define ICON_MS_DEVELOPER_MODE "\xee\x86\xb0" // U+e1b0
#define ICON_MS_DEVELOPER_MODE_TV "\xee\xa1\xb4" // U+e874
#define ICON_MS_DEVICE_HUB "\xee\x8c\xb5" // U+e335
#define ICON_MS_DEVICE_RESET "\xee\xa2\xb3" // U+e8b3
#define ICON_MS_DEVICE_THERMOSTAT "\xee\x87\xbf" // U+e1ff
#define ICON_MS_DEVICE_UNKNOWN "\xee\x8c\xb9" // U+e339
#define ICON_MS_DEVICES "\xee\x8c\xa6" // U+e326
#define ICON_MS_DEVICES_FOLD "\xee\xaf\x9e" // U+ebde
#define ICON_MS_DEVICES_OFF "\xef\x9e\xa5" // U+f7a5
#define ICON_MS_DEVICES_OTHER "\xee\x8c\xb7" // U+e337
#define ICON_MS_DEVICES_WEARABLES "\xef\x9a\xab" // U+f6ab
#define ICON_MS_DEW_POINT "\xef\xa1\xb9" // U+f879
#define ICON_MS_DIAGNOSIS "\xee\x82\xa8" // U+e0a8
#define ICON_MS_DIALER_SIP "\xee\x82\xbb" // U+e0bb
#define ICON_MS_DIALOGS "\xee\xa6\x9f" // U+e99f
#define ICON_MS_DIALPAD "\xee\x82\xbc" // U+e0bc
#define ICON_MS_DIAMOND "\xee\xab\x95" // U+ead5
#define ICON_MS_DICTIONARY "\xef\x94\xb9" // U+f539
#define ICON_MS_DIFFERENCE "\xee\xad\xbd" // U+eb7d
#define ICON_MS_DIGITAL_OUT_OF_HOME "\xef\x87\x9e" // U+f1de
#define ICON_MS_DIGITAL_WELLBEING "\xee\xbe\x86" // U+ef86
#define ICON_MS_DINING "\xee\xbf\xb4" // U+eff4
#define ICON_MS_DINNER_DINING "\xee\xa9\x97" // U+ea57
#define ICON_MS_DIRECTIONS "\xee\x94\xae" // U+e52e
#define ICON_MS_DIRECTIONS_ALT "\xef\xa2\x80" // U+f880
#define ICON_MS_DIRECTIONS_ALT_OFF "\xef\xa2\x81" // U+f881
#define ICON_MS_DIRECTIONS_BIKE "\xee\x94\xaf" // U+e52f
#define ICON_MS_DIRECTIONS_BOAT "\xee\xbf\xb5" // U+eff5
#define ICON_MS_DIRECTIONS_BOAT_FILLED "\xee\xbf\xb5" // U+eff5
#define ICON_MS_DIRECTIONS_BUS "\xee\xbf\xb6" // U+eff6
#define ICON_MS_DIRECTIONS_BUS_FILLED "\xee\xbf\xb6" // U+eff6
#define ICON_MS_DIRECTIONS_CAR "\xee\xbf\xb7" // U+eff7
#define ICON_MS_DIRECTIONS_CAR_FILLED "\xee\xbf\xb7" // U+eff7
#define ICON_MS_DIRECTIONS_OFF "\xef\x84\x8f" // U+f10f
#define ICON_MS_DIRECTIONS_RAILWAY "\xee\xbf\xb8" // U+eff8
#define ICON_MS_DIRECTIONS_RAILWAY_FILLED "\xee\xbf\xb8" // U+eff8
#define ICON_MS_DIRECTIONS_RUN "\xee\x95\xa6" // U+e566
#define ICON_MS_DIRECTIONS_SUBWAY "\xee\xbf\xba" // U+effa
#define ICON_MS_DIRECTIONS_SUBWAY_FILLED "\xee\xbf\xba" // U+effa
#define ICON_MS_DIRECTIONS_TRANSIT "\xee\xbf\xba" // U+effa
#define ICON_MS_DIRECTIONS_TRANSIT_FILLED "\xee\xbf\xba" // U+effa
#define ICON_MS_DIRECTIONS_WALK "\xee\x94\xb6" // U+e536
#define ICON_MS_DIRECTORY_SYNC "\xee\x8e\x94" // U+e394
#define ICON_MS_DIRTY_LENS "\xee\xbd\x8b" // U+ef4b
#define ICON_MS_DISABLED_BY_DEFAULT "\xef\x88\xb0" // U+f230
#define ICON_MS_DISABLED_VISIBLE "\xee\x9d\xae" // U+e76e
#define ICON_MS_DISC_FULL "\xee\x98\x90" // U+e610
#define ICON_MS_DISCOVER_TUNE "\xee\x80\x98" // U+e018
#define ICON_MS_DISHWASHER "\xee\xa6\xa0" // U+e9a0
#define ICON_MS_DISHWASHER_GEN "\xee\xa0\xb2" // U+e832
#define ICON_MS_DISPLAY_EXTERNAL_INPUT "\xef\x9f\xa7" // U+f7e7
#define ICON_MS_DISPLAY_SETTINGS "\xee\xae\x97" // U+eb97
#define ICON_MS_DISTANCE "\xef\x9b\xaa" // U+f6ea
#define ICON_MS_DIVERSITY_1 "\xef\xa3\x97" // U+f8d7
#define ICON_MS_DIVERSITY_2 "\xef\xa3\x98" // U+f8d8
#define ICON_MS_DIVERSITY_3 "\xef\xa3\x99" // U+f8d9
#define ICON_MS_DIVERSITY_4 "\xef\xa1\x97" // U+f857
#define ICON_MS_DNS "\xee\xa1\xb5" // U+e875
#define ICON_MS_DO_DISTURB "\xef\x82\x8c" // U+f08c
#define ICON_MS_DO_DISTURB_ALT "\xef\x82\x8d" // U+f08d
#define ICON_MS_DO_DISTURB_OFF "\xef\x82\x8e" // U+f08e
#define ICON_MS_DO_DISTURB_ON "\xef\x82\x8f" // U+f08f
#define ICON_MS_DO_NOT_DISTURB "\xef\x82\x8d" // U+f08d
#define ICON_MS_DO_NOT_DISTURB_ALT "\xef\x82\x8c" // U+f08c
#define ICON_MS_DO_NOT_DISTURB_OFF "\xef\x82\x8e" // U+f08e
#define ICON_MS_DO_NOT_DISTURB_ON "\xef\x82\x8f" // U+f08f
#define ICON_MS_DO_NOT_DISTURB_ON_TOTAL_SILENCE "\xee\xbf\xbb" // U+effb
#define ICON_MS_DO_NOT_STEP "\xef\x86\x9f" // U+f19f
#define ICON_MS_DO_NOT_TOUCH "\xef\x86\xb0" // U+f1b0
#define ICON_MS_DOCK "\xee\x8c\x8e" // U+e30e
#define ICON_MS_DOCK_TO_BOTTOM "\xef\x9f\xa6" // U+f7e6
#define ICON_MS_DOCK_TO_LEFT "\xef\x9f\xa5" // U+f7e5
#define ICON_MS_DOCK_TO_RIGHT "\xef\x9f\xa4" // U+f7e4
#define ICON_MS_DOCS_ADD_ON "\xef\x83\x82" // U+f0c2
#define ICON_MS_DOCS_APPS_SCRIPT "\xef\x83\x83" // U+f0c3
#define ICON_MS_DOCUMENT_SCANNER "\xee\x97\xba" // U+e5fa
#define ICON_MS_DOMAIN "\xee\x9f\xae" // U+e7ee
#define ICON_MS_DOMAIN_ADD "\xee\xad\xa2" // U+eb62
#define ICON_MS_DOMAIN_DISABLED "\xee\x83\xaf" // U+e0ef
#define ICON_MS_DOMAIN_VERIFICATION "\xee\xbd\x8c" // U+ef4c
#define ICON_MS_DOMAIN_VERIFICATION_OFF "\xef\x9e\xb0" // U+f7b0
#define ICON_MS_DOMINO_MASK "\xef\x97\xa4" // U+f5e4
#define ICON_MS_DONE "\xee\xa1\xb6" // U+e876
#define ICON_MS_DONE_ALL "\xee\xa1\xb7" // U+e877
#define ICON_MS_DONE_OUTLINE "\xee\xa4\xaf" // U+e92f
#define ICON_MS_DONUT_LARGE "\xee\xa4\x97" // U+e917
#define ICON_MS_DONUT_SMALL "\xee\xa4\x98" // U+e918
#define ICON_MS_DOOR_BACK "\xee\xbf\xbc" // U+effc
#define ICON_MS_DOOR_FRONT "\xee\xbf\xbd" // U+effd
#define ICON_MS_DOOR_OPEN "\xee\x9d\xbc" // U+e77c
#define ICON_MS_DOOR_SENSOR "\xee\x8a\x8a" // U+e28a
#define ICON_MS_DOOR_SLIDING "\xee\xbf\xbe" // U+effe
#define ICON_MS_DOORBELL "\xee\xbf\xbf" // U+efff
#define ICON_MS_DOORBELL_3P "\xee\x87\xa7" // U+e1e7
#define ICON_MS_DOORBELL_CHIME "\xee\x87\xb3" // U+e1f3
#define ICON_MS_DOUBLE_ARROW "\xee\xa9\x90" // U+ea50
#define ICON_MS_DOWNHILL_SKIING "\xee\x94\x89" // U+e509
#define ICON_MS_DOWNLOAD "\xef\x82\x90" // U+f090
#define ICON_MS_DOWNLOAD_2 "\xef\x94\xa3" // U+f523
#define ICON_MS_DOWNLOAD_DONE "\xef\x82\x91" // U+f091
#define ICON_MS_DOWNLOAD_FOR_OFFLINE "\xef\x80\x80" // U+f000
#define ICON_MS_DOWNLOADING "\xef\x80\x81" // U+f001
#define ICON_MS_DRAFT "\xee\x99\xad" // U+e66d
#define ICON_MS_DRAFT_ORDERS "\xee\x9e\xb3" // U+e7b3
#define ICON_MS_DRAFTS "\xee\x85\x91" // U+e151
#define ICON_MS_DRAG_CLICK "\xef\x9c\x9f" // U+f71f
#define ICON_MS_DRAG_HANDLE "\xee\x89\x9d" // U+e25d
#define ICON_MS_DRAG_INDICATOR "\xee\xa5\x85" // U+e945
#define ICON_MS_DRAG_PAN "\xef\x9c\x9e" // U+f71e
#define ICON_MS_DRAW "\xee\x9d\x86" // U+e746
#define ICON_MS_DRAW_ABSTRACT "\xef\x9f\xb8" // U+f7f8
#define ICON_MS_DRAW_COLLAGE "\xef\x9f\xb7" // U+f7f7
#define ICON_MS_DRAWING_RECOGNITION "\xee\xac\x80" // U+eb00
#define ICON_MS_DRESSER "\xee\x88\x90" // U+e210
#define ICON_MS_DRIVE_ETA "\xee\xbf\xb7" // U+eff7
#define ICON_MS_DRIVE_FILE_MOVE "\xee\xa6\xa1" // U+e9a1
#define ICON_MS_DRIVE_FILE_MOVE_OUTLINE "\xee\xa6\xa1" // U+e9a1
#define ICON_MS_DRIVE_FILE_MOVE_RTL "\xee\xa6\xa1" // U+e9a1
#define ICON_MS_DRIVE_FILE_RENAME_OUTLINE "\xee\xa6\xa2" // U+e9a2
#define ICON_MS_DRIVE_FOLDER_UPLOAD "\xee\xa6\xa3" // U+e9a3
#define ICON_MS_DRIVE_FUSIONTABLE "\xee\x99\xb8" // U+e678
#define ICON_MS_DROPDOWN "\xee\xa6\xa4" // U+e9a4
#define ICON_MS_DRY "\xef\x86\xb3" // U+f1b3
#define ICON_MS_DRY_CLEANING "\xee\xa9\x98" // U+ea58
#define ICON_MS_DUAL_SCREEN "\xef\x9b\x8f" // U+f6cf
#define ICON_MS_DUO "\xee\xa6\xa5" // U+e9a5
#define ICON_MS_DVR "\xee\x86\xb2" // U+e1b2
#define ICON_MS_DYNAMIC_FEED "\xee\xa8\x94" // U+ea14
#define ICON_MS_DYNAMIC_FORM "\xef\x86\xbf" // U+f1bf
#define ICON_MS_E911_AVATAR "\xef\x84\x9a" // U+f11a
#define ICON_MS_E911_EMERGENCY "\xef\x84\x99" // U+f119
#define ICON_MS_E_MOBILEDATA "\xef\x80\x82" // U+f002
#define ICON_MS_E_MOBILEDATA_BADGE "\xef\x9f\xa3" // U+f7e3
#define ICON_MS_EARBUDS "\xef\x80\x83" // U+f003
#define ICON_MS_EARBUDS_BATTERY "\xef\x80\x84" // U+f004
#define ICON_MS_EARLY_ON "\xee\x8a\xba" // U+e2ba
#define ICON_MS_EARTHQUAKE "\xef\x99\x8f" // U+f64f
#define ICON_MS_EAST "\xef\x87\x9f" // U+f1df
#define ICON_MS_ECG "\xef\xa0\x8f" // U+f80f
#define ICON_MS_ECG_HEART "\xef\x9b\xa9" // U+f6e9
#define ICON_MS_ECO "\xee\xa8\xb5" // U+ea35
#define ICON_MS_EDA "\xef\x9b\xa8" // U+f6e8
#define ICON_MS_EDGESENSOR_HIGH "\xef\x80\x85" // U+f005
#define ICON_MS_EDGESENSOR_LOW "\xef\x80\x86" // U+f006
#define ICON_MS_EDIT "\xef\x82\x97" // U+f097
#define ICON_MS_EDIT_ATTRIBUTES "\xee\x95\xb8" // U+e578
#define ICON_MS_EDIT_CALENDAR "\xee\x9d\x82" // U+e742
#define ICON_MS_EDIT_DOCUMENT "\xef\xa2\x8c" // U+f88c
#define ICON_MS_EDIT_LOCATION "\xee\x95\xa8" // U+e568
#define ICON_MS_EDIT_LOCATION_ALT "\xee\x87\x85" // U+e1c5
#define ICON_MS_EDIT_NOTE "\xee\x9d\x85" // U+e745
#define ICON_MS_EDIT_NOTIFICATIONS "\xee\x94\xa5" // U+e525
#define ICON_MS_EDIT_OFF "\xee\xa5\x90" // U+e950
#define ICON_MS_EDIT_ROAD "\xee\xbd\x8d" // U+ef4d
#define ICON_MS_EDIT_SQUARE "\xef\xa2\x8d" // U+f88d
#define ICON_MS_EDITOR_CHOICE "\xef\x94\xa8" // U+f528
#define ICON_MS_EGG "\xee\xab\x8c" // U+eacc
#define ICON_MS_EGG_ALT "\xee\xab\x88" // U+eac8
#define ICON_MS_EJECT "\xee\xa3\xbb" // U+e8fb
#define ICON_MS_ELDERLY "\xef\x88\x9a" // U+f21a
#define ICON_MS_ELDERLY_WOMAN "\xee\xad\xa9" // U+eb69
#define ICON_MS_ELECTRIC_BIKE "\xee\xac\x9b" // U+eb1b
#define ICON_MS_ELECTRIC_BOLT "\xee\xb0\x9c" // U+ec1c
#define ICON_MS_ELECTRIC_CAR "\xee\xac\x9c" // U+eb1c
#define ICON_MS_ELECTRIC_METER "\xee\xb0\x9b" // U+ec1b
#define ICON_MS_ELECTRIC_MOPED "\xee\xac\x9d" // U+eb1d
#define ICON_MS_ELECTRIC_RICKSHAW "\xee\xac\x9e" // U+eb1e
#define ICON_MS_ELECTRIC_SCOOTER "\xee\xac\x9f" // U+eb1f
#define ICON_MS_ELECTRICAL_SERVICES "\xef\x84\x82" // U+f102
#define ICON_MS_ELEVATION "\xef\x9b\xa7" // U+f6e7
#define ICON_MS_ELEVATOR "\xef\x86\xa0" // U+f1a0
#define ICON_MS_EMAIL "\xee\x85\x99" // U+e159
#define ICON_MS_EMERGENCY "\xee\x87\xab" // U+e1eb
#define ICON_MS_EMERGENCY_HEAT "\xef\x85\x9d" // U+f15d
#define ICON_MS_EMERGENCY_HEAT_2 "\xef\x93\xa5" // U+f4e5
#define ICON_MS_EMERGENCY_HOME "\xee\xa0\xaa" // U+e82a
#define ICON_MS_EMERGENCY_RECORDING "\xee\xaf\xb4" // U+ebf4
#define ICON_MS_EMERGENCY_SHARE "\xee\xaf\xb6" // U+ebf6
#define ICON_MS_EMERGENCY_SHARE_OFF "\xef\x96\x9e" // U+f59e
#define ICON_MS_EMOJI_EMOTIONS "\xee\xa8\xa2" // U+ea22
#define ICON_MS_EMOJI_EVENTS "\xee\xa8\xa3" // U+ea23
#define ICON_MS_EMOJI_FLAGS "\xef\x83\x86" // U+f0c6
#define ICON_MS_EMOJI_FOOD_BEVERAGE "\xee\xa8\x9b" // U+ea1b
#define ICON_MS_EMOJI_LANGUAGE "\xef\x93\x8d" // U+f4cd
#define ICON_MS_EMOJI_NATURE "\xee\xa8\x9c" // U+ea1c
#define ICON_MS_EMOJI_OBJECTS "\xee\xa8\xa4" // U+ea24
#define ICON_MS_EMOJI_PEOPLE "\xee\xa8\x9d" // U+ea1d
#define ICON_MS_EMOJI_SYMBOLS "\xee\xa8\x9e" // U+ea1e
#define ICON_MS_EMOJI_TRANSPORTATION "\xee\xa8\x9f" // U+ea1f
#define ICON_MS_EMOTICON "\xee\x97\xb3" // U+e5f3
#define ICON_MS_EMPTY_DASHBOARD "\xef\xa1\x84" // U+f844
#define ICON_MS_ENABLE "\xef\x86\x88" // U+f188
#define ICON_MS_ENCRYPTED "\xee\x96\x93" // U+e593
#define ICON_MS_ENDOCRINOLOGY "\xee\x82\xa9" // U+e0a9
#define ICON_MS_ENERGY "\xee\xa6\xa6" // U+e9a6
#define ICON_MS_ENERGY_PROGRAM_SAVING "\xef\x85\x9f" // U+f15f
#define ICON_MS_ENERGY_PROGRAM_TIME_USED "\xef\x85\xa1" // U+f161
#define ICON_MS_ENERGY_SAVINGS_LEAF "\xee\xb0\x9a" // U+ec1a
#define ICON_MS_ENGINEERING "\xee\xa8\xbd" // U+ea3d
#define ICON_MS_ENHANCED_ENCRYPTION "\xee\x98\xbf" // U+e63f
#define ICON_MS_ENT "\xee\x82\xaa" // U+e0aa
#define ICON_MS_ENTERPRISE "\xee\x9c\x8e" // U+e70e
#define ICON_MS_ENTERPRISE_OFF "\xee\xad\x8d" // U+eb4d
#define ICON_MS_EQUAL "\xef\x9d\xbb" // U+f77b
#define ICON_MS_EQUALIZER "\xee\x80\x9d" // U+e01d
#define ICON_MS_ERROR "\xef\xa2\xb6" // U+f8b6
#define ICON_MS_ERROR_CIRCLE_ROUNDED "\xef\xa2\xb6" // U+f8b6
#define ICON_MS_ERROR_MED "\xee\x92\x9b" // U+e49b
#define ICON_MS_ERROR_OUTLINE "\xef\xa2\xb6" // U+f8b6
#define ICON_MS_ESCALATOR "\xef\x86\xa1" // U+f1a1
#define ICON_MS_ESCALATOR_WARNING "\xef\x86\xac" // U+f1ac
#define ICON_MS_EURO "\xee\xa8\x95" // U+ea15
#define ICON_MS_EURO_SYMBOL "\xee\xa4\xa6" // U+e926
#define ICON_MS_EV_CHARGER "\xee\x95\xad" // U+e56d
#define ICON_MS_EV_MOBILEDATA_BADGE "\xef\x9f\xa2" // U+f7e2
#define ICON_MS_EV_SHADOW "\xee\xbe\x8f" // U+ef8f
#define ICON_MS_EV_SHADOW_ADD "\xef\x96\x80" // U+f580
#define ICON_MS_EV_SHADOW_MINUS "\xef\x95\xbf" // U+f57f
#define ICON_MS_EV_STATION "\xee\x95\xad" // U+e56d
#define ICON_MS_EVENT "\xee\xa1\xb8" // U+e878
#define ICON_MS_EVENT_AVAILABLE "\xee\x98\x94" // U+e614
#define ICON_MS_EVENT_BUSY "\xee\x98\x95" // U+e615
#define ICON_MS_EVENT_LIST "\xef\x9a\x83" // U+f683
#define ICON_MS_EVENT_NOTE "\xee\x98\x96" // U+e616
#define ICON_MS_EVENT_REPEAT "\xee\xad\xbb" // U+eb7b
#define ICON_MS_EVENT_SEAT "\xee\xa4\x83" // U+e903
#define ICON_MS_EVENT_UPCOMING "\xef\x88\xb8" // U+f238
#define ICON_MS_EXCLAMATION "\xef\x88\xaf" // U+f22f
#define ICON_MS_EXERCISE "\xef\x9b\xa6" // U+f6e6
#define ICON_MS_EXIT_TO_APP "\xee\xa1\xb9" // U+e879
#define ICON_MS_EXPAND "\xee\xa5\x8f" // U+e94f
#define ICON_MS_EXPAND_ALL "\xee\xa5\x86" // U+e946
#define ICON_MS_EXPAND_CIRCLE_DOWN "\xee\x9f\x8d" // U+e7cd
#define ICON_MS_EXPAND_CIRCLE_RIGHT "\xef\x96\x91" // U+f591
#define ICON_MS_EXPAND_CIRCLE_UP "\xef\x97\x92" // U+f5d2
#define ICON_MS_EXPAND_CONTENT "\xef\xa0\xb0" // U+f830
#define ICON_MS_EXPAND_LESS "\xee\x97\x8e" // U+e5ce
#define ICON_MS_EXPAND_MORE "\xee\x97\x8f" // U+e5cf
#define ICON_MS_EXPERIMENT "\xee\x9a\x86" // U+e686
#define ICON_MS_EXPLICIT "\xee\x80\x9e" // U+e01e
#define ICON_MS_EXPLORE "\xee\xa1\xba" // U+e87a
#define ICON_MS_EXPLORE_NEARBY "\xee\x94\xb8" // U+e538
#define ICON_MS_EXPLORE_OFF "\xee\xa6\xa8" // U+e9a8
#define ICON_MS_EXPLOSION "\xef\x9a\x85" // U+f685
#define ICON_MS_EXPORT_NOTES "\xee\x82\xac" // U+e0ac
#define ICON_MS_EXPOSURE "\xee\x8f\xb6" // U+e3f6
#define ICON_MS_EXPOSURE_NEG_1 "\xee\x8f\x8b" // U+e3cb
#define ICON_MS_EXPOSURE_NEG_2 "\xee\x8f\x8c" // U+e3cc
#define ICON_MS_EXPOSURE_PLUS_1 "\xee\xa0\x80" // U+e800
#define ICON_MS_EXPOSURE_PLUS_2 "\xee\x8f\x8e" // U+e3ce
#define ICON_MS_EXPOSURE_ZERO "\xee\x8f\x8f" // U+e3cf
#define ICON_MS_EXTENSION "\xee\xa1\xbb" // U+e87b
#define ICON_MS_EXTENSION_OFF "\xee\x93\xb5" // U+e4f5
#define ICON_MS_EYE_TRACKING "\xef\x93\x89" // U+f4c9
#define ICON_MS_EYEGLASSES "\xef\x9b\xae" // U+f6ee
#define ICON_MS_FACE "\xef\x80\x88" // U+f008
#define ICON_MS_FACE_2 "\xef\xa3\x9a" // U+f8da
#define ICON_MS_FACE_3 "\xef\xa3\x9b" // U+f8db
#define ICON_MS_FACE_4 "\xef\xa3\x9c" // U+f8dc
#define ICON_MS_FACE_5 "\xef\xa3\x9d" // U+f8dd
#define ICON_MS_FACE_6 "\xef\xa3\x9e" // U+f8de
#define ICON_MS_FACE_RETOUCHING_NATURAL "\xee\xbd\x8e" // U+ef4e
#define ICON_MS_FACE_RETOUCHING_OFF "\xef\x80\x87" // U+f007
#define ICON_MS_FACE_UNLOCK "\xef\x80\x88" // U+f008
#define ICON_MS_FACT_CHECK "\xef\x83\x85" // U+f0c5
#define ICON_MS_FACTORY "\xee\xae\xbc" // U+ebbc
#define ICON_MS_FALLING "\xef\x98\x8d" // U+f60d
#define ICON_MS_FAMILIAR_FACE_AND_ZONE "\xee\x88\x9c" // U+e21c
#define ICON_MS_FAMILY_HISTORY "\xee\x82\xad" // U+e0ad
#define ICON_MS_FAMILY_HOME "\xee\xac\xa6" // U+eb26
#define ICON_MS_FAMILY_LINK "\xee\xac\x99" // U+eb19
#define ICON_MS_FAMILY_RESTROOM "\xef\x86\xa2" // U+f1a2
#define ICON_MS_FAMILY_STAR "\xef\x94\xa7" // U+f527
#define ICON_MS_FARSIGHT_DIGITAL "\xef\x95\x99" // U+f559
#define ICON_MS_FAST_FORWARD "\xee\x80\x9f" // U+e01f
#define ICON_MS_FAST_REWIND "\xee\x80\xa0" // U+e020
#define ICON_MS_FASTFOOD "\xee\x95\xba" // U+e57a
#define ICON_MS_FAUCET "\xee\x89\xb8" // U+e278
#define ICON_MS_FAVORITE "\xee\xa1\xbe" // U+e87e
#define ICON_MS_FAVORITE_BORDER "\xee\xa1\xbe" // U+e87e
#define ICON_MS_FAX "\xee\xab\x98" // U+ead8
#define ICON_MS_FEATURE_SEARCH "\xee\xa6\xa9" // U+e9a9
#define ICON_MS_FEATURED_PLAY_LIST "\xee\x81\xad" // U+e06d
#define ICON_MS_FEATURED_SEASONAL_AND_GIFTS "\xee\xbe\x91" // U+ef91
#define ICON_MS_FEATURED_VIDEO "\xee\x81\xae" // U+e06e
#define ICON_MS_FEED "\xef\x80\x89" // U+f009
#define ICON_MS_FEEDBACK "\xee\xa1\xbf" // U+e87f
#define ICON_MS_FEMALE "\xee\x96\x90" // U+e590
#define ICON_MS_FEMUR "\xef\xa2\x91" // U+f891
#define ICON_MS_FEMUR_ALT "\xef\xa2\x92" // U+f892
#define ICON_MS_FENCE "\xef\x87\xb6" // U+f1f6
#define ICON_MS_FERTILE "\xef\x9b\xa5" // U+f6e5
#define ICON_MS_FESTIVAL "\xee\xa9\xa8" // U+ea68
#define ICON_MS_FIBER_DVR "\xee\x81\x9d" // U+e05d
#define ICON_MS_FIBER_MANUAL_RECORD "\xee\x81\xa1" // U+e061
#define ICON_MS_FIBER_NEW "\xee\x81\x9e" // U+e05e
#define ICON_MS_FIBER_PIN "\xee\x81\xaa" // U+e06a
#define ICON_MS_FIBER_SMART_RECORD "\xee\x81\xa2" // U+e062
#define ICON_MS_FILE_COPY "\xee\x85\xb3" // U+e173
#define ICON_MS_FILE_COPY_OFF "\xef\x93\x98" // U+f4d8
#define ICON_MS_FILE_DOWNLOAD "\xef\x82\x90" // U+f090
#define ICON_MS_FILE_DOWNLOAD_DONE "\xef\x82\x91" // U+f091
#define ICON_MS_FILE_DOWNLOAD_OFF "\xee\x93\xbe" // U+e4fe
#define ICON_MS_FILE_MAP "\xee\x8b\x85" // U+e2c5
#define ICON_MS_FILE_OPEN "\xee\xab\xb3" // U+eaf3
#define ICON_MS_FILE_PRESENT "\xee\xa8\x8e" // U+ea0e
#define ICON_MS_FILE_SAVE "\xef\x85\xbf" // U+f17f
#define ICON_MS_FILE_SAVE_OFF "\xee\x94\x85" // U+e505
#define ICON_MS_FILE_UPLOAD "\xef\x82\x9b" // U+f09b
#define ICON_MS_FILE_UPLOAD_OFF "\xef\xa2\x86" // U+f886
#define ICON_MS_FILTER "\xee\x8f\x93" // U+e3d3
#define ICON_MS_FILTER_1 "\xee\x8f\x90" // U+e3d0
#define ICON_MS_FILTER_2 "\xee\x8f\x91" // U+e3d1
#define ICON_MS_FILTER_3 "\xee\x8f\x92" // U+e3d2
#define ICON_MS_FILTER_4 "\xee\x8f\x94" // U+e3d4
#define ICON_MS_FILTER_5 "\xee\x8f\x95" // U+e3d5
#define ICON_MS_FILTER_6 "\xee\x8f\x96" // U+e3d6
#define ICON_MS_FILTER_7 "\xee\x8f\x97" // U+e3d7
#define ICON_MS_FILTER_8 "\xee\x8f\x98" // U+e3d8
#define ICON_MS_FILTER_9 "\xee\x8f\x99" // U+e3d9
#define ICON_MS_FILTER_9_PLUS "\xee\x8f\x9a" // U+e3da
#define ICON_MS_FILTER_ALT "\xee\xbd\x8f" // U+ef4f
#define ICON_MS_FILTER_ALT_OFF "\xee\xac\xb2" // U+eb32
#define ICON_MS_FILTER_B_AND_W "\xee\x8f\x9b" // U+e3db
#define ICON_MS_FILTER_CENTER_FOCUS "\xee\x8f\x9c" // U+e3dc
#define ICON_MS_FILTER_DRAMA "\xee\x8f\x9d" // U+e3dd
#define ICON_MS_FILTER_FRAMES "\xee\x8f\x9e" // U+e3de
#define ICON_MS_FILTER_HDR "\xee\x8f\x9f" // U+e3df
#define ICON_MS_FILTER_LIST "\xee\x85\x92" // U+e152
#define ICON_MS_FILTER_LIST_ALT "\xee\xa5\x8e" // U+e94e
#define ICON_MS_FILTER_LIST_OFF "\xee\xad\x97" // U+eb57
#define ICON_MS_FILTER_NONE "\xee\x8f\xa0" // U+e3e0
#define ICON_MS_FILTER_RETROLUX "\xee\x8f\xa1" // U+e3e1
#define ICON_MS_FILTER_TILT_SHIFT "\xee\x8f\xa2" // U+e3e2
#define ICON_MS_FILTER_VINTAGE "\xee\x8f\xa3" // U+e3e3
#define ICON_MS_FINANCE "\xee\x9a\xbf" // U+e6bf
#define ICON_MS_FINANCE_CHIP "\xef\xa1\x8e" // U+f84e
#define ICON_MS_FINANCE_MODE "\xee\xbe\x92" // U+ef92
#define ICON_MS_FIND_IN_PAGE "\xee\xa2\x80" // U+e880
#define ICON_MS_FIND_REPLACE "\xee\xa2\x81" // U+e881
#define ICON_MS_FINGERPRINT "\xee\xa4\x8d" // U+e90d
#define ICON_MS_FINGERPRINT_OFF "\xef\x92\x9d" // U+f49d
#define ICON_MS_FIRE_EXTINGUISHER "\xef\x87\x98" // U+f1d8
#define ICON_MS_FIRE_HYDRANT "\xef\x86\xa3" // U+f1a3
#define ICON_MS_FIRE_TRUCK "\xef\xa3\xb2" // U+f8f2
#define ICON_MS_FIREPLACE "\xee\xa9\x83" // U+ea43
#define ICON_MS_FIRST_PAGE "\xee\x97\x9c" // U+e5dc
#define ICON_MS_FIT_PAGE "\xef\x9d\xba" // U+f77a
#define ICON_MS_FIT_SCREEN "\xee\xa8\x90" // U+ea10
#define ICON_MS_FIT_WIDTH "\xef\x9d\xb9" // U+f779
#define ICON_MS_FITNESS_CENTER "\xee\xad\x83" // U+eb43
#define ICON_MS_FLAG "\xef\x83\x86" // U+f0c6
#define ICON_MS_FLAG_CIRCLE "\xee\xab\xb8" // U+eaf8
#define ICON_MS_FLAG_FILLED "\xef\x83\x86" // U+f0c6
#define ICON_MS_FLAKY "\xee\xbd\x90" // U+ef50
#define ICON_MS_FLARE "\xee\x8f\xa4" // U+e3e4
#define ICON_MS_FLASH_AUTO "\xee\x8f\xa5" // U+e3e5
#define ICON_MS_FLASH_OFF "\xee\x8f\xa6" // U+e3e6
#define ICON_MS_FLASH_ON "\xee\x8f\xa7" // U+e3e7
#define ICON_MS_FLASHLIGHT_OFF "\xef\x80\x8a" // U+f00a
#define ICON_MS_FLASHLIGHT_ON "\xef\x80\x8b" // U+f00b
#define ICON_MS_FLATWARE "\xef\x80\x8c" // U+f00c
#define ICON_MS_FLEX_DIRECTION "\xef\x9d\xb8" // U+f778
#define ICON_MS_FLEX_NO_WRAP "\xef\x9d\xb7" // U+f777
#define ICON_MS_FLEX_WRAP "\xef\x9d\xb6" // U+f776
#define ICON_MS_FLIGHT "\xee\x94\xb9" // U+e539
#define ICON_MS_FLIGHT_CLASS "\xee\x9f\x8b" // U+e7cb
#define ICON_MS_FLIGHT_LAND "\xee\xa4\x84" // U+e904
#define ICON_MS_FLIGHT_TAKEOFF "\xee\xa4\x85" // U+e905
#define ICON_MS_FLIGHTS_AND_HOTELS "\xee\xa6\xab" // U+e9ab
#define ICON_MS_FLIGHTSMODE "\xee\xbe\x93" // U+ef93
#define ICON_MS_FLIP "\xee\x8f\xa8" // U+e3e8
#define ICON_MS_FLIP_CAMERA_ANDROID "\xee\xa8\xb7" // U+ea37
#define ICON_MS_FLIP_CAMERA_IOS "\xee\xa8\xb8" // U+ea38
#define ICON_MS_FLIP_TO_BACK "\xee\xa2\x82" // U+e882
#define ICON_MS_FLIP_TO_FRONT "\xee\xa2\x83" // U+e883
#define ICON_MS_FLOOD "\xee\xaf\xa6" // U+ebe6
#define ICON_MS_FLOOR "\xef\x9b\xa4" // U+f6e4
#define ICON_MS_FLOOR_LAMP "\xee\x88\x9e" // U+e21e
#define ICON_MS_FLOURESCENT "\xef\x81\xbd" // U+f07d
#define ICON_MS_FLOWSHEET "\xee\x82\xae" // U+e0ae
#define ICON_MS_FLUID "\xee\x92\x83" // U+e483
#define ICON_MS_FLUID_BALANCE "\xef\xa0\x8d" // U+f80d
#define ICON_MS_FLUID_MED "\xef\xa0\x8c" // U+f80c
#define ICON_MS_FLUORESCENT "\xef\x81\xbd" // U+f07d
#define ICON_MS_FLUTTER "\xef\x87\x9d" // U+f1dd
#define ICON_MS_FLUTTER_DASH "\xee\x80\x8b" // U+e00b
#define ICON_MS_FMD_BAD "\xef\x80\x8e" // U+f00e
#define ICON_MS_FMD_GOOD "\xef\x87\x9b" // U+f1db
#define ICON_MS_FOGGY "\xee\xa0\x98" // U+e818
#define ICON_MS_FOLDED_HANDS "\xef\x97\xad" // U+f5ed
#define ICON_MS_FOLDER "\xee\x8b\x87" // U+e2c7
#define ICON_MS_FOLDER_COPY "\xee\xae\xbd" // U+ebbd
#define ICON_MS_FOLDER_DATA "\xef\x96\x86" // U+f586
#define ICON_MS_FOLDER_DELETE "\xee\xac\xb4" // U+eb34
#define ICON_MS_FOLDER_LIMITED "\xef\x93\xa4" // U+f4e4
#define ICON_MS_FOLDER_MANAGED "\xef\x9d\xb5" // U+f775
#define ICON_MS_FOLDER_OFF "\xee\xae\x83" // U+eb83
#define ICON_MS_FOLDER_OPEN "\xee\x8b\x88" // U+e2c8
#define ICON_MS_FOLDER_SHARED "\xee\x8b\x89" // U+e2c9
#define ICON_MS_FOLDER_SPECIAL "\xee\x98\x97" // U+e617
#define ICON_MS_FOLDER_SUPERVISED "\xef\x9d\xb4" // U+f774
#define ICON_MS_FOLDER_ZIP "\xee\xac\xac" // U+eb2c
#define ICON_MS_FOLLOW_THE_SIGNS "\xef\x88\xa2" // U+f222
#define ICON_MS_FONT_DOWNLOAD "\xee\x85\xa7" // U+e167
#define ICON_MS_FONT_DOWNLOAD_OFF "\xee\x93\xb9" // U+e4f9
#define ICON_MS_FOOD_BANK "\xef\x87\xb2" // U+f1f2
#define ICON_MS_FOOT_BONES "\xef\xa2\x93" // U+f893
#define ICON_MS_FOOTPRINT "\xef\xa1\xbd" // U+f87d
#define ICON_MS_FOR_YOU "\xee\xa6\xac" // U+e9ac
#define ICON_MS_FOREST "\xee\xaa\x99" // U+ea99
#define ICON_MS_FORK_LEFT "\xee\xae\xa0" // U+eba0
#define ICON_MS_FORK_RIGHT "\xee\xae\xac" // U+ebac
#define ICON_MS_FORKLIFT "\xef\xa1\xa8" // U+f868
#define ICON_MS_FORMAT_ALIGN_CENTER "\xee\x88\xb4" // U+e234
#define ICON_MS_FORMAT_ALIGN_JUSTIFY "\xee\x88\xb5" // U+e235
#define ICON_MS_FORMAT_ALIGN_LEFT "\xee\x88\xb6" // U+e236
#define ICON_MS_FORMAT_ALIGN_RIGHT "\xee\x88\xb7" // U+e237
#define ICON_MS_FORMAT_BOLD "\xee\x88\xb8" // U+e238
#define ICON_MS_FORMAT_CLEAR "\xee\x88\xb9" // U+e239
#define ICON_MS_FORMAT_COLOR_FILL "\xee\x88\xba" // U+e23a
#define ICON_MS_FORMAT_COLOR_RESET "\xee\x88\xbb" // U+e23b
#define ICON_MS_FORMAT_COLOR_TEXT "\xee\x88\xbc" // U+e23c
#define ICON_MS_FORMAT_H1 "\xef\xa1\x9d" // U+f85d
#define ICON_MS_FORMAT_H2 "\xef\xa1\x9e" // U+f85e
#define ICON_MS_FORMAT_H3 "\xef\xa1\x9f" // U+f85f
#define ICON_MS_FORMAT_H4 "\xef\xa1\xa0" // U+f860
#define ICON_MS_FORMAT_H5 "\xef\xa1\xa1" // U+f861
#define ICON_MS_FORMAT_H6 "\xef\xa1\xa2" // U+f862
#define ICON_MS_FORMAT_IMAGE_LEFT "\xef\xa1\xa3" // U+f863
#define ICON_MS_FORMAT_IMAGE_RIGHT "\xef\xa1\xa4" // U+f864
#define ICON_MS_FORMAT_INDENT_DECREASE "\xee\x88\xbd" // U+e23d
#define ICON_MS_FORMAT_INDENT_INCREASE "\xee\x88\xbe" // U+e23e
#define ICON_MS_FORMAT_INK_HIGHLIGHTER "\xef\xa0\xab" // U+f82b
#define ICON_MS_FORMAT_ITALIC "\xee\x88\xbf" // U+e23f
#define ICON_MS_FORMAT_LETTER_SPACING "\xef\x9d\xb3" // U+f773
#define ICON_MS_FORMAT_LETTER_SPACING_2 "\xef\x98\x98" // U+f618
#define ICON_MS_FORMAT_LETTER_SPACING_STANDARD "\xef\x98\x97" // U+f617
#define ICON_MS_FORMAT_LETTER_SPACING_WIDE "\xef\x98\x96" // U+f616
#define ICON_MS_FORMAT_LETTER_SPACING_WIDER "\xef\x98\x95" // U+f615
#define ICON_MS_FORMAT_LINE_SPACING "\xee\x89\x80" // U+e240
#define ICON_MS_FORMAT_LIST_BULLETED "\xee\x89\x81" // U+e241
#define ICON_MS_FORMAT_LIST_BULLETED_ADD "\xef\xa1\x89" // U+f849
#define ICON_MS_FORMAT_LIST_NUMBERED "\xee\x89\x82" // U+e242
#define ICON_MS_FORMAT_LIST_NUMBERED_RTL "\xee\x89\xa7" // U+e267
#define ICON_MS_FORMAT_OVERLINE "\xee\xad\xa5" // U+eb65
#define ICON_MS_FORMAT_PAINT "\xee\x89\x83" // U+e243
#define ICON_MS_FORMAT_PARAGRAPH "\xef\xa1\xa5" // U+f865
#define ICON_MS_FORMAT_QUOTE "\xee\x89\x84" // U+e244
#define ICON_MS_FORMAT_SHAPES "\xee\x89\x9e" // U+e25e
#define ICON_MS_FORMAT_SIZE "\xee\x89\x85" // U+e245
#define ICON_MS_FORMAT_STRIKETHROUGH "\xee\x89\x86" // U+e246
#define ICON_MS_FORMAT_TEXT_CLIP "\xef\xa0\xaa" // U+f82a
#define ICON_MS_FORMAT_TEXT_OVERFLOW "\xef\xa0\xa9" // U+f829
#define ICON_MS_FORMAT_TEXT_WRAP "\xef\xa0\xa8" // U+f828
#define ICON_MS_FORMAT_TEXTDIRECTION_L_TO_R "\xee\x89\x87" // U+e247
#define ICON_MS_FORMAT_TEXTDIRECTION_R_TO_L "\xee\x89\x88" // U+e248
#define ICON_MS_FORMAT_TEXTDIRECTION_VERTICAL "\xef\x92\xb8" // U+f4b8
#define ICON_MS_FORMAT_UNDERLINED "\xee\x89\x89" // U+e249
#define ICON_MS_FORMAT_UNDERLINED_SQUIGGLE "\xef\xa2\x85" // U+f885
#define ICON_MS_FORMS_ADD_ON "\xef\x83\x87" // U+f0c7
#define ICON_MS_FORMS_APPS_SCRIPT "\xef\x83\x88" // U+f0c8
#define ICON_MS_FORT "\xee\xaa\xad" // U+eaad
#define ICON_MS_FORUM "\xee\xa2\xaf" // U+e8af
#define ICON_MS_FORWARD "\xef\x95\xba" // U+f57a
#define ICON_MS_FORWARD_10 "\xee\x81\x96" // U+e056
#define ICON_MS_FORWARD_30 "\xee\x81\x97" // U+e057
#define ICON_MS_FORWARD_5 "\xee\x81\x98" // U+e058
#define ICON_MS_FORWARD_CIRCLE "\xef\x9b\xb5" // U+f6f5
#define ICON_MS_FORWARD_MEDIA "\xef\x9b\xb4" // U+f6f4
#define ICON_MS_FORWARD_TO_INBOX "\xef\x86\x87" // U+f187
#define ICON_MS_FOUNDATION "\xef\x88\x80" // U+f200
#define ICON_MS_FRAME_INSPECT "\xef\x9d\xb2" // U+f772
#define ICON_MS_FRAME_PERSON "\xef\xa2\xa6" // U+f8a6
#define ICON_MS_FRAME_PERSON_MIC "\xef\x93\x95" // U+f4d5
#define ICON_MS_FRAME_PERSON_OFF "\xef\x9f\x91" // U+f7d1
#define ICON_MS_FRAME_RELOAD "\xef\x9d\xb1" // U+f771
#define ICON_MS_FRAME_SOURCE "\xef\x9d\xb0" // U+f770
#define ICON_MS_FREE_BREAKFAST "\xee\xad\x84" // U+eb44
#define ICON_MS_FREE_CANCELLATION "\xee\x9d\x88" // U+e748
#define ICON_MS_FRONT_HAND "\xee\x9d\xa9" // U+e769
#define ICON_MS_FRONT_LOADER "\xef\xa1\xa9" // U+f869
#define ICON_MS_FULL_COVERAGE "\xee\xac\x92" // U+eb12
#define ICON_MS_FULL_HD "\xef\x96\x8b" // U+f58b
#define ICON_MS_FULL_STACKED_BAR_CHART "\xef\x88\x92" // U+f212
#define ICON_MS_FULLSCREEN "\xee\x97\x90" // U+e5d0
#define ICON_MS_FULLSCREEN_EXIT "\xee\x97\x91" // U+e5d1
#define ICON_MS_FUNCTION "\xef\xa1\xa6" // U+f866
#define ICON_MS_FUNCTIONS "\xee\x89\x8a" // U+e24a
#define ICON_MS_G_MOBILEDATA "\xef\x80\x90" // U+f010
#define ICON_MS_G_MOBILEDATA_BADGE "\xef\x9f\xa1" // U+f7e1
#define ICON_MS_G_TRANSLATE "\xee\xa4\xa7" // U+e927
#define ICON_MS_GALLERY_THUMBNAIL "\xef\xa1\xaf" // U+f86f
#define ICON_MS_GAMEPAD "\xee\x8c\x8f" // U+e30f
#define ICON_MS_GAMES "\xee\x8c\x8f" // U+e30f
#define ICON_MS_GARAGE "\xef\x80\x91" // U+f011
#define ICON_MS_GARAGE_DOOR "\xee\x9c\x94" // U+e714
#define ICON_MS_GARAGE_HOME "\xee\xa0\xad" // U+e82d
#define ICON_MS_GARDEN_CART "\xef\xa2\xa9" // U+f8a9
#define ICON_MS_GAS_METER "\xee\xb0\x99" // U+ec19
#define ICON_MS_GASTROENTEROLOGY "\xee\x83\xb1" // U+e0f1
#define ICON_MS_GATE "\xee\x89\xb7" // U+e277
#define ICON_MS_GAVEL "\xee\xa4\x8e" // U+e90e
#define ICON_MS_GENERAL_DEVICE "\xee\x9b\x9e" // U+e6de
#define ICON_MS_GENERATING_TOKENS "\xee\x9d\x89" // U+e749
#define ICON_MS_GENETICS "\xee\x83\xb3" // U+e0f3
#define ICON_MS_GENRES "\xee\x9b\xae" // U+e6ee
#define ICON_MS_GESTURE "\xee\x85\x95" // U+e155
#define ICON_MS_GESTURE_SELECT "\xef\x99\x97" // U+f657
#define ICON_MS_GET_APP "\xef\x82\x90" // U+f090
#define ICON_MS_GIF "\xee\xa4\x88" // U+e908
#define ICON_MS_GIF_BOX "\xee\x9e\xa3" // U+e7a3
#define ICON_MS_GIRL "\xee\xad\xa8" // U+eb68
#define ICON_MS_GITE "\xee\x96\x8b" // U+e58b
#define ICON_MS_GLASS_CUP "\xef\x9b\xa3" // U+f6e3
#define ICON_MS_GLOBE "\xee\x99\x8c" // U+e64c
#define ICON_MS_GLOBE_ASIA "\xef\x9e\x99" // U+f799
#define ICON_MS_GLOBE_UK "\xef\x9e\x98" // U+f798
#define ICON_MS_GLUCOSE "\xee\x92\xa0" // U+e4a0
#define ICON_MS_GLYPHS "\xef\xa2\xa3" // U+f8a3
#define ICON_MS_GO_TO_LINE "\xef\x9c\x9d" // U+f71d
#define ICON_MS_GOLF_COURSE "\xee\xad\x85" // U+eb45
#define ICON_MS_GOOGLE_HOME_DEVICES "\xee\x9c\x95" // U+e715
#define ICON_MS_GOOGLE_PLUS_RESHARE "\xef\x95\xba" // U+f57a
#define ICON_MS_GOOGLE_TV_REMOTE "\xef\x97\x9b" // U+f5db
#define ICON_MS_GOOGLE_WIFI "\xef\x95\xb9" // U+f579
#define ICON_MS_GPP_BAD "\xef\x80\x92" // U+f012
#define ICON_MS_GPP_GOOD "\xef\x80\x93" // U+f013
#define ICON_MS_GPP_MAYBE "\xef\x80\x94" // U+f014
#define ICON_MS_GPS_FIXED "\xee\x95\x9c" // U+e55c
#define ICON_MS_GPS_NOT_FIXED "\xee\x86\xb7" // U+e1b7
#define ICON_MS_GPS_OFF "\xee\x86\xb6" // U+e1b6
#define ICON_MS_GRADE "\xee\xa2\x85" // U+e885
#define ICON_MS_GRADIENT "\xee\x8f\xa9" // U+e3e9
#define ICON_MS_GRADING "\xee\xa9\x8f" // U+ea4f
#define ICON_MS_GRAIN "\xee\x8f\xaa" // U+e3ea
#define ICON_MS_GRAPHIC_EQ "\xee\x86\xb8" // U+e1b8
#define ICON_MS_GRASS "\xef\x88\x85" // U+f205
#define ICON_MS_GRID_3X3 "\xef\x80\x95" // U+f015
#define ICON_MS_GRID_3X3_OFF "\xef\x99\xbc" // U+f67c
#define ICON_MS_GRID_4X4 "\xef\x80\x96" // U+f016
#define ICON_MS_GRID_GOLDENRATIO "\xef\x80\x97" // U+f017
#define ICON_MS_GRID_GUIDES "\xef\x9d\xaf" // U+f76f
#define ICON_MS_GRID_OFF "\xee\x8f\xab" // U+e3eb
#define ICON_MS_GRID_ON "\xee\x8f\xac" // U+e3ec
#define ICON_MS_GRID_VIEW "\xee\xa6\xb0" // U+e9b0
#define ICON_MS_GROCERY "\xee\xbe\x97" // U+ef97
#define ICON_MS_GROUP "\xee\xa8\xa1" // U+ea21
#define ICON_MS_GROUP_ADD "\xee\x9f\xb0" // U+e7f0
#define ICON_MS_GROUP_OFF "\xee\x9d\x87" // U+e747
#define ICON_MS_GROUP_REMOVE "\xee\x9e\xad" // U+e7ad
#define ICON_MS_GROUP_WORK "\xee\xa2\x86" // U+e886
#define ICON_MS_GROUPED_BAR_CHART "\xef\x88\x91" // U+f211
#define ICON_MS_GROUPS "\xef\x88\xb3" // U+f233
#define ICON_MS_GROUPS_2 "\xef\xa3\x9f" // U+f8df
#define ICON_MS_GROUPS_3 "\xef\xa3\xa0" // U+f8e0
#define ICON_MS_GUARDIAN "\xef\x93\x81" // U+f4c1
#define ICON_MS_GYNECOLOGY "\xee\x83\xb4" // U+e0f4
#define ICON_MS_H_MOBILEDATA "\xef\x80\x98" // U+f018
#define ICON_MS_H_MOBILEDATA_BADGE "\xef\x9f\xa0" // U+f7e0
#define ICON_MS_H_PLUS_MOBILEDATA "\xef\x80\x99" // U+f019
#define ICON_MS_H_PLUS_MOBILEDATA_BADGE "\xef\x9f\x9f" // U+f7df
#define ICON_MS_HAIL "\xee\xa6\xb1" // U+e9b1
#define ICON_MS_HALLWAY "\xee\x9b\xb8" // U+e6f8
#define ICON_MS_HAND_BONES "\xef\xa2\x94" // U+f894
#define ICON_MS_HAND_GESTURE "\xee\xbe\x9c" // U+ef9c
#define ICON_MS_HANDHELD_CONTROLLER "\xef\x93\x86" // U+f4c6
#define ICON_MS_HANDSHAKE "\xee\xaf\x8b" // U+ebcb
#define ICON_MS_HANDWRITING_RECOGNITION "\xee\xac\x82" // U+eb02
#define ICON_MS_HANDYMAN "\xef\x84\x8b" // U+f10b
#define ICON_MS_HANGOUT_VIDEO "\xee\x83\x81" // U+e0c1
#define ICON_MS_HANGOUT_VIDEO_OFF "\xee\x83\x82" // U+e0c2
#define ICON_MS_HARD_DRIVE "\xef\xa0\x8e" // U+f80e
#define ICON_MS_HARD_DRIVE_2 "\xef\x9e\xa4" // U+f7a4
#define ICON_MS_HARDWARE "\xee\xa9\x99" // U+ea59
#define ICON_MS_HD "\xee\x81\x92" // U+e052
#define ICON_MS_HDR_AUTO "\xef\x80\x9a" // U+f01a
#define ICON_MS_HDR_AUTO_SELECT "\xef\x80\x9b" // U+f01b
#define ICON_MS_HDR_ENHANCED_SELECT "\xee\xbd\x91" // U+ef51
#define ICON_MS_HDR_OFF "\xee\x8f\xad" // U+e3ed
#define ICON_MS_HDR_OFF_SELECT "\xef\x80\x9c" // U+f01c
#define ICON_MS_HDR_ON "\xee\x8f\xae" // U+e3ee
#define ICON_MS_HDR_ON_SELECT "\xef\x80\x9d" // U+f01d
#define ICON_MS_HDR_PLUS "\xef\x80\x9e" // U+f01e
#define ICON_MS_HDR_PLUS_OFF "\xee\x8f\xaf" // U+e3ef
#define ICON_MS_HDR_STRONG "\xee\x8f\xb1" // U+e3f1
#define ICON_MS_HDR_WEAK "\xee\x8f\xb2" // U+e3f2
#define ICON_MS_HEAD_MOUNTED_DEVICE "\xef\x93\x85" // U+f4c5
#define ICON_MS_HEADPHONES "\xef\x80\x9f" // U+f01f
#define ICON_MS_HEADPHONES_BATTERY "\xef\x80\xa0" // U+f020
#define ICON_MS_HEADSET "\xef\x80\x9f" // U+f01f
#define ICON_MS_HEADSET_MIC "\xee\x8c\x91" // U+e311
#define ICON_MS_HEADSET_OFF "\xee\x8c\xba" // U+e33a
#define ICON_MS_HEALING "\xee\x8f\xb3" // U+e3f3
#define ICON_MS_HEALTH_AND_BEAUTY "\xee\xbe\x9d" // U+ef9d
#define ICON_MS_HEALTH_AND_SAFETY "\xee\x87\x95" // U+e1d5
#define ICON_MS_HEALTH_METRICS "\xef\x9b\xa2" // U+f6e2
#define ICON_MS_HEAP_SNAPSHOT_LARGE "\xef\x9d\xae" // U+f76e
#define ICON_MS_HEAP_SNAPSHOT_MULTIPLE "\xef\x9d\xad" // U+f76d
#define ICON_MS_HEAP_SNAPSHOT_THUMBNAIL "\xef\x9d\xac" // U+f76c
#define ICON_MS_HEARING "\xee\x80\xa3" // U+e023
#define ICON_MS_HEARING_DISABLED "\xef\x84\x84" // U+f104
#define ICON_MS_HEART_BROKEN "\xee\xab\x82" // U+eac2
#define ICON_MS_HEART_CHECK "\xef\x98\x8a" // U+f60a
#define ICON_MS_HEART_MINUS "\xef\xa2\x83" // U+f883
#define ICON_MS_HEART_PLUS "\xef\xa2\x84" // U+f884
#define ICON_MS_HEAT "\xef\x94\xb7" // U+f537
#define ICON_MS_HEAT_PUMP "\xee\xb0\x98" // U+ec18
#define ICON_MS_HEAT_PUMP_BALANCE "\xee\x89\xbe" // U+e27e
#define ICON_MS_HEIGHT "\xee\xa8\x96" // U+ea16
#define ICON_MS_HELICOPTER "\xef\x98\x8c" // U+f60c
#define ICON_MS_HELP "\xee\xa3\xbd" // U+e8fd
#define ICON_MS_HELP_CENTER "\xef\x87\x80" // U+f1c0
#define ICON_MS_HELP_CLINIC "\xef\xa0\x90" // U+f810
#define ICON_MS_HELP_OUTLINE "\xee\xa3\xbd" // U+e8fd
#define ICON_MS_HEMATOLOGY "\xee\x83\xb6" // U+e0f6
#define ICON_MS_HEVC "\xef\x80\xa1" // U+f021
#define ICON_MS_HEXAGON "\xee\xac\xb9" // U+eb39
#define ICON_MS_HIDE "\xee\xbe\x9e" // U+ef9e
#define ICON_MS_HIDE_IMAGE "\xef\x80\xa2" // U+f022
#define ICON_MS_HIDE_SOURCE "\xef\x80\xa3" // U+f023
#define ICON_MS_HIGH_DENSITY "\xef\x9e\x9c" // U+f79c
#define ICON_MS_HIGH_QUALITY "\xee\x80\xa4" // U+e024
#define ICON_MS_HIGH_RES "\xef\x95\x8b" // U+f54b
#define ICON_MS_HIGHLIGHT "\xee\x89\x9f" // U+e25f
#define ICON_MS_HIGHLIGHT_KEYBOARD_FOCUS "\xef\x94\x90" // U+f510
#define ICON_MS_HIGHLIGHT_MOUSE_CURSOR "\xef\x94\x91" // U+f511
#define ICON_MS_HIGHLIGHT_OFF "\xee\xa2\x88" // U+e888
#define ICON_MS_HIGHLIGHT_TEXT_CURSOR "\xef\x94\x92" // U+f512
#define ICON_MS_HIGHLIGHTER_SIZE_1 "\xef\x9d\xab" // U+f76b
#define ICON_MS_HIGHLIGHTER_SIZE_2 "\xef\x9d\xaa" // U+f76a
#define ICON_MS_HIGHLIGHTER_SIZE_3 "\xef\x9d\xa9" // U+f769
#define ICON_MS_HIGHLIGHTER_SIZE_4 "\xef\x9d\xa8" // U+f768
#define ICON_MS_HIGHLIGHTER_SIZE_5 "\xef\x9d\xa7" // U+f767
#define ICON_MS_HIKING "\xee\x94\x8a" // U+e50a
#define ICON_MS_HISTORY "\xee\xa2\xb3" // U+e8b3
#define ICON_MS_HISTORY_EDU "\xee\xa8\xbe" // U+ea3e
#define ICON_MS_HISTORY_OFF "\xef\x93\x9a" // U+f4da
#define ICON_MS_HISTORY_TOGGLE_OFF "\xef\x85\xbd" // U+f17d
#define ICON_MS_HIVE "\xee\xaa\xa6" // U+eaa6
#define ICON_MS_HLS "\xee\xae\x8a" // U+eb8a
#define ICON_MS_HLS_OFF "\xee\xae\x8c" // U+eb8c
#define ICON_MS_HOLIDAY_VILLAGE "\xee\x96\x8a" // U+e58a
#define ICON_MS_HOME "\xee\xa6\xb2" // U+e9b2
#define ICON_MS_HOME_AND_GARDEN "\xee\xbe\x9f" // U+ef9f
#define ICON_MS_HOME_APP_LOGO "\xee\x8a\x95" // U+e295
#define ICON_MS_HOME_FILLED "\xee\xa6\xb2" // U+e9b2
#define ICON_MS_HOME_HEALTH "\xee\x92\xb9" // U+e4b9
#define ICON_MS_HOME_IMPROVEMENT_AND_TOOLS "\xee\xbe\xa0" // U+efa0
#define ICON_MS_HOME_IOT_DEVICE "\xee\x8a\x83" // U+e283
#define ICON_MS_HOME_MAX "\xef\x80\xa4" // U+f024
#define ICON_MS_HOME_MAX_DOTS "\xee\xa1\x89" // U+e849
#define ICON_MS_HOME_MINI "\xef\x80\xa5" // U+f025
#define ICON_MS_HOME_PIN "\xef\x85\x8d" // U+f14d
#define ICON_MS_HOME_REPAIR_SERVICE "\xef\x84\x80" // U+f100
#define ICON_MS_HOME_SPEAKER "\xef\x84\x9c" // U+f11c
#define ICON_MS_HOME_STORAGE "\xef\xa1\xac" // U+f86c
#define ICON_MS_HOME_WORK "\xef\x80\xb0" // U+f030
#define ICON_MS_HORIZONTAL_DISTRIBUTE "\xee\x80\x94" // U+e014
#define ICON_MS_HORIZONTAL_RULE "\xef\x84\x88" // U+f108
#define ICON_MS_HORIZONTAL_SPLIT "\xee\xa5\x87" // U+e947
#define ICON_MS_HOT_TUB "\xee\xad\x86" // U+eb46
#define ICON_MS_HOTEL "\xee\x95\x89" // U+e549
#define ICON_MS_HOTEL_CLASS "\xee\x9d\x83" // U+e743
#define ICON_MS_HOURGLASS "\xee\xaf\xbf" // U+ebff
#define ICON_MS_HOURGLASS_BOTTOM "\xee\xa9\x9c" // U+ea5c
#define ICON_MS_HOURGLASS_DISABLED "\xee\xbd\x93" // U+ef53
#define ICON_MS_HOURGLASS_EMPTY "\xee\xa2\x8b" // U+e88b
#define ICON_MS_HOURGLASS_FULL "\xee\xa2\x8c" // U+e88c
#define ICON_MS_HOURGLASS_TOP "\xee\xa9\x9b" // U+ea5b
#define ICON_MS_HOUSE "\xee\xa9\x84" // U+ea44
#define ICON_MS_HOUSE_SIDING "\xef\x88\x82" // U+f202
#define ICON_MS_HOUSE_WITH_SHIELD "\xee\x9e\x86" // U+e786
#define ICON_MS_HOUSEBOAT "\xee\x96\x84" // U+e584
#define ICON_MS_HOUSEHOLD_SUPPLIES "\xee\xbe\xa1" // U+efa1
#define ICON_MS_HOW_TO_REG "\xee\x85\xb4" // U+e174
#define ICON_MS_HOW_TO_VOTE "\xee\x85\xb5" // U+e175
#define ICON_MS_HR_RESTING "\xef\x9a\xba" // U+f6ba
#define ICON_MS_HTML "\xee\xad\xbe" // U+eb7e
#define ICON_MS_HTTP "\xee\xa4\x82" // U+e902
#define ICON_MS_HTTPS "\xee\xa2\x99" // U+e899
#define ICON_MS_HUB "\xee\xa7\xb4" // U+e9f4
#define ICON_MS_HUMERUS "\xef\xa2\x95" // U+f895
#define ICON_MS_HUMERUS_ALT "\xef\xa2\x96" // U+f896
#define ICON_MS_HUMIDITY_HIGH "\xef\x85\xa3" // U+f163
#define ICON_MS_HUMIDITY_INDOOR "\xef\x95\x98" // U+f558
#define ICON_MS_HUMIDITY_LOW "\xef\x85\xa4" // U+f164
#define ICON_MS_HUMIDITY_MID "\xef\x85\xa5" // U+f165
#define ICON_MS_HUMIDITY_PERCENTAGE "\xef\xa1\xbe" // U+f87e
#define ICON_MS_HVAC "\xef\x84\x8e" // U+f10e
#define ICON_MS_ICE_SKATING "\xee\x94\x8b" // U+e50b
#define ICON_MS_ICECREAM "\xee\xa9\xa9" // U+ea69
#define ICON_MS_ID_CARD "\xef\x93\x8a" // U+f4ca
#define ICON_MS_IFL "\xee\x80\xa5" // U+e025
#define ICON_MS_IFRAME "\xef\x9c\x9b" // U+f71b
#define ICON_MS_IFRAME_OFF "\xef\x9c\x9c" // U+f71c
#define ICON_MS_IMAGE "\xee\x8f\xb4" // U+e3f4
#define ICON_MS_IMAGE_ASPECT_RATIO "\xee\x8f\xb5" // U+e3f5
#define ICON_MS_IMAGE_NOT_SUPPORTED "\xef\x84\x96" // U+f116
#define ICON_MS_IMAGE_SEARCH "\xee\x90\xbf" // U+e43f
#define ICON_MS_IMAGESEARCH_ROLLER "\xee\xa6\xb4" // U+e9b4
#define ICON_MS_IMAGESMODE "\xee\xbe\xa2" // U+efa2
#define ICON_MS_IMMUNOLOGY "\xee\x83\xbb" // U+e0fb
#define ICON_MS_IMPORT_CONTACTS "\xee\x83\xa0" // U+e0e0
#define ICON_MS_IMPORT_EXPORT "\xee\xa3\x95" // U+e8d5
#define ICON_MS_IMPORTANT_DEVICES "\xee\xa4\x92" // U+e912
#define ICON_MS_IN_HOME_MODE "\xee\xa0\xb3" // U+e833
#define ICON_MS_INACTIVE_ORDER "\xee\x83\xbc" // U+e0fc
#define ICON_MS_INBOX "\xee\x85\x96" // U+e156
#define ICON_MS_INBOX_CUSTOMIZE "\xef\xa1\x99" // U+f859
#define ICON_MS_INCOMPLETE_CIRCLE "\xee\x9e\x9b" // U+e79b
#define ICON_MS_INDETERMINATE_CHECK_BOX "\xee\xa4\x89" // U+e909
#define ICON_MS_INDETERMINATE_QUESTION_BOX "\xef\x95\xad" // U+f56d
#define ICON_MS_INFO "\xee\xa2\x8e" // U+e88e
#define ICON_MS_INFO_I "\xef\x96\x9b" // U+f59b
#define ICON_MS_INFRARED "\xef\xa1\xbc" // U+f87c
#define ICON_MS_INK_ERASER "\xee\x9b\x90" // U+e6d0
#define ICON_MS_INK_ERASER_OFF "\xee\x9f\xa3" // U+e7e3
#define ICON_MS_INK_HIGHLIGHTER "\xee\x9b\x91" // U+e6d1
#define ICON_MS_INK_HIGHLIGHTER_MOVE "\xef\x94\xa4" // U+f524
#define ICON_MS_INK_MARKER "\xee\x9b\x92" // U+e6d2
#define ICON_MS_INK_PEN "\xee\x9b\x93" // U+e6d3
#define ICON_MS_INPATIENT "\xee\x83\xbe" // U+e0fe
#define ICON_MS_INPUT "\xee\xa2\x90" // U+e890
#define ICON_MS_INPUT_CIRCLE "\xef\x9c\x9a" // U+f71a
#define ICON_MS_INSERT_CHART "\xef\x83\x8c" // U+f0cc
#define ICON_MS_INSERT_CHART_FILLED "\xef\x83\x8c" // U+f0cc
#define ICON_MS_INSERT_CHART_OUTLINED "\xef\x83\x8c" // U+f0cc
#define ICON_MS_INSERT_COMMENT "\xee\x89\x8c" // U+e24c
#define ICON_MS_INSERT_DRIVE_FILE "\xee\x99\xad" // U+e66d
#define ICON_MS_INSERT_EMOTICON "\xee\xa8\xa2" // U+ea22
#define ICON_MS_INSERT_INVITATION "\xee\xa1\xb8" // U+e878
#define ICON_MS_INSERT_LINK "\xee\x89\x90" // U+e250
#define ICON_MS_INSERT_PAGE_BREAK "\xee\xab\x8a" // U+eaca
#define ICON_MS_INSERT_PHOTO "\xee\x8f\xb4" // U+e3f4
#define ICON_MS_INSERT_TEXT "\xef\xa0\xa7" // U+f827
#define ICON_MS_INSIGHTS "\xef\x82\x92" // U+f092
#define ICON_MS_INSTALL_DESKTOP "\xee\xad\xb1" // U+eb71
#define ICON_MS_INSTALL_MOBILE "\xee\xad\xb2" // U+eb72
#define ICON_MS_INSTANT_MIX "\xee\x80\xa6" // U+e026
#define ICON_MS_INTEGRATION_INSTRUCTIONS "\xee\xbd\x94" // U+ef54
#define ICON_MS_INTERACTIVE_SPACE "\xef\x9f\xbf" // U+f7ff
#define ICON_MS_INTERESTS "\xee\x9f\x88" // U+e7c8
#define ICON_MS_INTERPRETER_MODE "\xee\xa0\xbb" // U+e83b
#define ICON_MS_INVENTORY "\xee\x85\xb9" // U+e179
#define ICON_MS_INVENTORY_2 "\xee\x86\xa1" // U+e1a1
#define ICON_MS_INVERT_COLORS "\xee\xa2\x91" // U+e891
#define ICON_MS_INVERT_COLORS_OFF "\xee\x83\x84" // U+e0c4
#define ICON_MS_IOS "\xee\x80\xa7" // U+e027
#define ICON_MS_IOS_SHARE "\xee\x9a\xb8" // U+e6b8
#define ICON_MS_IRON "\xee\x96\x83" // U+e583
#define ICON_MS_ISO "\xee\x8f\xb6" // U+e3f6
#define ICON_MS_JAMBOARD_KIOSK "\xee\xa6\xb5" // U+e9b5
#define ICON_MS_JAVASCRIPT "\xee\xad\xbc" // U+eb7c
#define ICON_MS_JOIN "\xef\xa1\x8f" // U+f84f
#define ICON_MS_JOIN_FULL "\xef\xa1\x8f" // U+f84f
#define ICON_MS_JOIN_INNER "\xee\xab\xb4" // U+eaf4
#define ICON_MS_JOIN_LEFT "\xee\xab\xb2" // U+eaf2
#define ICON_MS_JOIN_RIGHT "\xee\xab\xaa" // U+eaea
#define ICON_MS_JOYSTICK "\xef\x97\xae" // U+f5ee
#define ICON_MS_JUMP_TO_ELEMENT "\xef\x9c\x99" // U+f719
#define ICON_MS_KAYAKING "\xee\x94\x8c" // U+e50c
#define ICON_MS_KEBAB_DINING "\xee\xa1\x82" // U+e842
#define ICON_MS_KEEP "\xef\x80\xa6" // U+f026
#define ICON_MS_KEEP_OFF "\xee\x9b\xb9" // U+e6f9
#define ICON_MS_KEEP_PIN "\xef\x80\xa6" // U+f026
#define ICON_MS_KEEP_PUBLIC "\xef\x95\xaf" // U+f56f
#define ICON_MS_KETTLE "\xee\x8a\xb9" // U+e2b9
#define ICON_MS_KEY "\xee\x9c\xbc" // U+e73c
#define ICON_MS_KEY_OFF "\xee\xae\x84" // U+eb84
#define ICON_MS_KEY_VERTICAL "\xef\x94\x9a" // U+f51a
#define ICON_MS_KEY_VISUALIZER "\xef\x86\x99" // U+f199
#define ICON_MS_KEYBOARD "\xee\x8c\x92" // U+e312
#define ICON_MS_KEYBOARD_ALT "\xef\x80\xa8" // U+f028
#define ICON_MS_KEYBOARD_ARROW_DOWN "\xee\x8c\x93" // U+e313
#define ICON_MS_KEYBOARD_ARROW_LEFT "\xee\x8c\x94" // U+e314
#define ICON_MS_KEYBOARD_ARROW_RIGHT "\xee\x8c\x95" // U+e315
#define ICON_MS_KEYBOARD_ARROW_UP "\xee\x8c\x96" // U+e316
#define ICON_MS_KEYBOARD_BACKSPACE "\xee\x8c\x97" // U+e317
#define ICON_MS_KEYBOARD_CAPSLOCK "\xee\x8c\x98" // U+e318
#define ICON_MS_KEYBOARD_CAPSLOCK_BADGE "\xef\x9f\x9e" // U+f7de
#define ICON_MS_KEYBOARD_COMMAND_KEY "\xee\xab\xa7" // U+eae7
#define ICON_MS_KEYBOARD_CONTROL_KEY "\xee\xab\xa6" // U+eae6
#define ICON_MS_KEYBOARD_DOUBLE_ARROW_DOWN "\xee\xab\x90" // U+ead0
#define ICON_MS_KEYBOARD_DOUBLE_ARROW_LEFT "\xee\xab\x83" // U+eac3
#define ICON_MS_KEYBOARD_DOUBLE_ARROW_RIGHT "\xee\xab\x89" // U+eac9
#define ICON_MS_KEYBOARD_DOUBLE_ARROW_UP "\xee\xab\x8f" // U+eacf
#define ICON_MS_KEYBOARD_EXTERNAL_INPUT "\xef\x9f\x9d" // U+f7dd
#define ICON_MS_KEYBOARD_FULL "\xef\x9f\x9c" // U+f7dc
#define ICON_MS_KEYBOARD_HIDE "\xee\x8c\x9a" // U+e31a
#define ICON_MS_KEYBOARD_KEYS "\xef\x99\xbb" // U+f67b
#define ICON_MS_KEYBOARD_LOCK "\xef\x92\x92" // U+f492
#define ICON_MS_KEYBOARD_LOCK_OFF "\xef\x92\x91" // U+f491
#define ICON_MS_KEYBOARD_OFF "\xef\x99\xba" // U+f67a
#define ICON_MS_KEYBOARD_ONSCREEN "\xef\x9f\x9b" // U+f7db
#define ICON_MS_KEYBOARD_OPTION_KEY "\xee\xab\xa8" // U+eae8
#define ICON_MS_KEYBOARD_PREVIOUS_LANGUAGE "\xef\x9f\x9a" // U+f7da
#define ICON_MS_KEYBOARD_RETURN "\xee\x8c\x9b" // U+e31b
#define ICON_MS_KEYBOARD_TAB "\xee\x8c\x9c" // U+e31c
#define ICON_MS_KEYBOARD_TAB_RTL "\xee\xb1\xb3" // U+ec73
#define ICON_MS_KEYBOARD_VOICE "\xee\x8c\x9d" // U+e31d
#define ICON_MS_KID_STAR "\xef\x94\xa6" // U+f526
#define ICON_MS_KING_BED "\xee\xa9\x85" // U+ea45
#define ICON_MS_KITCHEN "\xee\xad\x87" // U+eb47
#define ICON_MS_KITESURFING "\xee\x94\x8d" // U+e50d
#define ICON_MS_LAB_PANEL "\xee\x84\x83" // U+e103
#define ICON_MS_LAB_PROFILE "\xee\x84\x84" // U+e104
#define ICON_MS_LAB_RESEARCH "\xef\xa0\x8b" // U+f80b
#define ICON_MS_LABEL "\xee\xa2\x93" // U+e893
#define ICON_MS_LABEL_IMPORTANT "\xee\xa5\x88" // U+e948
#define ICON_MS_LABEL_IMPORTANT_OUTLINE "\xee\xa5\x88" // U+e948
#define ICON_MS_LABEL_OFF "\xee\xa6\xb6" // U+e9b6
#define ICON_MS_LABEL_OUTLINE "\xee\xa2\x93" // U+e893
#define ICON_MS_LABS "\xee\x84\x85" // U+e105
#define ICON_MS_LAN "\xee\xac\xaf" // U+eb2f
#define ICON_MS_LANDSCAPE "\xee\x95\xa4" // U+e564
#define ICON_MS_LANDSCAPE_2 "\xef\x93\x84" // U+f4c4
#define ICON_MS_LANDSCAPE_2_OFF "\xef\x93\x83" // U+f4c3
#define ICON_MS_LANDSLIDE "\xee\xaf\x97" // U+ebd7
#define ICON_MS_LANGUAGE "\xee\xa2\x94" // U+e894
#define ICON_MS_LANGUAGE_CHINESE_ARRAY "\xef\x9d\xa6" // U+f766
#define ICON_MS_LANGUAGE_CHINESE_CANGJIE "\xef\x9d\xa5" // U+f765
#define ICON_MS_LANGUAGE_CHINESE_DAYI "\xef\x9d\xa4" // U+f764
#define ICON_MS_LANGUAGE_CHINESE_PINYIN "\xef\x9d\xa3" // U+f763
#define ICON_MS_LANGUAGE_CHINESE_QUICK "\xef\x9d\xa2" // U+f762
#define ICON_MS_LANGUAGE_CHINESE_WUBI "\xef\x9d\xa1" // U+f761
#define ICON_MS_LANGUAGE_FRENCH "\xef\x9d\xa0" // U+f760
#define ICON_MS_LANGUAGE_GB_ENGLISH "\xef\x9d\x9f" // U+f75f
#define ICON_MS_LANGUAGE_INTERNATIONAL "\xef\x9d\x9e" // U+f75e
#define ICON_MS_LANGUAGE_JAPANESE_KANA "\xef\x94\x93" // U+f513
#define ICON_MS_LANGUAGE_KOREAN_LATIN "\xef\x9d\x9d" // U+f75d
#define ICON_MS_LANGUAGE_PINYIN "\xef\x9d\x9c" // U+f75c
#define ICON_MS_LANGUAGE_SPANISH "\xef\x97\xa9" // U+f5e9
#define ICON_MS_LANGUAGE_US "\xef\x9d\x99" // U+f759
#define ICON_MS_LANGUAGE_US_COLEMAK "\xef\x9d\x9b" // U+f75b
#define ICON_MS_LANGUAGE_US_DVORAK "\xef\x9d\x9a" // U+f75a
#define ICON_MS_LAPS "\xef\x9a\xb9" // U+f6b9
#define ICON_MS_LAPTOP "\xee\x8c\x9e" // U+e31e
#define ICON_MS_LAPTOP_CHROMEBOOK "\xee\x8c\x9f" // U+e31f
#define ICON_MS_LAPTOP_MAC "\xee\x8c\xa0" // U+e320
#define ICON_MS_LAPTOP_WINDOWS "\xee\x8c\xa1" // U+e321
#define ICON_MS_LASSO_SELECT "\xee\xac\x83" // U+eb03
#define ICON_MS_LAST_PAGE "\xee\x97\x9d" // U+e5dd
#define ICON_MS_LAUNCH "\xee\xa2\x9e" // U+e89e
#define ICON_MS_LAUNDRY "\xee\x8a\xa8" // U+e2a8
#define ICON_MS_LAYERS "\xee\x94\xbb" // U+e53b
#define ICON_MS_LAYERS_CLEAR "\xee\x94\xbc" // U+e53c
#define ICON_MS_LDA "\xee\x84\x86" // U+e106
#define ICON_MS_LEADERBOARD "\xef\x88\x8c" // U+f20c
#define ICON_MS_LEAK_ADD "\xee\x8f\xb8" // U+e3f8
#define ICON_MS_LEAK_REMOVE "\xee\x8f\xb9" // U+e3f9
#define ICON_MS_LEFT_CLICK "\xef\x9c\x98" // U+f718
#define ICON_MS_LEFT_PANEL_CLOSE "\xef\x9c\x97" // U+f717
#define ICON_MS_LEFT_PANEL_OPEN "\xef\x9c\x96" // U+f716
#define ICON_MS_LEGEND_TOGGLE "\xef\x84\x9b" // U+f11b
#define ICON_MS_LENS "\xee\x8f\xba" // U+e3fa
#define ICON_MS_LENS_BLUR "\xef\x80\xa9" // U+f029
#define ICON_MS_LETTER_SWITCH "\xef\x9d\x98" // U+f758
#define ICON_MS_LIBRARY_ADD "\xee\x80\xbc" // U+e03c
#define ICON_MS_LIBRARY_ADD_CHECK "\xee\xa6\xb7" // U+e9b7
#define ICON_MS_LIBRARY_BOOKS "\xee\x80\xaf" // U+e02f
#define ICON_MS_LIBRARY_MUSIC "\xee\x80\xb0" // U+e030
#define ICON_MS_LICENSE "\xee\xac\x84" // U+eb04
#define ICON_MS_LIFT_TO_TALK "\xee\xbe\xa3" // U+efa3
#define ICON_MS_LIGHT "\xef\x80\xaa" // U+f02a
#define ICON_MS_LIGHT_GROUP "\xee\x8a\x8b" // U+e28b
#define ICON_MS_LIGHT_MODE "\xee\x94\x98" // U+e518
#define ICON_MS_LIGHT_OFF "\xee\xa6\xb8" // U+e9b8
#define ICON_MS_LIGHTBULB "\xee\xa4\x8f" // U+e90f
#define ICON_MS_LIGHTBULB_CIRCLE "\xee\xaf\xbe" // U+ebfe
#define ICON_MS_LIGHTBULB_OUTLINE "\xee\xa4\x8f" // U+e90f
#define ICON_MS_LIGHTNING_STAND "\xee\xbe\xa4" // U+efa4
#define ICON_MS_LINE_AXIS "\xee\xaa\x9a" // U+ea9a
#define ICON_MS_LINE_CURVE "\xef\x9d\x97" // U+f757
#define ICON_MS_LINE_END "\xef\xa0\xa6" // U+f826
#define ICON_MS_LINE_END_ARROW "\xef\xa0\x9d" // U+f81d
#define ICON_MS_LINE_END_ARROW_NOTCH "\xef\xa0\x9c" // U+f81c
#define ICON_MS_LINE_END_CIRCLE "\xef\xa0\x9b" // U+f81b
#define ICON_MS_LINE_END_DIAMOND "\xef\xa0\x9a" // U+f81a
#define ICON_MS_LINE_END_SQUARE "\xef\xa0\x99" // U+f819
#define ICON_MS_LINE_START "\xef\xa0\xa5" // U+f825
#define ICON_MS_LINE_START_ARROW "\xef\xa0\x98" // U+f818
#define ICON_MS_LINE_START_ARROW_NOTCH "\xef\xa0\x97" // U+f817
#define ICON_MS_LINE_START_CIRCLE "\xef\xa0\x96" // U+f816
#define ICON_MS_LINE_START_DIAMOND "\xef\xa0\x95" // U+f815
#define ICON_MS_LINE_START_SQUARE "\xef\xa0\x94" // U+f814
#define ICON_MS_LINE_STYLE "\xee\xa4\x99" // U+e919
#define ICON_MS_LINE_WEIGHT "\xee\xa4\x9a" // U+e91a
#define ICON_MS_LINEAR_SCALE "\xee\x89\xa0" // U+e260
#define ICON_MS_LINK "\xee\x89\x90" // U+e250
#define ICON_MS_LINK_OFF "\xee\x85\xaf" // U+e16f
#define ICON_MS_LINKED_CAMERA "\xee\x90\xb8" // U+e438
#define ICON_MS_LINKED_SERVICES "\xef\x94\xb5" // U+f535
#define ICON_MS_LIQUOR "\xee\xa9\xa0" // U+ea60
#define ICON_MS_LIST "\xee\xa2\x96" // U+e896
#define ICON_MS_LIST_ALT "\xee\x83\xae" // U+e0ee
#define ICON_MS_LIST_ALT_ADD "\xef\x9d\x96" // U+f756
#define ICON_MS_LISTS "\xee\xa6\xb9" // U+e9b9
#define ICON_MS_LIVE_HELP "\xee\x83\x86" // U+e0c6
#define ICON_MS_LIVE_TV "\xee\x98\xba" // U+e63a
#define ICON_MS_LIVING "\xef\x80\xab" // U+f02b
#define ICON_MS_LOCAL_ACTIVITY "\xee\x95\x93" // U+e553
#define ICON_MS_LOCAL_AIRPORT "\xee\x94\xbd" // U+e53d
#define ICON_MS_LOCAL_ATM "\xee\x94\xbe" // U+e53e
#define ICON_MS_LOCAL_BAR "\xee\x95\x80" // U+e540
#define ICON_MS_LOCAL_CAFE "\xee\xad\x84" // U+eb44
#define ICON_MS_LOCAL_CAR_WASH "\xee\x95\x82" // U+e542
#define ICON_MS_LOCAL_CONVENIENCE_STORE "\xee\x95\x83" // U+e543
#define ICON_MS_LOCAL_DINING "\xee\x95\xa1" // U+e561
#define ICON_MS_LOCAL_DRINK "\xee\x95\x84" // U+e544
#define ICON_MS_LOCAL_FIRE_DEPARTMENT "\xee\xbd\x95" // U+ef55
#define ICON_MS_LOCAL_FLORIST "\xee\x95\x85" // U+e545
#define ICON_MS_LOCAL_GAS_STATION "\xee\x95\x86" // U+e546
#define ICON_MS_LOCAL_GROCERY_STORE "\xee\xa3\x8c" // U+e8cc
#define ICON_MS_LOCAL_HOSPITAL "\xee\x95\x88" // U+e548
#define ICON_MS_LOCAL_HOTEL "\xee\x95\x89" // U+e549
#define ICON_MS_LOCAL_LAUNDRY_SERVICE "\xee\x95\x8a" // U+e54a
#define ICON_MS_LOCAL_LIBRARY "\xee\x95\x8b" // U+e54b
#define ICON_MS_LOCAL_MALL "\xee\x95\x8c" // U+e54c
#define ICON_MS_LOCAL_MOVIES "\xee\xa3\x9a" // U+e8da
#define ICON_MS_LOCAL_OFFER "\xef\x81\x9b" // U+f05b
#define ICON_MS_LOCAL_PARKING "\xee\x95\x8f" // U+e54f
#define ICON_MS_LOCAL_PHARMACY "\xee\x95\x90" // U+e550
#define ICON_MS_LOCAL_PHONE "\xef\x83\x94" // U+f0d4
#define ICON_MS_LOCAL_PIZZA "\xee\x95\x92" // U+e552
#define ICON_MS_LOCAL_PLAY "\xee\x95\x93" // U+e553
#define ICON_MS_LOCAL_POLICE "\xee\xbd\x96" // U+ef56
#define ICON_MS_LOCAL_POST_OFFICE "\xee\x95\x94" // U+e554
#define ICON_MS_LOCAL_PRINTSHOP "\xee\xa2\xad" // U+e8ad
#define ICON_MS_LOCAL_SEE "\xee\x95\x97" // U+e557
#define ICON_MS_LOCAL_SHIPPING "\xee\x95\x98" // U+e558
#define ICON_MS_LOCAL_TAXI "\xee\x95\x99" // U+e559
#define ICON_MS_LOCATION_AUTOMATION "\xef\x85\x8f" // U+f14f
#define ICON_MS_LOCATION_AWAY "\xef\x85\x90" // U+f150
#define ICON_MS_LOCATION_CHIP "\xef\xa1\x90" // U+f850
#define ICON_MS_LOCATION_CITY "\xee\x9f\xb1" // U+e7f1
#define ICON_MS_LOCATION_DISABLED "\xee\x86\xb6" // U+e1b6
#define ICON_MS_LOCATION_HOME "\xef\x85\x92" // U+f152
#define ICON_MS_LOCATION_OFF "\xee\x83\x87" // U+e0c7
#define ICON_MS_LOCATION_ON "\xef\x87\x9b" // U+f1db
#define ICON_MS_LOCATION_PIN "\xef\x87\x9b" // U+f1db
#define ICON_MS_LOCATION_SEARCHING "\xee\x86\xb7" // U+e1b7
#define ICON_MS_LOCATOR_TAG "\xef\xa3\x81" // U+f8c1
#define ICON_MS_LOCK "\xee\xa2\x99" // U+e899
#define ICON_MS_LOCK_CLOCK "\xee\xbd\x97" // U+ef57
#define ICON_MS_LOCK_OPEN "\xee\xa2\x98" // U+e898
#define ICON_MS_LOCK_OPEN_RIGHT "\xef\x99\x96" // U+f656
#define ICON_MS_LOCK_OUTLINE "\xee\xa2\x99" // U+e899
#define ICON_MS_LOCK_PERSON "\xef\xa3\xb3" // U+f8f3
#define ICON_MS_LOCK_RESET "\xee\xab\x9e" // U+eade
#define ICON_MS_LOGIN "\xee\xa9\xb7" // U+ea77
#define ICON_MS_LOGO_DEV "\xee\xab\x96" // U+ead6
#define ICON_MS_LOGOUT "\xee\xa6\xba" // U+e9ba
#define ICON_MS_LOOKS "\xee\x8f\xbc" // U+e3fc
#define ICON_MS_LOOKS_3 "\xee\x8f\xbb" // U+e3fb
#define ICON_MS_LOOKS_4 "\xee\x8f\xbd" // U+e3fd
#define ICON_MS_LOOKS_5 "\xee\x8f\xbe" // U+e3fe
#define ICON_MS_LOOKS_6 "\xee\x8f\xbf" // U+e3ff
#define ICON_MS_LOOKS_ONE "\xee\x90\x80" // U+e400
#define ICON_MS_LOOKS_TWO "\xee\x90\x81" // U+e401
#define ICON_MS_LOOP "\xee\xa1\xa3" // U+e863
#define ICON_MS_LOUPE "\xee\x90\x82" // U+e402
#define ICON_MS_LOW_DENSITY "\xef\x9e\x9b" // U+f79b
#define ICON_MS_LOW_PRIORITY "\xee\x85\xad" // U+e16d
#define ICON_MS_LOWERCASE "\xef\x92\x8a" // U+f48a
#define ICON_MS_LOYALTY "\xee\xa2\x9a" // U+e89a
#define ICON_MS_LTE_MOBILEDATA "\xef\x80\xac" // U+f02c
#define ICON_MS_LTE_MOBILEDATA_BADGE "\xef\x9f\x99" // U+f7d9
#define ICON_MS_LTE_PLUS_MOBILEDATA "\xef\x80\xad" // U+f02d
#define ICON_MS_LTE_PLUS_MOBILEDATA_BADGE "\xef\x9f\x98" // U+f7d8
#define ICON_MS_LUGGAGE "\xef\x88\xb5" // U+f235
#define ICON_MS_LUNCH_DINING "\xee\xa9\xa1" // U+ea61
#define ICON_MS_LYRICS "\xee\xb0\x8b" // U+ec0b
#define ICON_MS_MACRO_AUTO "\xef\x9b\xb2" // U+f6f2
#define ICON_MS_MACRO_OFF "\xef\xa3\x92" // U+f8d2
#define ICON_MS_MAGIC_BUTTON "\xef\x84\xb6" // U+f136
#define ICON_MS_MAGIC_EXCHANGE "\xef\x9f\xb4" // U+f7f4
#define ICON_MS_MAGIC_TETHER "\xef\x9f\x97" // U+f7d7
#define ICON_MS_MAGNIFICATION_LARGE "\xef\xa0\xbd" // U+f83d
#define ICON_MS_MAGNIFICATION_SMALL "\xef\xa0\xbc" // U+f83c
#define ICON_MS_MAGNIFY_DOCKED "\xef\x9f\x96" // U+f7d6
#define ICON_MS_MAGNIFY_FULLSCREEN "\xef\x9f\x95" // U+f7d5
#define ICON_MS_MAIL "\xee\x85\x99" // U+e159
#define ICON_MS_MAIL_LOCK "\xee\xb0\x8a" // U+ec0a
#define ICON_MS_MAIL_OFF "\xef\x92\x8b" // U+f48b
#define ICON_MS_MAIL_OUTLINE "\xee\x85\x99" // U+e159
#define ICON_MS_MALE "\xee\x96\x8e" // U+e58e
#define ICON_MS_MAN "\xee\x93\xab" // U+e4eb
#define ICON_MS_MAN_2 "\xef\xa3\xa1" // U+f8e1
#define ICON_MS_MAN_3 "\xef\xa3\xa2" // U+f8e2
#define ICON_MS_MAN_4 "\xef\xa3\xa3" // U+f8e3
#define ICON_MS_MANAGE_ACCOUNTS "\xef\x80\xae" // U+f02e
#define ICON_MS_MANAGE_HISTORY "\xee\xaf\xa7" // U+ebe7
#define ICON_MS_MANAGE_SEARCH "\xef\x80\xaf" // U+f02f
#define ICON_MS_MANGA "\xef\x97\xa3" // U+f5e3
#define ICON_MS_MANUFACTURING "\xee\x9c\xa6" // U+e726
#define ICON_MS_MAP "\xee\x95\x9b" // U+e55b
#define ICON_MS_MAPS_HOME_WORK "\xef\x80\xb0" // U+f030
#define ICON_MS_MAPS_UGC "\xee\xbd\x98" // U+ef58
#define ICON_MS_MARGIN "\xee\xa6\xbb" // U+e9bb
#define ICON_MS_MARK_AS_UNREAD "\xee\xa6\xbc" // U+e9bc
#define ICON_MS_MARK_CHAT_READ "\xef\x86\x8b" // U+f18b
#define ICON_MS_MARK_CHAT_UNREAD "\xef\x86\x89" // U+f189
#define ICON_MS_MARK_EMAIL_READ "\xef\x86\x8c" // U+f18c
#define ICON_MS_MARK_EMAIL_UNREAD "\xef\x86\x8a" // U+f18a
#define ICON_MS_MARK_UNREAD_CHAT_ALT "\xee\xae\x9d" // U+eb9d
#define ICON_MS_MARKDOWN "\xef\x95\x92" // U+f552
#define ICON_MS_MARKDOWN_COPY "\xef\x95\x93" // U+f553
#define ICON_MS_MARKDOWN_PASTE "\xef\x95\x94" // U+f554
#define ICON_MS_MARKUNREAD "\xee\x85\x99" // U+e159
#define ICON_MS_MARKUNREAD_MAILBOX "\xee\xa2\x9b" // U+e89b
#define ICON_MS_MASKED_TRANSITIONS "\xee\x9c\xae" // U+e72e
#define ICON_MS_MASKS "\xef\x88\x98" // U+f218
#define ICON_MS_MATCH_CASE "\xef\x9b\xb1" // U+f6f1
#define ICON_MS_MATCH_WORD "\xef\x9b\xb0" // U+f6f0
#define ICON_MS_MATTER "\xee\xa4\x87" // U+e907
#define ICON_MS_MAXIMIZE "\xee\xa4\xb0" // U+e930
#define ICON_MS_MEASURING_TAPE "\xef\x9a\xaf" // U+f6af
#define ICON_MS_MEDIA_BLUETOOTH_OFF "\xef\x80\xb1" // U+f031
#define ICON_MS_MEDIA_BLUETOOTH_ON "\xef\x80\xb2" // U+f032
#define ICON_MS_MEDIA_LINK "\xef\xa0\xbf" // U+f83f
#define ICON_MS_MEDIA_OUTPUT "\xef\x93\xb2" // U+f4f2
#define ICON_MS_MEDIA_OUTPUT_OFF "\xef\x93\xb3" // U+f4f3
#define ICON_MS_MEDIATION "\xee\xbe\xa7" // U+efa7
#define ICON_MS_MEDICAL_INFORMATION "\xee\xaf\xad" // U+ebed
#define ICON_MS_MEDICAL_MASK "\xef\xa0\x8a" // U+f80a
#define ICON_MS_MEDICAL_SERVICES "\xef\x84\x89" // U+f109
#define ICON_MS_MEDICATION "\xef\x80\xb3" // U+f033
#define ICON_MS_MEDICATION_LIQUID "\xee\xaa\x87" // U+ea87
#define ICON_MS_MEETING_ROOM "\xee\xad\x8f" // U+eb4f
#define ICON_MS_MEMORY "\xee\x8c\xa2" // U+e322
#define ICON_MS_MEMORY_ALT "\xef\x9e\xa3" // U+f7a3
#define ICON_MS_MENSTRUAL_HEALTH "\xef\x9b\xa1" // U+f6e1
#define ICON_MS_MENU "\xee\x97\x92" // U+e5d2
#define ICON_MS_MENU_BOOK "\xee\xa8\x99" // U+ea19
#define ICON_MS_MENU_OPEN "\xee\xa6\xbd" // U+e9bd
#define ICON_MS_MERGE "\xee\xae\x98" // U+eb98
#define ICON_MS_MERGE_TYPE "\xee\x89\x92" // U+e252
#define ICON_MS_MESSAGE "\xee\x83\x89" // U+e0c9
#define ICON_MS_METABOLISM "\xee\x84\x8b" // U+e10b
#define ICON_MS_MFG_NEST_YALE_LOCK "\xef\x84\x9d" // U+f11d
#define ICON_MS_MIC "\xee\x8c\x9d" // U+e31d
#define ICON_MS_MIC_DOUBLE "\xef\x97\x91" // U+f5d1
#define ICON_MS_MIC_EXTERNAL_OFF "\xee\xbd\x99" // U+ef59
#define ICON_MS_MIC_EXTERNAL_ON "\xee\xbd\x9a" // U+ef5a
#define ICON_MS_MIC_NONE "\xee\x8c\x9d" // U+e31d
#define ICON_MS_MIC_OFF "\xee\x80\xab" // U+e02b
#define ICON_MS_MICROBIOLOGY "\xee\x84\x8c" // U+e10c
#define ICON_MS_MICROWAVE "\xef\x88\x84" // U+f204
#define ICON_MS_MICROWAVE_GEN "\xee\xa1\x87" // U+e847
#define ICON_MS_MILITARY_TECH "\xee\xa8\xbf" // U+ea3f
#define ICON_MS_MIMO "\xee\xa6\xbe" // U+e9be
#define ICON_MS_MIMO_DISCONNECT "\xee\xa6\xbf" // U+e9bf
#define ICON_MS_MINDFULNESS "\xef\x9b\xa0" // U+f6e0
#define ICON_MS_MINIMIZE "\xee\xa4\xb1" // U+e931
#define ICON_MS_MINOR_CRASH "\xee\xaf\xb1" // U+ebf1
#define ICON_MS_MINTMARK "\xee\xbe\xa9" // U+efa9
#define ICON_MS_MISSED_VIDEO_CALL "\xef\x83\x8e" // U+f0ce
#define ICON_MS_MISSED_VIDEO_CALL_FILLED "\xef\x83\x8e" // U+f0ce
#define ICON_MS_MISSING_CONTROLLER "\xee\x9c\x81" // U+e701
#define ICON_MS_MIST "\xee\x86\x88" // U+e188
#define ICON_MS_MITRE "\xef\x95\x87" // U+f547
#define ICON_MS_MIXTURE_MED "\xee\x93\x88" // U+e4c8
#define ICON_MS_MMS "\xee\x98\x98" // U+e618
#define ICON_MS_MOBILE_FRIENDLY "\xee\x88\x80" // U+e200
#define ICON_MS_MOBILE_OFF "\xee\x88\x81" // U+e201
#define ICON_MS_MOBILE_SCREEN_SHARE "\xee\x83\xa7" // U+e0e7
#define ICON_MS_MOBILEDATA_OFF "\xef\x80\xb4" // U+f034
#define ICON_MS_MODE "\xef\x82\x97" // U+f097
#define ICON_MS_MODE_COMMENT "\xee\x89\x93" // U+e253
#define ICON_MS_MODE_COOL "\xef\x85\xa6" // U+f166
#define ICON_MS_MODE_COOL_OFF "\xef\x85\xa7" // U+f167
#define ICON_MS_MODE_DUAL "\xef\x95\x97" // U+f557
#define ICON_MS_MODE_EDIT "\xef\x82\x97" // U+f097
#define ICON_MS_MODE_EDIT_OUTLINE "\xef\x82\x97" // U+f097
#define ICON_MS_MODE_FAN "\xef\x85\xa8" // U+f168
#define ICON_MS_MODE_FAN_OFF "\xee\xb0\x97" // U+ec17
#define ICON_MS_MODE_HEAT "\xef\x85\xaa" // U+f16a
#define ICON_MS_MODE_HEAT_COOL "\xef\x85\xab" // U+f16b
#define ICON_MS_MODE_HEAT_OFF "\xef\x85\xad" // U+f16d
#define ICON_MS_MODE_NIGHT "\xef\x80\xb6" // U+f036
#define ICON_MS_MODE_OF_TRAVEL "\xee\x9f\x8e" // U+e7ce
#define ICON_MS_MODE_OFF_ON "\xef\x85\xaf" // U+f16f
#define ICON_MS_MODE_STANDBY "\xef\x80\xb7" // U+f037
#define ICON_MS_MODEL_TRAINING "\xef\x83\x8f" // U+f0cf
#define ICON_MS_MONETIZATION_ON "\xee\x89\xa3" // U+e263
#define ICON_MS_MONEY "\xee\x95\xbd" // U+e57d
#define ICON_MS_MONEY_OFF "\xef\x80\xb8" // U+f038
#define ICON_MS_MONEY_OFF_CSRED "\xef\x80\xb8" // U+f038
#define ICON_MS_MONITOR "\xee\xbd\x9b" // U+ef5b
#define ICON_MS_MONITOR_HEART "\xee\xaa\xa2" // U+eaa2
#define ICON_MS_MONITOR_WEIGHT "\xef\x80\xb9" // U+f039
#define ICON_MS_MONITOR_WEIGHT_GAIN "\xef\x9b\x9f" // U+f6df
#define ICON_MS_MONITOR_WEIGHT_LOSS "\xef\x9b\x9e" // U+f6de
#define ICON_MS_MONITORING "\xef\x86\x90" // U+f190
#define ICON_MS_MONOCHROME_PHOTOS "\xee\x90\x83" // U+e403
#define ICON_MS_MOOD "\xee\xa8\xa2" // U+ea22
#define ICON_MS_MOOD_BAD "\xee\x9f\xb3" // U+e7f3
#define ICON_MS_MOP "\xee\x8a\x8d" // U+e28d
#define ICON_MS_MORE "\xee\x98\x99" // U+e619
#define ICON_MS_MORE_DOWN "\xef\x86\x96" // U+f196
#define ICON_MS_MORE_HORIZ "\xee\x97\x93" // U+e5d3
#define ICON_MS_MORE_TIME "\xee\xa9\x9d" // U+ea5d
#define ICON_MS_MORE_UP "\xef\x86\x97" // U+f197
#define ICON_MS_MORE_VERT "\xee\x97\x94" // U+e5d4
#define ICON_MS_MOSQUE "\xee\xaa\xb2" // U+eab2
#define ICON_MS_MOTION_BLUR "\xef\x83\x90" // U+f0d0
#define ICON_MS_MOTION_MODE "\xef\xa1\x82" // U+f842
#define ICON_MS_MOTION_PHOTOS_AUTO "\xef\x80\xba" // U+f03a
#define ICON_MS_MOTION_PHOTOS_OFF "\xee\xa7\x80" // U+e9c0
#define ICON_MS_MOTION_PHOTOS_ON "\xee\xa7\x81" // U+e9c1
#define ICON_MS_MOTION_PHOTOS_PAUSE "\xef\x88\xa7" // U+f227
#define ICON_MS_MOTION_PHOTOS_PAUSED "\xef\x88\xa7" // U+f227
#define ICON_MS_MOTION_SENSOR_ACTIVE "\xee\x9e\x92" // U+e792
#define ICON_MS_MOTION_SENSOR_ALERT "\xee\x9e\x84" // U+e784
#define ICON_MS_MOTION_SENSOR_IDLE "\xee\x9e\x83" // U+e783
#define ICON_MS_MOTION_SENSOR_URGENT "\xee\x9e\x8e" // U+e78e
#define ICON_MS_MOTORCYCLE "\xee\xa4\x9b" // U+e91b
#define ICON_MS_MOUNTAIN_FLAG "\xef\x97\xa2" // U+f5e2
#define ICON_MS_MOUSE "\xee\x8c\xa3" // U+e323
#define ICON_MS_MOUSE_LOCK "\xef\x92\x90" // U+f490
#define ICON_MS_MOUSE_LOCK_OFF "\xef\x92\x8f" // U+f48f
#define ICON_MS_MOVE "\xee\x9d\x80" // U+e740
#define ICON_MS_MOVE_DOWN "\xee\xad\xa1" // U+eb61
#define ICON_MS_MOVE_GROUP "\xef\x9c\x95" // U+f715
#define ICON_MS_MOVE_ITEM "\xef\x87\xbf" // U+f1ff
#define ICON_MS_MOVE_LOCATION "\xee\x9d\x81" // U+e741
#define ICON_MS_MOVE_SELECTION_DOWN "\xef\x9c\x94" // U+f714
#define ICON_MS_MOVE_SELECTION_LEFT "\xef\x9c\x93" // U+f713
#define ICON_MS_MOVE_SELECTION_RIGHT "\xef\x9c\x92" // U+f712
#define ICON_MS_MOVE_SELECTION_UP "\xef\x9c\x91" // U+f711
#define ICON_MS_MOVE_TO_INBOX "\xee\x85\xa8" // U+e168
#define ICON_MS_MOVE_UP "\xee\xad\xa4" // U+eb64
#define ICON_MS_MOVED_LOCATION "\xee\x96\x94" // U+e594
#define ICON_MS_MOVIE "\xee\x90\x84" // U+e404
#define ICON_MS_MOVIE_CREATION "\xee\x90\x84" // U+e404
#define ICON_MS_MOVIE_EDIT "\xef\xa1\x80" // U+f840
#define ICON_MS_MOVIE_FILTER "\xee\x90\xba" // U+e43a
#define ICON_MS_MOVIE_INFO "\xee\x80\xad" // U+e02d
#define ICON_MS_MOVIE_OFF "\xef\x92\x99" // U+f499
#define ICON_MS_MOVING "\xee\x94\x81" // U+e501
#define ICON_MS_MOVING_BEDS "\xee\x9c\xbd" // U+e73d
#define ICON_MS_MOVING_MINISTRY "\xee\x9c\xbe" // U+e73e
#define ICON_MS_MP "\xee\xa7\x83" // U+e9c3
#define ICON_MS_MULTICOOKER "\xee\x8a\x93" // U+e293
#define ICON_MS_MULTILINE_CHART "\xee\x9b\x9f" // U+e6df
#define ICON_MS_MULTIPLE_STOP "\xef\x86\xb9" // U+f1b9
#define ICON_MS_MUSEUM "\xee\xa8\xb6" // U+ea36
#define ICON_MS_MUSIC_CAST "\xee\xac\x9a" // U+eb1a
#define ICON_MS_MUSIC_NOTE "\xee\x90\x85" // U+e405
#define ICON_MS_MUSIC_OFF "\xee\x91\x80" // U+e440
#define ICON_MS_MUSIC_VIDEO "\xee\x81\xa3" // U+e063
#define ICON_MS_MY_LOCATION "\xee\x95\x9c" // U+e55c
#define ICON_MS_MYSTERY "\xef\x97\xa1" // U+f5e1
#define ICON_MS_NAT "\xee\xbd\x9c" // U+ef5c
#define ICON_MS_NATURE "\xee\x90\x86" // U+e406
#define ICON_MS_NATURE_PEOPLE "\xee\x90\x87" // U+e407
#define ICON_MS_NAVIGATE_BEFORE "\xee\x90\x88" // U+e408
#define ICON_MS_NAVIGATE_NEXT "\xee\x90\x89" // U+e409
#define ICON_MS_NAVIGATION "\xee\x95\x9d" // U+e55d
#define ICON_MS_NEAR_ME "\xee\x95\xa9" // U+e569
#define ICON_MS_NEAR_ME_DISABLED "\xef\x87\xaf" // U+f1ef
#define ICON_MS_NEARBY "\xee\x9a\xb7" // U+e6b7
#define ICON_MS_NEARBY_ERROR "\xef\x80\xbb" // U+f03b
#define ICON_MS_NEARBY_OFF "\xef\x80\xbc" // U+f03c
#define ICON_MS_NEPHROLOGY "\xee\x84\x8d" // U+e10d
#define ICON_MS_NEST_AUDIO "\xee\xae\xbf" // U+ebbf
#define ICON_MS_NEST_CAM_FLOODLIGHT "\xef\xa2\xb7" // U+f8b7
#define ICON_MS_NEST_CAM_INDOOR "\xef\x84\x9e" // U+f11e
#define ICON_MS_NEST_CAM_IQ "\xef\x84\x9f" // U+f11f
#define ICON_MS_NEST_CAM_IQ_OUTDOOR "\xef\x84\xa0" // U+f120
#define ICON_MS_NEST_CAM_MAGNET_MOUNT "\xef\xa2\xb8" // U+f8b8
#define ICON_MS_NEST_CAM_OUTDOOR "\xef\x84\xa1" // U+f121
#define ICON_MS_NEST_CAM_STAND "\xef\xa2\xb9" // U+f8b9
#define ICON_MS_NEST_CAM_WALL_MOUNT "\xef\xa2\xba" // U+f8ba
#define ICON_MS_NEST_CAM_WIRED_STAND "\xee\xb0\x96" // U+ec16
#define ICON_MS_NEST_CLOCK_FARSIGHT_ANALOG "\xef\xa2\xbb" // U+f8bb
#define ICON_MS_NEST_CLOCK_FARSIGHT_DIGITAL "\xef\xa2\xbc" // U+f8bc
#define ICON_MS_NEST_CONNECT "\xef\x84\xa2" // U+f122
#define ICON_MS_NEST_DETECT "\xef\x84\xa3" // U+f123
#define ICON_MS_NEST_DISPLAY "\xef\x84\xa4" // U+f124
#define ICON_MS_NEST_DISPLAY_MAX "\xef\x84\xa5" // U+f125
#define ICON_MS_NEST_DOORBELL_VISITOR "\xef\xa2\xbd" // U+f8bd
#define ICON_MS_NEST_ECO_LEAF "\xef\xa2\xbe" // U+f8be
#define ICON_MS_NEST_FARSIGHT_WEATHER "\xef\xa2\xbf" // U+f8bf
#define ICON_MS_NEST_FOUND_SAVINGS "\xef\xa3\x80" // U+f8c0
#define ICON_MS_NEST_GALE_WIFI "\xef\x95\xb9" // U+f579
#define ICON_MS_NEST_HEAT_LINK_E "\xef\x84\xa6" // U+f126
#define ICON_MS_NEST_HEAT_LINK_GEN_3 "\xef\x84\xa7" // U+f127
#define ICON_MS_NEST_HELLO_DOORBELL "\xee\xa0\xac" // U+e82c
#define ICON_MS_NEST_LOCATOR_TAG "\xef\xa3\x81" // U+f8c1
#define ICON_MS_NEST_MINI "\xee\x9e\x89" // U+e789
#define ICON_MS_NEST_MULTI_ROOM "\xef\xa3\x82" // U+f8c2
#define ICON_MS_NEST_PROTECT "\xee\x9a\x8e" // U+e68e
#define ICON_MS_NEST_REMOTE "\xef\x97\x9b" // U+f5db
#define ICON_MS_NEST_REMOTE_COMFORT_SENSOR "\xef\x84\xaa" // U+f12a
#define ICON_MS_NEST_SECURE_ALARM "\xef\x84\xab" // U+f12b
#define ICON_MS_NEST_SUNBLOCK "\xef\xa3\x83" // U+f8c3
#define ICON_MS_NEST_TAG "\xef\xa3\x81" // U+f8c1
#define ICON_MS_NEST_THERMOSTAT "\xee\x9a\x8f" // U+e68f
#define ICON_MS_NEST_THERMOSTAT_E_EU "\xef\x84\xad" // U+f12d
#define ICON_MS_NEST_THERMOSTAT_GEN_3 "\xef\x84\xae" // U+f12e
#define ICON_MS_NEST_THERMOSTAT_SENSOR "\xef\x84\xaf" // U+f12f
#define ICON_MS_NEST_THERMOSTAT_SENSOR_EU "\xef\x84\xb0" // U+f130
#define ICON_MS_NEST_THERMOSTAT_ZIRCONIUM_EU "\xef\x84\xb1" // U+f131
#define ICON_MS_NEST_TRUE_RADIANT "\xef\xa3\x84" // U+f8c4
#define ICON_MS_NEST_WAKE_ON_APPROACH "\xef\xa3\x85" // U+f8c5
#define ICON_MS_NEST_WAKE_ON_PRESS "\xef\xa3\x86" // U+f8c6
#define ICON_MS_NEST_WIFI_GALE "\xef\x84\xb2" // U+f132
#define ICON_MS_NEST_WIFI_MISTRAL "\xef\x84\xb3" // U+f133
#define ICON_MS_NEST_WIFI_POINT "\xef\x84\xb4" // U+f134
#define ICON_MS_NEST_WIFI_POINT_VENTO "\xef\x84\xb4" // U+f134
#define ICON_MS_NEST_WIFI_PRO "\xef\x95\xab" // U+f56b
#define ICON_MS_NEST_WIFI_PRO_2 "\xef\x95\xaa" // U+f56a
#define ICON_MS_NEST_WIFI_ROUTER "\xef\x84\xb3" // U+f133
#define ICON_MS_NETWORK_CELL "\xee\x86\xb9" // U+e1b9
#define ICON_MS_NETWORK_CHECK "\xee\x99\x80" // U+e640
#define ICON_MS_NETWORK_INTELLIGENCE_HISTORY "\xef\x97\xb6" // U+f5f6
#define ICON_MS_NETWORK_INTELLIGENCE_UPDATE "\xef\x97\xb5" // U+f5f5
#define ICON_MS_NETWORK_LOCKED "\xee\x98\x9a" // U+e61a
#define ICON_MS_NETWORK_MANAGE "\xef\x9e\xab" // U+f7ab
#define ICON_MS_NETWORK_NODE "\xef\x95\xae" // U+f56e
#define ICON_MS_NETWORK_PING "\xee\xaf\x8a" // U+ebca
#define ICON_MS_NETWORK_WIFI "\xee\x86\xba" // U+e1ba
#define ICON_MS_NETWORK_WIFI_1_BAR "\xee\xaf\xa4" // U+ebe4
#define ICON_MS_NETWORK_WIFI_1_BAR_LOCKED "\xef\x96\x8f" // U+f58f
#define ICON_MS_NETWORK_WIFI_2_BAR "\xee\xaf\x96" // U+ebd6
#define ICON_MS_NETWORK_WIFI_2_BAR_LOCKED "\xef\x96\x8e" // U+f58e
#define ICON_MS_NETWORK_WIFI_3_BAR "\xee\xaf\xa1" // U+ebe1
#define ICON_MS_NETWORK_WIFI_3_BAR_LOCKED "\xef\x96\x8d" // U+f58d
#define ICON_MS_NETWORK_WIFI_LOCKED "\xef\x94\xb2" // U+f532
#define ICON_MS_NEUROLOGY "\xee\x84\x8e" // U+e10e
#define ICON_MS_NEW_LABEL "\xee\x98\x89" // U+e609
#define ICON_MS_NEW_RELEASES "\xee\xbd\xb6" // U+ef76
#define ICON_MS_NEW_WINDOW "\xef\x9c\x90" // U+f710
#define ICON_MS_NEWS "\xee\x80\xb2" // U+e032
#define ICON_MS_NEWSMODE "\xee\xbe\xad" // U+efad
#define ICON_MS_NEWSPAPER "\xee\xae\x81" // U+eb81
#define ICON_MS_NEWSSTAND "\xee\xa7\x84" // U+e9c4
#define ICON_MS_NEXT_PLAN "\xee\xbd\x9d" // U+ef5d
#define ICON_MS_NEXT_WEEK "\xee\x85\xaa" // U+e16a
#define ICON_MS_NFC "\xee\x86\xbb" // U+e1bb
#define ICON_MS_NIGHT_SHELTER "\xef\x87\xb1" // U+f1f1
#define ICON_MS_NIGHT_SIGHT_AUTO "\xef\x87\x97" // U+f1d7
#define ICON_MS_NIGHT_SIGHT_AUTO_OFF "\xef\x87\xb9" // U+f1f9
#define ICON_MS_NIGHT_SIGHT_MAX "\xef\x9b\x83" // U+f6c3
#define ICON_MS_NIGHTLIFE "\xee\xa9\xa2" // U+ea62
#define ICON_MS_NIGHTLIGHT "\xef\x80\xbd" // U+f03d
#define ICON_MS_NIGHTLIGHT_ROUND "\xef\x80\xbd" // U+f03d
#define ICON_MS_NIGHTS_STAY "\xee\xa9\x86" // U+ea46
#define ICON_MS_NO_ACCOUNTS "\xef\x80\xbe" // U+f03e
#define ICON_MS_NO_ADULT_CONTENT "\xef\xa3\xbe" // U+f8fe
#define ICON_MS_NO_BACKPACK "\xef\x88\xb7" // U+f237
#define ICON_MS_NO_CRASH "\xee\xaf\xb0" // U+ebf0
#define ICON_MS_NO_DRINKS "\xef\x86\xa5" // U+f1a5
#define ICON_MS_NO_ENCRYPTION "\xef\x80\xbf" // U+f03f
#define ICON_MS_NO_ENCRYPTION_GMAILERRORRED "\xef\x80\xbf" // U+f03f
#define ICON_MS_NO_FLASH "\xef\x86\xa6" // U+f1a6
#define ICON_MS_NO_FOOD "\xef\x86\xa7" // U+f1a7
#define ICON_MS_NO_LUGGAGE "\xef\x88\xbb" // U+f23b
#define ICON_MS_NO_MEALS "\xef\x87\x96" // U+f1d6
#define ICON_MS_NO_MEETING_ROOM "\xee\xad\x8e" // U+eb4e
#define ICON_MS_NO_PHOTOGRAPHY "\xef\x86\xa8" // U+f1a8
#define ICON_MS_NO_SIM "\xee\x87\x8e" // U+e1ce
#define ICON_MS_NO_SOUND "\xee\x9c\x90" // U+e710
#define ICON_MS_NO_STROLLER "\xef\x86\xaf" // U+f1af
#define ICON_MS_NO_TRANSFER "\xef\x87\x95" // U+f1d5
#define ICON_MS_NOISE_AWARE "\xee\xaf\xac" // U+ebec
#define ICON_MS_NOISE_CONTROL_OFF "\xee\xaf\xb3" // U+ebf3
#define ICON_MS_NOISE_CONTROL_ON "\xef\xa2\xa8" // U+f8a8
#define ICON_MS_NORDIC_WALKING "\xee\x94\x8e" // U+e50e
#define ICON_MS_NORTH "\xef\x87\xa0" // U+f1e0
#define ICON_MS_NORTH_EAST "\xef\x87\xa1" // U+f1e1
#define ICON_MS_NORTH_WEST "\xef\x87\xa2" // U+f1e2
#define ICON_MS_NOT_ACCESSIBLE "\xef\x83\xbe" // U+f0fe
#define ICON_MS_NOT_ACCESSIBLE_FORWARD "\xef\x95\x8a" // U+f54a
#define ICON_MS_NOT_INTERESTED "\xef\x82\x8c" // U+f08c
#define ICON_MS_NOT_LISTED_LOCATION "\xee\x95\xb5" // U+e575
#define ICON_MS_NOT_STARTED "\xef\x83\x91" // U+f0d1
#define ICON_MS_NOTE "\xee\x99\xad" // U+e66d
#define ICON_MS_NOTE_ADD "\xee\xa2\x9c" // U+e89c
#define ICON_MS_NOTE_ALT "\xef\x81\x80" // U+f040
#define ICON_MS_NOTE_STACK "\xef\x95\xa2" // U+f562
#define ICON_MS_NOTE_STACK_ADD "\xef\x95\xa3" // U+f563
#define ICON_MS_NOTES "\xee\x89\xac" // U+e26c
#define ICON_MS_NOTIFICATION_ADD "\xee\x8e\x99" // U+e399
#define ICON_MS_NOTIFICATION_IMPORTANT "\xee\x80\x84" // U+e004
#define ICON_MS_NOTIFICATION_MULTIPLE "\xee\x9b\x82" // U+e6c2
#define ICON_MS_NOTIFICATIONS "\xee\x9f\xb5" // U+e7f5
#define ICON_MS_NOTIFICATIONS_ACTIVE "\xee\x9f\xb7" // U+e7f7
#define ICON_MS_NOTIFICATIONS_NONE "\xee\x9f\xb5" // U+e7f5
#define ICON_MS_NOTIFICATIONS_OFF "\xee\x9f\xb6" // U+e7f6
#define ICON_MS_NOTIFICATIONS_PAUSED "\xee\x9f\xb8" // U+e7f8
#define ICON_MS_NOTIFICATIONS_UNREAD "\xef\x93\xbe" // U+f4fe
#define ICON_MS_NUMBERS "\xee\xab\x87" // U+eac7
#define ICON_MS_NUTRITION "\xee\x84\x90" // U+e110
#define ICON_MS_ODS "\xee\x9b\xa8" // U+e6e8
#define ICON_MS_ODT "\xee\x9b\xa9" // U+e6e9
#define ICON_MS_OFFLINE_BOLT "\xee\xa4\xb2" // U+e932
#define ICON_MS_OFFLINE_PIN "\xee\xa4\x8a" // U+e90a
#define ICON_MS_OFFLINE_PIN_OFF "\xef\x93\x90" // U+f4d0
#define ICON_MS_OFFLINE_SHARE "\xee\xa7\x85" // U+e9c5
#define ICON_MS_OIL_BARREL "\xee\xb0\x95" // U+ec15
#define ICON_MS_ON_DEVICE_TRAINING "\xee\xaf\xbd" // U+ebfd
#define ICON_MS_ON_HUB_DEVICE "\xee\x9b\x83" // U+e6c3
#define ICON_MS_ONCOLOGY "\xee\x84\x94" // U+e114
#define ICON_MS_ONDEMAND_VIDEO "\xee\x98\xba" // U+e63a
#define ICON_MS_ONLINE_PREDICTION "\xef\x83\xab" // U+f0eb
#define ICON_MS_ONSEN "\xef\x9b\xb8" // U+f6f8
#define ICON_MS_OPACITY "\xee\xa4\x9c" // U+e91c
#define ICON_MS_OPEN_IN_BROWSER "\xee\xa2\x9d" // U+e89d
#define ICON_MS_OPEN_IN_FULL "\xef\x87\x8e" // U+f1ce
#define ICON_MS_OPEN_IN_NEW "\xee\xa2\x9e" // U+e89e
#define ICON_MS_OPEN_IN_NEW_DOWN "\xef\x9c\x8f" // U+f70f
#define ICON_MS_OPEN_IN_NEW_OFF "\xee\x93\xb6" // U+e4f6
#define ICON_MS_OPEN_IN_PHONE "\xee\x9c\x82" // U+e702
#define ICON_MS_OPEN_JAM "\xee\xbe\xae" // U+efae
#define ICON_MS_OPEN_RUN "\xef\x92\xb7" // U+f4b7
#define ICON_MS_OPEN_WITH "\xee\xa2\x9f" // U+e89f
#define ICON_MS_OPHTHALMOLOGY "\xee\x84\x95" // U+e115
#define ICON_MS_ORAL_DISEASE "\xee\x84\x96" // U+e116
#define ICON_MS_ORDER_APPROVE "\xef\xa0\x92" // U+f812
#define ICON_MS_ORDER_PLAY "\xef\xa0\x91" // U+f811
#define ICON_MS_ORDERS "\xee\xac\x94" // U+eb14
#define ICON_MS_ORTHOPEDICS "\xef\xa2\x97" // U+f897
#define ICON_MS_OTHER_ADMISSION "\xee\x91\xbb" // U+e47b
#define ICON_MS_OTHER_HOUSES "\xee\x96\x8c" // U+e58c
#define ICON_MS_OUTBOUND "\xee\x87\x8a" // U+e1ca
#define ICON_MS_OUTBOX "\xee\xbd\x9f" // U+ef5f
#define ICON_MS_OUTBOX_ALT "\xee\xac\x97" // U+eb17
#define ICON_MS_OUTDOOR_GARDEN "\xee\x88\x85" // U+e205
#define ICON_MS_OUTDOOR_GRILL "\xee\xa9\x87" // U+ea47
#define ICON_MS_OUTGOING_MAIL "\xef\x83\x92" // U+f0d2
#define ICON_MS_OUTLET "\xef\x87\x94" // U+f1d4
#define ICON_MS_OUTLINED_FLAG "\xef\x83\x86" // U+f0c6
#define ICON_MS_OUTPATIENT "\xee\x84\x98" // U+e118
#define ICON_MS_OUTPATIENT_MED "\xee\x84\x99" // U+e119
#define ICON_MS_OUTPUT "\xee\xae\xbe" // U+ebbe
#define ICON_MS_OUTPUT_CIRCLE "\xef\x9c\x8e" // U+f70e
#define ICON_MS_OVEN "\xee\xa7\x87" // U+e9c7
#define ICON_MS_OVEN_GEN "\xee\xa1\x83" // U+e843
#define ICON_MS_OVERVIEW "\xee\x92\xa7" // U+e4a7
#define ICON_MS_OVERVIEW_KEY "\xef\x9f\x94" // U+f7d4
#define ICON_MS_OXYGEN_SATURATION "\xee\x93\x9e" // U+e4de
#define ICON_MS_P2P "\xef\x94\xaa" // U+f52a
#define ICON_MS_PACE "\xef\x9a\xb8" // U+f6b8
#define ICON_MS_PACEMAKER "\xee\x99\x96" // U+e656
#define ICON_MS_PACKAGE "\xee\x92\x8f" // U+e48f
#define ICON_MS_PACKAGE_2 "\xef\x95\xa9" // U+f569
#define ICON_MS_PADDING "\xee\xa7\x88" // U+e9c8
#define ICON_MS_PAGE_CONTROL "\xee\x9c\xb1" // U+e731
#define ICON_MS_PAGE_INFO "\xef\x98\x94" // U+f614
#define ICON_MS_PAGELESS "\xef\x94\x89" // U+f509
#define ICON_MS_PAGES "\xee\x9f\xb9" // U+e7f9
#define ICON_MS_PAGEVIEW "\xee\xa2\xa0" // U+e8a0
#define ICON_MS_PAID "\xef\x81\x81" // U+f041
#define ICON_MS_PALETTE "\xee\x90\x8a" // U+e40a
#define ICON_MS_PALLET "\xef\xa1\xaa" // U+f86a
#define ICON_MS_PAN_TOOL "\xee\xa4\xa5" // U+e925
#define ICON_MS_PAN_TOOL_ALT "\xee\xae\xb9" // U+ebb9
#define ICON_MS_PAN_ZOOM "\xef\x99\x95" // U+f655
#define ICON_MS_PANORAMA "\xee\x90\x8b" // U+e40b
#define ICON_MS_PANORAMA_FISH_EYE "\xee\x90\x8c" // U+e40c
#define ICON_MS_PANORAMA_HORIZONTAL "\xee\x90\x8d" // U+e40d
#define ICON_MS_PANORAMA_PHOTOSPHERE "\xee\xa7\x89" // U+e9c9
#define ICON_MS_PANORAMA_VERTICAL "\xee\x90\x8e" // U+e40e
#define ICON_MS_PANORAMA_WIDE_ANGLE "\xee\x90\x8f" // U+e40f
#define ICON_MS_PARAGLIDING "\xee\x94\x8f" // U+e50f
#define ICON_MS_PARK "\xee\xa9\xa3" // U+ea63
#define ICON_MS_PARTLY_CLOUDY_DAY "\xef\x85\xb2" // U+f172
#define ICON_MS_PARTLY_CLOUDY_NIGHT "\xef\x85\xb4" // U+f174
#define ICON_MS_PARTNER_EXCHANGE "\xef\x9f\xb9" // U+f7f9
#define ICON_MS_PARTNER_REPORTS "\xee\xbe\xaf" // U+efaf
#define ICON_MS_PARTY_MODE "\xee\x9f\xba" // U+e7fa
#define ICON_MS_PASSKEY "\xef\xa1\xbf" // U+f87f
#define ICON_MS_PASSWORD "\xef\x81\x82" // U+f042
#define ICON_MS_PASSWORD_2 "\xef\x92\xa9" // U+f4a9
#define ICON_MS_PASSWORD_2_OFF "\xef\x92\xa8" // U+f4a8
#define ICON_MS_PATIENT_LIST "\xee\x99\x93" // U+e653
#define ICON_MS_PATTERN "\xef\x81\x83" // U+f043
#define ICON_MS_PAUSE "\xee\x80\xb4" // U+e034
#define ICON_MS_PAUSE_CIRCLE "\xee\x86\xa2" // U+e1a2
#define ICON_MS_PAUSE_CIRCLE_FILLED "\xee\x86\xa2" // U+e1a2
#define ICON_MS_PAUSE_CIRCLE_OUTLINE "\xee\x86\xa2" // U+e1a2
#define ICON_MS_PAUSE_PRESENTATION "\xee\x83\xaa" // U+e0ea
#define ICON_MS_PAYMENT "\xee\xa2\xa1" // U+e8a1
#define ICON_MS_PAYMENTS "\xee\xbd\xa3" // U+ef63
#define ICON_MS_PEDAL_BIKE "\xee\xac\xa9" // U+eb29
#define ICON_MS_PEDIATRICS "\xee\x84\x9d" // U+e11d
#define ICON_MS_PEN_SIZE_1 "\xef\x9d\x95" // U+f755
#define ICON_MS_PEN_SIZE_2 "\xef\x9d\x94" // U+f754
#define ICON_MS_PEN_SIZE_3 "\xef\x9d\x93" // U+f753
#define ICON_MS_PEN_SIZE_4 "\xef\x9d\x92" // U+f752
#define ICON_MS_PEN_SIZE_5 "\xef\x9d\x91" // U+f751
#define ICON_MS_PENDING "\xee\xbd\xa4" // U+ef64
#define ICON_MS_PENDING_ACTIONS "\xef\x86\xbb" // U+f1bb
#define ICON_MS_PENTAGON "\xee\xad\x90" // U+eb50
#define ICON_MS_PEOPLE "\xee\xa8\xa1" // U+ea21
#define ICON_MS_PEOPLE_ALT "\xee\xa8\xa1" // U+ea21
#define ICON_MS_PEOPLE_OUTLINE "\xee\xa8\xa1" // U+ea21
#define ICON_MS_PERCENT "\xee\xad\x98" // U+eb58
#define ICON_MS_PERFORMANCE_MAX "\xee\x94\x9a" // U+e51a
#define ICON_MS_PERGOLA "\xee\x88\x83" // U+e203
#define ICON_MS_PERM_CAMERA_MIC "\xee\xa2\xa2" // U+e8a2
#define ICON_MS_PERM_CONTACT_CALENDAR "\xee\xa2\xa3" // U+e8a3
#define ICON_MS_PERM_DATA_SETTING "\xee\xa2\xa4" // U+e8a4
#define ICON_MS_PERM_DEVICE_INFORMATION "\xee\xa2\xa5" // U+e8a5
#define ICON_MS_PERM_IDENTITY "\xef\x83\x93" // U+f0d3
#define ICON_MS_PERM_MEDIA "\xee\xa2\xa7" // U+e8a7
#define ICON_MS_PERM_PHONE_MSG "\xee\xa2\xa8" // U+e8a8
#define ICON_MS_PERM_SCAN_WIFI "\xee\xa2\xa9" // U+e8a9
#define ICON_MS_PERSON "\xef\x83\x93" // U+f0d3
#define ICON_MS_PERSON_2 "\xef\xa3\xa4" // U+f8e4
#define ICON_MS_PERSON_3 "\xef\xa3\xa5" // U+f8e5
#define ICON_MS_PERSON_4 "\xef\xa3\xa6" // U+f8e6
#define ICON_MS_PERSON_ADD "\xee\xa9\x8d" // U+ea4d
#define ICON_MS_PERSON_ADD_ALT "\xee\xa9\x8d" // U+ea4d
#define ICON_MS_PERSON_ADD_DISABLED "\xee\xa7\x8b" // U+e9cb
#define ICON_MS_PERSON_ALERT "\xef\x95\xa7" // U+f567
#define ICON_MS_PERSON_APRON "\xef\x96\xa3" // U+f5a3
#define ICON_MS_PERSON_BOOK "\xef\x97\xa8" // U+f5e8
#define ICON_MS_PERSON_CANCEL "\xef\x95\xa6" // U+f566
#define ICON_MS_PERSON_CELEBRATE "\xef\x9f\xbe" // U+f7fe
#define ICON_MS_PERSON_CHECK "\xef\x95\xa5" // U+f565
#define ICON_MS_PERSON_EDIT "\xef\x93\xba" // U+f4fa
#define ICON_MS_PERSON_FILLED "\xef\x83\x93" // U+f0d3
#define ICON_MS_PERSON_OFF "\xee\x94\x90" // U+e510
#define ICON_MS_PERSON_OUTLINE "\xef\x83\x93" // U+f0d3
#define ICON_MS_PERSON_PIN "\xee\x95\x9a" // U+e55a
#define ICON_MS_PERSON_PIN_CIRCLE "\xee\x95\xaa" // U+e56a
#define ICON_MS_PERSON_PLAY "\xef\x9f\xbd" // U+f7fd
#define ICON_MS_PERSON_RAISED_HAND "\xef\x96\x9a" // U+f59a
#define ICON_MS_PERSON_REMOVE "\xee\xbd\xa6" // U+ef66
#define ICON_MS_PERSON_SEARCH "\xef\x84\x86" // U+f106
#define ICON_MS_PERSONAL_BAG "\xee\xac\x8e" // U+eb0e
#define ICON_MS_PERSONAL_BAG_OFF "\xee\xac\x8f" // U+eb0f
#define ICON_MS_PERSONAL_BAG_QUESTION "\xee\xac\x90" // U+eb10
#define ICON_MS_PERSONAL_INJURY "\xee\x9b\x9a" // U+e6da
#define ICON_MS_PERSONAL_PLACES "\xee\x9c\x83" // U+e703
#define ICON_MS_PERSONAL_VIDEO "\xee\x98\xbb" // U+e63b
#define ICON_MS_PEST_CONTROL "\xef\x83\xba" // U+f0fa
#define ICON_MS_PEST_CONTROL_RODENT "\xef\x83\xbd" // U+f0fd
#define ICON_MS_PET_SUPPLIES "\xee\xbe\xb1" // U+efb1
#define ICON_MS_PETS "\xee\xa4\x9d" // U+e91d
#define ICON_MS_PHISHING "\xee\xab\x97" // U+ead7
#define ICON_MS_PHONE "\xef\x83\x94" // U+f0d4
#define ICON_MS_PHONE_ALT "\xef\x83\x94" // U+f0d4
#define ICON_MS_PHONE_ANDROID "\xee\x8c\xa4" // U+e324
#define ICON_MS_PHONE_BLUETOOTH_SPEAKER "\xee\x98\x9b" // U+e61b
#define ICON_MS_PHONE_CALLBACK "\xee\x99\x89" // U+e649
#define ICON_MS_PHONE_DISABLED "\xee\xa7\x8c" // U+e9cc
#define ICON_MS_PHONE_ENABLED "\xee\xa7\x8d" // U+e9cd
#define ICON_MS_PHONE_FORWARDED "\xee\x98\x9c" // U+e61c
#define ICON_MS_PHONE_IN_TALK "\xee\x98\x9d" // U+e61d
#define ICON_MS_PHONE_IPHONE "\xee\x8c\xa5" // U+e325
#define ICON_MS_PHONE_LOCKED "\xee\x98\x9e" // U+e61e
#define ICON_MS_PHONE_MISSED "\xee\x98\x9f" // U+e61f
#define ICON_MS_PHONE_PAUSED "\xee\x98\xa0" // U+e620
#define ICON_MS_PHONELINK "\xee\x8c\xa6" // U+e326
#define ICON_MS_PHONELINK_ERASE "\xee\x83\x9b" // U+e0db
#define ICON_MS_PHONELINK_LOCK "\xee\x83\x9c" // U+e0dc
#define ICON_MS_PHONELINK_OFF "\xee\x8c\xa7" // U+e327
#define ICON_MS_PHONELINK_RING "\xee\x83\x9d" // U+e0dd
#define ICON_MS_PHONELINK_RING_OFF "\xef\x9e\xaa" // U+f7aa
#define ICON_MS_PHONELINK_SETUP "\xee\xbd\x81" // U+ef41
#define ICON_MS_PHOTO "\xee\x90\xb2" // U+e432
#define ICON_MS_PHOTO_ALBUM "\xee\x90\x91" // U+e411
#define ICON_MS_PHOTO_AUTO_MERGE "\xef\x94\xb0" // U+f530
#define ICON_MS_PHOTO_CAMERA "\xee\x90\x92" // U+e412
#define ICON_MS_PHOTO_CAMERA_BACK "\xee\xbd\xa8" // U+ef68
#define ICON_MS_PHOTO_CAMERA_FRONT "\xee\xbd\xa9" // U+ef69
#define ICON_MS_PHOTO_FILTER "\xee\x90\xbb" // U+e43b
#define ICON_MS_PHOTO_FRAME "\xef\x83\x99" // U+f0d9
#define ICON_MS_PHOTO_LIBRARY "\xee\x90\x93" // U+e413
#define ICON_MS_PHOTO_PRINTS "\xee\xbe\xb2" // U+efb2
#define ICON_MS_PHOTO_SIZE_SELECT_ACTUAL "\xee\x90\xb2" // U+e432
#define ICON_MS_PHOTO_SIZE_SELECT_LARGE "\xee\x90\xb3" // U+e433
#define ICON_MS_PHOTO_SIZE_SELECT_SMALL "\xee\x90\xb4" // U+e434
#define ICON_MS_PHP "\xee\xae\x8f" // U+eb8f
#define ICON_MS_PHYSICAL_THERAPY "\xee\x84\x9e" // U+e11e
#define ICON_MS_PIANO "\xee\x94\xa1" // U+e521
#define ICON_MS_PIANO_OFF "\xee\x94\xa0" // U+e520
#define ICON_MS_PICTURE_AS_PDF "\xee\x90\x95" // U+e415
#define ICON_MS_PICTURE_IN_PICTURE "\xee\xa2\xaa" // U+e8aa
#define ICON_MS_PICTURE_IN_PICTURE_ALT "\xee\xa4\x91" // U+e911
#define ICON_MS_PICTURE_IN_PICTURE_CENTER "\xef\x95\x90" // U+f550
#define ICON_MS_PICTURE_IN_PICTURE_LARGE "\xef\x95\x8f" // U+f54f
#define ICON_MS_PICTURE_IN_PICTURE_MEDIUM "\xef\x95\x8e" // U+f54e
#define ICON_MS_PICTURE_IN_PICTURE_MOBILE "\xef\x94\x97" // U+f517
#define ICON_MS_PICTURE_IN_PICTURE_OFF "\xef\x94\xaf" // U+f52f
#define ICON_MS_PICTURE_IN_PICTURE_SMALL "\xef\x95\x8d" // U+f54d
#define ICON_MS_PIE_CHART "\xef\x83\x9a" // U+f0da
#define ICON_MS_PIE_CHART_FILLED "\xef\x83\x9a" // U+f0da
#define ICON_MS_PIE_CHART_OUTLINE "\xef\x83\x9a" // U+f0da
#define ICON_MS_PIE_CHART_OUTLINED "\xef\x83\x9a" // U+f0da
#define ICON_MS_PILL "\xee\x84\x9f" // U+e11f
#define ICON_MS_PILL_OFF "\xef\xa0\x89" // U+f809
#define ICON_MS_PIN "\xef\x81\x85" // U+f045
#define ICON_MS_PIN_DROP "\xee\x95\x9e" // U+e55e
#define ICON_MS_PIN_END "\xee\x9d\xa7" // U+e767
#define ICON_MS_PIN_INVOKE "\xee\x9d\xa3" // U+e763
#define ICON_MS_PINCH "\xee\xac\xb8" // U+eb38
#define ICON_MS_PINCH_ZOOM_IN "\xef\x87\xba" // U+f1fa
#define ICON_MS_PINCH_ZOOM_OUT "\xef\x87\xbb" // U+f1fb
#define ICON_MS_PIP "\xef\x99\x8d" // U+f64d
#define ICON_MS_PIP_EXIT "\xef\x9c\x8d" // U+f70d
#define ICON_MS_PIVOT_TABLE_CHART "\xee\xa7\x8e" // U+e9ce
#define ICON_MS_PLACE "\xef\x87\x9b" // U+f1db
#define ICON_MS_PLACE_ITEM "\xef\x87\xb0" // U+f1f0
#define ICON_MS_PLAGIARISM "\xee\xa9\x9a" // U+ea5a
#define ICON_MS_PLANNER_BANNER_AD_PT "\xee\x9a\x92" // U+e692
#define ICON_MS_PLANNER_REVIEW "\xee\x9a\x94" // U+e694
#define ICON_MS_PLAY_ARROW "\xee\x80\xb7" // U+e037
#define ICON_MS_PLAY_CIRCLE "\xee\x87\x84" // U+e1c4
#define ICON_MS_PLAY_DISABLED "\xee\xbd\xaa" // U+ef6a
#define ICON_MS_PLAY_FOR_WORK "\xee\xa4\x86" // U+e906
#define ICON_MS_PLAY_LESSON "\xef\x81\x87" // U+f047
#define ICON_MS_PLAY_MUSIC "\xee\x9b\xae" // U+e6ee
#define ICON_MS_PLAY_PAUSE "\xef\x84\xb7" // U+f137
#define ICON_MS_PLAY_SHAPES "\xef\x9f\xbc" // U+f7fc
#define ICON_MS_PLAYING_CARDS "\xef\x97\x9c" // U+f5dc
#define ICON_MS_PLAYLIST_ADD "\xee\x80\xbb" // U+e03b
#define ICON_MS_PLAYLIST_ADD_CHECK "\xee\x81\xa5" // U+e065
#define ICON_MS_PLAYLIST_ADD_CHECK_CIRCLE "\xee\x9f\xa6" // U+e7e6
#define ICON_MS_PLAYLIST_ADD_CIRCLE "\xee\x9f\xa5" // U+e7e5
#define ICON_MS_PLAYLIST_PLAY "\xee\x81\x9f" // U+e05f
#define ICON_MS_PLAYLIST_REMOVE "\xee\xae\x80" // U+eb80
#define ICON_MS_PLUMBING "\xef\x84\x87" // U+f107
#define ICON_MS_PLUS_ONE "\xee\xa0\x80" // U+e800
#define ICON_MS_PODCASTS "\xef\x81\x88" // U+f048
#define ICON_MS_PODIATRY "\xee\x84\xa0" // U+e120
#define ICON_MS_PODIUM "\xef\x9f\xbb" // U+f7fb
#define ICON_MS_POINT_OF_SALE "\xef\x85\xbe" // U+f17e
#define ICON_MS_POINT_SCAN "\xef\x9c\x8c" // U+f70c
#define ICON_MS_POKER_CHIP "\xef\x92\x9b" // U+f49b
#define ICON_MS_POLICY "\xee\xa8\x97" // U+ea17
#define ICON_MS_POLL "\xef\x83\x8c" // U+f0cc
#define ICON_MS_POLYLINE "\xee\xae\xbb" // U+ebbb
#define ICON_MS_POLYMER "\xee\xa2\xab" // U+e8ab
#define ICON_MS_POOL "\xee\xad\x88" // U+eb48
#define ICON_MS_PORTABLE_WIFI_OFF "\xef\x82\x87" // U+f087
#define ICON_MS_PORTRAIT "\xee\xa1\x91" // U+e851
#define ICON_MS_POSITION_BOTTOM_LEFT "\xef\x9c\x8b" // U+f70b
#define ICON_MS_POSITION_BOTTOM_RIGHT "\xef\x9c\x8a" // U+f70a
#define ICON_MS_POSITION_TOP_RIGHT "\xef\x9c\x89" // U+f709
#define ICON_MS_POST "\xee\x9c\x85" // U+e705
#define ICON_MS_POST_ADD "\xee\xa8\xa0" // U+ea20
#define ICON_MS_POTTED_PLANT "\xef\xa2\xaa" // U+f8aa
#define ICON_MS_POWER "\xee\x98\xbc" // U+e63c
#define ICON_MS_POWER_INPUT "\xee\x8c\xb6" // U+e336
#define ICON_MS_POWER_OFF "\xee\x99\x86" // U+e646
#define ICON_MS_POWER_ROUNDED "\xef\xa3\x87" // U+f8c7
#define ICON_MS_POWER_SETTINGS_NEW "\xef\xa3\x87" // U+f8c7
#define ICON_MS_PRAYER_TIMES "\xef\xa0\xb8" // U+f838
#define ICON_MS_PRECISION_MANUFACTURING "\xef\x81\x89" // U+f049
#define ICON_MS_PREGNANCY "\xef\x97\xb1" // U+f5f1
#define ICON_MS_PREGNANT_WOMAN "\xef\x97\xb1" // U+f5f1
#define ICON_MS_PRELIMINARY "\xee\x9f\x98" // U+e7d8
#define ICON_MS_PRESCRIPTIONS "\xee\x84\xa1" // U+e121
#define ICON_MS_PRESENT_TO_ALL "\xee\x83\x9f" // U+e0df
#define ICON_MS_PREVIEW "\xef\x87\x85" // U+f1c5
#define ICON_MS_PREVIEW_OFF "\xef\x9e\xaf" // U+f7af
#define ICON_MS_PRICE_CHANGE "\xef\x81\x8a" // U+f04a
#define ICON_MS_PRICE_CHECK "\xef\x81\x8b" // U+f04b
#define ICON_MS_PRINT "\xee\xa2\xad" // U+e8ad
#define ICON_MS_PRINT_ADD "\xef\x9e\xa2" // U+f7a2
#define ICON_MS_PRINT_CONNECT "\xef\x9e\xa1" // U+f7a1
#define ICON_MS_PRINT_DISABLED "\xee\xa7\x8f" // U+e9cf
#define ICON_MS_PRINT_ERROR "\xef\x9e\xa0" // U+f7a0
#define ICON_MS_PRINT_LOCK "\xef\x99\x91" // U+f651
#define ICON_MS_PRIORITY "\xee\x86\x9f" // U+e19f
#define ICON_MS_PRIORITY_HIGH "\xee\x99\x85" // U+e645
#define ICON_MS_PRIVACY "\xef\x85\x88" // U+f148
#define ICON_MS_PRIVACY_TIP "\xef\x83\x9c" // U+f0dc
#define ICON_MS_PRIVATE_CONNECTIVITY "\xee\x9d\x84" // U+e744
#define ICON_MS_PROBLEM "\xee\x84\xa2" // U+e122
#define ICON_MS_PROCEDURE "\xee\x99\x91" // U+e651
#define ICON_MS_PROCESS_CHART "\xef\xa1\x95" // U+f855
#define ICON_MS_PRODUCTION_QUANTITY_LIMITS "\xee\x87\x91" // U+e1d1
#define ICON_MS_PRODUCTIVITY "\xee\x8a\x96" // U+e296
#define ICON_MS_PROGRESS_ACTIVITY "\xee\xa7\x90" // U+e9d0
#define ICON_MS_PROMPT_SUGGESTION "\xef\x93\xb6" // U+f4f6
#define ICON_MS_PROPANE "\xee\xb0\x94" // U+ec14
#define ICON_MS_PROPANE_TANK "\xee\xb0\x93" // U+ec13
#define ICON_MS_PSYCHIATRY "\xee\x84\xa3" // U+e123
#define ICON_MS_PSYCHOLOGY "\xee\xa9\x8a" // U+ea4a
#define ICON_MS_PSYCHOLOGY_ALT "\xef\xa3\xaa" // U+f8ea
#define ICON_MS_PUBLIC "\xee\xa0\x8b" // U+e80b
#define ICON_MS_PUBLIC_OFF "\xef\x87\x8a" // U+f1ca
#define ICON_MS_PUBLISH "\xee\x89\x95" // U+e255
#define ICON_MS_PUBLISHED_WITH_CHANGES "\xef\x88\xb2" // U+f232
#define ICON_MS_PULMONOLOGY "\xee\x84\xa4" // U+e124
#define ICON_MS_PULSE_ALERT "\xef\x94\x81" // U+f501
#define ICON_MS_PUNCH_CLOCK "\xee\xaa\xa8" // U+eaa8
#define ICON_MS_PUSH_PIN "\xef\x84\x8d" // U+f10d
#define ICON_MS_QR_CODE "\xee\xbd\xab" // U+ef6b
#define ICON_MS_QR_CODE_2 "\xee\x80\x8a" // U+e00a
#define ICON_MS_QR_CODE_2_ADD "\xef\x99\x98" // U+f658
#define ICON_MS_QR_CODE_SCANNER "\xef\x88\x86" // U+f206
#define ICON_MS_QUERY_BUILDER "\xee\xbf\x96" // U+efd6
#define ICON_MS_QUERY_STATS "\xee\x93\xbc" // U+e4fc
#define ICON_MS_QUESTION_ANSWER "\xee\xa2\xaf" // U+e8af
#define ICON_MS_QUESTION_EXCHANGE "\xef\x9f\xb3" // U+f7f3
#define ICON_MS_QUESTION_MARK "\xee\xae\x8b" // U+eb8b
#define ICON_MS_QUEUE "\xee\x80\xbc" // U+e03c
#define ICON_MS_QUEUE_MUSIC "\xee\x80\xbd" // U+e03d
#define ICON_MS_QUEUE_PLAY_NEXT "\xee\x81\xa6" // U+e066
#define ICON_MS_QUICK_PHRASES "\xee\x9f\x91" // U+e7d1
#define ICON_MS_QUICK_REFERENCE "\xee\x91\xae" // U+e46e
#define ICON_MS_QUICK_REFERENCE_ALL "\xef\xa0\x81" // U+f801
#define ICON_MS_QUICK_REORDER "\xee\xac\x95" // U+eb15
#define ICON_MS_QUICKREPLY "\xee\xbd\xac" // U+ef6c
#define ICON_MS_QUIET_TIME "\xee\x87\xb9" // U+e1f9
#define ICON_MS_QUIET_TIME_ACTIVE "\xee\x8a\x91" // U+e291
#define ICON_MS_QUIZ "\xef\x81\x8c" // U+f04c
#define ICON_MS_R_MOBILEDATA "\xef\x81\x8d" // U+f04d
#define ICON_MS_RADAR "\xef\x81\x8e" // U+f04e
#define ICON_MS_RADIO "\xee\x80\xbe" // U+e03e
#define ICON_MS_RADIO_BUTTON_CHECKED "\xee\xa0\xb7" // U+e837
#define ICON_MS_RADIO_BUTTON_PARTIAL "\xef\x95\xa0" // U+f560
#define ICON_MS_RADIO_BUTTON_UNCHECKED "\xee\xa0\xb6" // U+e836
#define ICON_MS_RADIOLOGY "\xee\x84\xa5" // U+e125
#define ICON_MS_RAILWAY_ALERT "\xee\xa7\x91" // U+e9d1
#define ICON_MS_RAINY "\xef\x85\xb6" // U+f176
#define ICON_MS_RAINY_HEAVY "\xef\x98\x9f" // U+f61f
#define ICON_MS_RAINY_LIGHT "\xef\x98\x9e" // U+f61e
#define ICON_MS_RAINY_SNOW "\xef\x98\x9d" // U+f61d
#define ICON_MS_RAMEN_DINING "\xee\xa9\xa4" // U+ea64
#define ICON_MS_RAMP_LEFT "\xee\xae\x9c" // U+eb9c
#define ICON_MS_RAMP_RIGHT "\xee\xae\x96" // U+eb96
#define ICON_MS_RANGE_HOOD "\xee\x87\xaa" // U+e1ea
#define ICON_MS_RATE_REVIEW "\xee\x95\xa0" // U+e560
#define ICON_MS_RAVEN "\xef\x95\x95" // U+f555
#define ICON_MS_RAW_OFF "\xef\x81\x8f" // U+f04f
#define ICON_MS_RAW_ON "\xef\x81\x90" // U+f050
#define ICON_MS_READ_MORE "\xee\xbd\xad" // U+ef6d
#define ICON_MS_READINESS_SCORE "\xef\x9b\x9d" // U+f6dd
#define ICON_MS_REAL_ESTATE_AGENT "\xee\x9c\xba" // U+e73a
#define ICON_MS_REAR_CAMERA "\xef\x9b\x82" // U+f6c2
#define ICON_MS_REBASE "\xef\xa1\x85" // U+f845
#define ICON_MS_REBASE_EDIT "\xef\xa1\x86" // U+f846
#define ICON_MS_RECEIPT "\xee\xa2\xb0" // U+e8b0
#define ICON_MS_RECEIPT_LONG "\xee\xbd\xae" // U+ef6e
#define ICON_MS_RECENT_ACTORS "\xee\x80\xbf" // U+e03f
#define ICON_MS_RECENT_PATIENT "\xef\xa0\x88" // U+f808
#define ICON_MS_RECENTER "\xef\x93\x80" // U+f4c0
#define ICON_MS_RECOMMEND "\xee\xa7\x92" // U+e9d2
#define ICON_MS_RECORD_VOICE_OVER "\xee\xa4\x9f" // U+e91f
#define ICON_MS_RECTANGLE "\xee\xad\x94" // U+eb54
#define ICON_MS_RECYCLING "\xee\x9d\xa0" // U+e760
#define ICON_MS_REDEEM "\xee\xa3\xb6" // U+e8f6
#define ICON_MS_REDO "\xee\x85\x9a" // U+e15a
#define ICON_MS_REDUCE_CAPACITY "\xef\x88\x9c" // U+f21c
#define ICON_MS_REFRESH "\xee\x97\x95" // U+e5d5
#define ICON_MS_REGULAR_EXPRESSION "\xef\x9d\x90" // U+f750
#define ICON_MS_RELAX "\xef\x9b\x9c" // U+f6dc
#define ICON_MS_RELEASE_ALERT "\xef\x99\x94" // U+f654
#define ICON_MS_REMEMBER_ME "\xef\x81\x91" // U+f051
#define ICON_MS_REMINDER "\xee\x9b\x86" // U+e6c6
#define ICON_MS_REMINDERS_ALT "\xee\x9b\x86" // U+e6c6
#define ICON_MS_REMOTE_GEN "\xee\xa0\xbe" // U+e83e
#define ICON_MS_REMOVE "\xee\x85\x9b" // U+e15b
#define ICON_MS_REMOVE_CIRCLE "\xef\x82\x8f" // U+f08f
#define ICON_MS_REMOVE_CIRCLE_OUTLINE "\xef\x82\x8f" // U+f08f
#define ICON_MS_REMOVE_DONE "\xee\xa7\x93" // U+e9d3
#define ICON_MS_REMOVE_FROM_QUEUE "\xee\x81\xa7" // U+e067
#define ICON_MS_REMOVE_MODERATOR "\xee\xa7\x94" // U+e9d4
#define ICON_MS_REMOVE_RED_EYE "\xee\xa3\xb4" // U+e8f4
#define ICON_MS_REMOVE_ROAD "\xee\xaf\xbc" // U+ebfc
#define ICON_MS_REMOVE_SELECTION "\xee\xa7\x95" // U+e9d5
#define ICON_MS_REMOVE_SHOPPING_CART "\xee\xa4\xa8" // U+e928
#define ICON_MS_REOPEN_WINDOW "\xef\x9c\x88" // U+f708
#define ICON_MS_REORDER "\xee\xa3\xbe" // U+e8fe
#define ICON_MS_REPARTITION "\xef\xa3\xa8" // U+f8e8
#define ICON_MS_REPEAT "\xee\x81\x80" // U+e040
#define ICON_MS_REPEAT_ON "\xee\xa7\x96" // U+e9d6
#define ICON_MS_REPEAT_ONE "\xee\x81\x81" // U+e041
#define ICON_MS_REPEAT_ONE_ON "\xee\xa7\x97" // U+e9d7
#define ICON_MS_REPLAY "\xee\x81\x82" // U+e042
#define ICON_MS_REPLAY_10 "\xee\x81\x99" // U+e059
#define ICON_MS_REPLAY_30 "\xee\x81\x9a" // U+e05a
#define ICON_MS_REPLAY_5 "\xee\x81\x9b" // U+e05b
#define ICON_MS_REPLAY_CIRCLE_FILLED "\xee\xa7\x98" // U+e9d8
#define ICON_MS_REPLY "\xee\x85\x9e" // U+e15e
#define ICON_MS_REPLY_ALL "\xee\x85\x9f" // U+e15f
#define ICON_MS_REPORT "\xef\x81\x92" // U+f052
#define ICON_MS_REPORT_GMAILERRORRED "\xef\x81\x92" // U+f052
#define ICON_MS_REPORT_OFF "\xee\x85\xb0" // U+e170
#define ICON_MS_REPORT_PROBLEM "\xef\x82\x83" // U+f083
#define ICON_MS_REQUEST_PAGE "\xef\x88\xac" // U+f22c
#define ICON_MS_REQUEST_QUOTE "\xef\x86\xb6" // U+f1b6
#define ICON_MS_RESET_IMAGE "\xef\xa0\xa4" // U+f824
#define ICON_MS_RESET_TV "\xee\xa7\x99" // U+e9d9
#define ICON_MS_RESET_WRENCH "\xef\x95\xac" // U+f56c
#define ICON_MS_RESIZE "\xef\x9c\x87" // U+f707
#define ICON_MS_RESPIRATORY_RATE "\xee\x84\xa7" // U+e127
#define ICON_MS_RESPONSIVE_LAYOUT "\xee\xa7\x9a" // U+e9da
#define ICON_MS_RESTART_ALT "\xef\x81\x93" // U+f053
#define ICON_MS_RESTAURANT "\xee\x95\xac" // U+e56c
#define ICON_MS_RESTAURANT_MENU "\xee\x95\xa1" // U+e561
#define ICON_MS_RESTORE "\xee\xa2\xb3" // U+e8b3
#define ICON_MS_RESTORE_FROM_TRASH "\xee\xa4\xb8" // U+e938
#define ICON_MS_RESTORE_PAGE "\xee\xa4\xa9" // U+e929
#define ICON_MS_RESUME "\xef\x9f\x90" // U+f7d0
#define ICON_MS_REVIEWS "\xef\x81\xbc" // U+f07c
#define ICON_MS_REWARDED_ADS "\xee\xbe\xb6" // U+efb6
#define ICON_MS_RHEUMATOLOGY "\xee\x84\xa8" // U+e128
#define ICON_MS_RIB_CAGE "\xef\xa2\x98" // U+f898
#define ICON_MS_RICE_BOWL "\xef\x87\xb5" // U+f1f5
#define ICON_MS_RIGHT_CLICK "\xef\x9c\x86" // U+f706
#define ICON_MS_RIGHT_PANEL_CLOSE "\xef\x9c\x85" // U+f705
#define ICON_MS_RIGHT_PANEL_OPEN "\xef\x9c\x84" // U+f704
#define ICON_MS_RING_VOLUME "\xef\x83\x9d" // U+f0dd
#define ICON_MS_RING_VOLUME_FILLED "\xef\x83\x9d" // U+f0dd
#define ICON_MS_RIPPLES "\xee\xa7\x9b" // U+e9db
#define ICON_MS_ROBOT "\xef\xa2\x82" // U+f882
#define ICON_MS_ROBOT_2 "\xef\x97\x90" // U+f5d0
#define ICON_MS_ROCKET "\xee\xae\xa5" // U+eba5
#define ICON_MS_ROCKET_LAUNCH "\xee\xae\x9b" // U+eb9b
#define ICON_MS_ROLLER_SHADES "\xee\xb0\x92" // U+ec12
#define ICON_MS_ROLLER_SHADES_CLOSED "\xee\xb0\x91" // U+ec11
#define ICON_MS_ROLLER_SKATING "\xee\xaf\x8d" // U+ebcd
#define ICON_MS_ROOFING "\xef\x88\x81" // U+f201
#define ICON_MS_ROOM "\xef\x87\x9b" // U+f1db
#define ICON_MS_ROOM_PREFERENCES "\xef\x86\xb8" // U+f1b8
#define ICON_MS_ROOM_SERVICE "\xee\xad\x89" // U+eb49
#define ICON_MS_ROTATE_90_DEGREES_CCW "\xee\x90\x98" // U+e418
#define ICON_MS_ROTATE_90_DEGREES_CW "\xee\xaa\xab" // U+eaab
#define ICON_MS_ROTATE_LEFT "\xee\x90\x99" // U+e419
#define ICON_MS_ROTATE_RIGHT "\xee\x90\x9a" // U+e41a
#define ICON_MS_ROUNDABOUT_LEFT "\xee\xae\x99" // U+eb99
#define ICON_MS_ROUNDABOUT_RIGHT "\xee\xae\xa3" // U+eba3
#define ICON_MS_ROUNDED_CORNER "\xee\xa4\xa0" // U+e920
#define ICON_MS_ROUTE "\xee\xab\x8d" // U+eacd
#define ICON_MS_ROUTER "\xee\x8c\xa8" // U+e328
#define ICON_MS_ROUTINE "\xee\x88\x8c" // U+e20c
#define ICON_MS_ROWING "\xee\xa4\xa1" // U+e921
#define ICON_MS_RSS_FEED "\xee\x83\xa5" // U+e0e5
#define ICON_MS_RSVP "\xef\x81\x95" // U+f055
#define ICON_MS_RTT "\xee\xa6\xad" // U+e9ad
#define ICON_MS_RUBRIC "\xee\xac\xa7" // U+eb27
#define ICON_MS_RULE "\xef\x87\x82" // U+f1c2
#define ICON_MS_RULE_FOLDER "\xef\x87\x89" // U+f1c9
#define ICON_MS_RULE_SETTINGS "\xef\x99\x8c" // U+f64c
#define ICON_MS_RUN_CIRCLE "\xee\xbd\xaf" // U+ef6f
#define ICON_MS_RUNNING_WITH_ERRORS "\xee\x94\x9d" // U+e51d
#define ICON_MS_RV_HOOKUP "\xee\x99\x82" // U+e642
#define ICON_MS_SAFETY_CHECK "\xee\xaf\xaf" // U+ebef
#define ICON_MS_SAFETY_CHECK_OFF "\xef\x96\x9d" // U+f59d
#define ICON_MS_SAFETY_DIVIDER "\xee\x87\x8c" // U+e1cc
#define ICON_MS_SAILING "\xee\x94\x82" // U+e502
#define ICON_MS_SALINITY "\xef\xa1\xb6" // U+f876
#define ICON_MS_SANITIZER "\xef\x88\x9d" // U+f21d
#define ICON_MS_SATELLITE "\xee\x95\xa2" // U+e562
#define ICON_MS_SATELLITE_ALT "\xee\xac\xba" // U+eb3a
#define ICON_MS_SAUNA "\xef\x9b\xb7" // U+f6f7
#define ICON_MS_SAVE "\xee\x85\xa1" // U+e161
#define ICON_MS_SAVE_ALT "\xef\x82\x90" // U+f090
#define ICON_MS_SAVE_AS "\xee\xad\xa0" // U+eb60
#define ICON_MS_SAVED_SEARCH "\xee\xa8\x91" // U+ea11
#define ICON_MS_SAVINGS "\xee\x8b\xab" // U+e2eb
#define ICON_MS_SCALE "\xee\xad\x9f" // U+eb5f
#define ICON_MS_SCAN "\xef\x9d\x8e" // U+f74e
#define ICON_MS_SCAN_DELETE "\xef\x9d\x8f" // U+f74f
#define ICON_MS_SCANNER "\xee\x8c\xa9" // U+e329
#define ICON_MS_SCATTER_PLOT "\xee\x89\xa8" // U+e268
#define ICON_MS_SCENE "\xee\x8a\xa7" // U+e2a7
#define ICON_MS_SCHEDULE "\xee\xbf\x96" // U+efd6
#define ICON_MS_SCHEDULE_SEND "\xee\xa8\x8a" // U+ea0a
#define ICON_MS_SCHEMA "\xee\x93\xbd" // U+e4fd
#define ICON_MS_SCHOOL "\xee\xa0\x8c" // U+e80c
#define ICON_MS_SCIENCE "\xee\xa9\x8b" // U+ea4b
#define ICON_MS_SCIENCE_OFF "\xef\x95\x82" // U+f542
#define ICON_MS_SCORE "\xee\x89\xa9" // U+e269
#define ICON_MS_SCOREBOARD "\xee\xaf\x90" // U+ebd0
#define ICON_MS_SCREEN_LOCK_LANDSCAPE "\xee\x86\xbe" // U+e1be
#define ICON_MS_SCREEN_LOCK_PORTRAIT "\xee\x86\xbf" // U+e1bf
#define ICON_MS_SCREEN_LOCK_ROTATION "\xee\x87\x80" // U+e1c0
#define ICON_MS_SCREEN_RECORD "\xef\x99\xb9" // U+f679
#define ICON_MS_SCREEN_ROTATION "\xee\x87\x81" // U+e1c1
#define ICON_MS_SCREEN_ROTATION_ALT "\xee\xaf\xae" // U+ebee
#define ICON_MS_SCREEN_ROTATION_UP "\xef\x99\xb8" // U+f678
#define ICON_MS_SCREEN_SEARCH_DESKTOP "\xee\xbd\xb0" // U+ef70
#define ICON_MS_SCREEN_SHARE "\xee\x83\xa2" // U+e0e2
#define ICON_MS_SCREENSHOT "\xef\x81\x96" // U+f056
#define ICON_MS_SCREENSHOT_FRAME "\xef\x99\xb7" // U+f677
#define ICON_MS_SCREENSHOT_KEYBOARD "\xef\x9f\x93" // U+f7d3
#define ICON_MS_SCREENSHOT_MONITOR "\xee\xb0\x88" // U+ec08
#define ICON_MS_SCREENSHOT_REGION "\xef\x9f\x92" // U+f7d2
#define ICON_MS_SCREENSHOT_TABLET "\xef\x9a\x97" // U+f697
#define ICON_MS_SCROLLABLE_HEADER "\xee\xa7\x9c" // U+e9dc
#define ICON_MS_SCUBA_DIVING "\xee\xaf\x8e" // U+ebce
#define ICON_MS_SD "\xee\xa7\x9d" // U+e9dd
#define ICON_MS_SD_CARD "\xee\x98\xa3" // U+e623
#define ICON_MS_SD_CARD_ALERT "\xef\x81\x97" // U+f057
#define ICON_MS_SD_STORAGE "\xee\x98\xa3" // U+e623
#define ICON_MS_SDK "\xee\x9c\xa0" // U+e720
#define ICON_MS_SEARCH "\xee\xa2\xb6" // U+e8b6
#define ICON_MS_SEARCH_CHECK "\xef\xa0\x80" // U+f800
#define ICON_MS_SEARCH_HANDS_FREE "\xee\x9a\x96" // U+e696
#define ICON_MS_SEARCH_INSIGHTS "\xef\x92\xbc" // U+f4bc
#define ICON_MS_SEARCH_OFF "\xee\xa9\xb6" // U+ea76
#define ICON_MS_SECURITY "\xee\x8c\xaa" // U+e32a
#define ICON_MS_SECURITY_KEY "\xef\x94\x83" // U+f503
#define ICON_MS_SECURITY_UPDATE "\xef\x81\xb2" // U+f072
#define ICON_MS_SECURITY_UPDATE_GOOD "\xef\x81\xb3" // U+f073
#define ICON_MS_SECURITY_UPDATE_WARNING "\xef\x81\xb4" // U+f074
#define ICON_MS_SEGMENT "\xee\xa5\x8b" // U+e94b
#define ICON_MS_SELECT "\xef\x9d\x8d" // U+f74d
#define ICON_MS_SELECT_ALL "\xee\x85\xa2" // U+e162
#define ICON_MS_SELECT_CHECK_BOX "\xef\x87\xbe" // U+f1fe
#define ICON_MS_SELECT_TO_SPEAK "\xef\x9f\x8f" // U+f7cf
#define ICON_MS_SELECT_WINDOW "\xee\x9b\xba" // U+e6fa
#define ICON_MS_SELECT_WINDOW_2 "\xef\x93\x88" // U+f4c8
#define ICON_MS_SELECT_WINDOW_OFF "\xee\x94\x86" // U+e506
#define ICON_MS_SELF_CARE "\xef\xa1\xad" // U+f86d
#define ICON_MS_SELF_IMPROVEMENT "\xee\xa9\xb8" // U+ea78
#define ICON_MS_SELL "\xef\x81\x9b" // U+f05b
#define ICON_MS_SEND "\xee\x85\xa3" // U+e163
#define ICON_MS_SEND_AND_ARCHIVE "\xee\xa8\x8c" // U+ea0c
#define ICON_MS_SEND_MONEY "\xee\xa2\xb7" // U+e8b7
#define ICON_MS_SEND_TIME_EXTENSION "\xee\xab\x9b" // U+eadb
#define ICON_MS_SEND_TO_MOBILE "\xef\x81\x9c" // U+f05c
#define ICON_MS_SENSOR_DOOR "\xef\x86\xb5" // U+f1b5
#define ICON_MS_SENSOR_OCCUPIED "\xee\xb0\x90" // U+ec10
#define ICON_MS_SENSOR_WINDOW "\xef\x86\xb4" // U+f1b4
#define ICON_MS_SENSORS "\xee\x94\x9e" // U+e51e
#define ICON_MS_SENSORS_KRX "\xef\x95\x96" // U+f556
#define ICON_MS_SENSORS_KRX_OFF "\xef\x94\x95" // U+f515
#define ICON_MS_SENSORS_OFF "\xee\x94\x9f" // U+e51f
#define ICON_MS_SENTIMENT_CALM "\xef\x9a\xa7" // U+f6a7
#define ICON_MS_SENTIMENT_CONTENT "\xef\x9a\xa6" // U+f6a6
#define ICON_MS_SENTIMENT_DISSATISFIED "\xee\xa0\x91" // U+e811
#define ICON_MS_SENTIMENT_EXCITED "\xef\x9a\xa5" // U+f6a5
#define ICON_MS_SENTIMENT_EXTREMELY_DISSATISFIED "\xef\x86\x94" // U+f194
#define ICON_MS_SENTIMENT_FRUSTRATED "\xef\x9a\xa4" // U+f6a4
#define ICON_MS_SENTIMENT_NEUTRAL "\xee\xa0\x92" // U+e812
#define ICON_MS_SENTIMENT_SAD "\xef\x9a\xa3" // U+f6a3
#define ICON_MS_SENTIMENT_SATISFIED "\xee\xa0\x93" // U+e813
#define ICON_MS_SENTIMENT_SATISFIED_ALT "\xee\xa0\x93" // U+e813
#define ICON_MS_SENTIMENT_STRESSED "\xef\x9a\xa2" // U+f6a2
#define ICON_MS_SENTIMENT_VERY_DISSATISFIED "\xee\xa0\x94" // U+e814
#define ICON_MS_SENTIMENT_VERY_SATISFIED "\xee\xa0\x95" // U+e815
#define ICON_MS_SENTIMENT_WORRIED "\xef\x9a\xa1" // U+f6a1
#define ICON_MS_SERIF "\xef\x92\xac" // U+f4ac
#define ICON_MS_SERVICE_TOOLBOX "\xee\x9c\x97" // U+e717
#define ICON_MS_SET_MEAL "\xef\x87\xaa" // U+f1ea
#define ICON_MS_SETTINGS "\xee\xa2\xb8" // U+e8b8
#define ICON_MS_SETTINGS_ACCESSIBILITY "\xef\x81\x9d" // U+f05d
#define ICON_MS_SETTINGS_ACCOUNT_BOX "\xef\xa0\xb5" // U+f835
#define ICON_MS_SETTINGS_ALERT "\xef\x85\x83" // U+f143
#define ICON_MS_SETTINGS_APPLICATIONS "\xee\xa2\xb9" // U+e8b9
#define ICON_MS_SETTINGS_B_ROLL "\xef\x98\xa5" // U+f625
#define ICON_MS_SETTINGS_BACKUP_RESTORE "\xee\xa2\xba" // U+e8ba
#define ICON_MS_SETTINGS_BLUETOOTH "\xee\xa2\xbb" // U+e8bb
#define ICON_MS_SETTINGS_BRIGHTNESS "\xee\xa2\xbd" // U+e8bd
#define ICON_MS_SETTINGS_CELL "\xee\xa2\xbc" // U+e8bc
#define ICON_MS_SETTINGS_CINEMATIC_BLUR "\xef\x98\xa4" // U+f624
#define ICON_MS_SETTINGS_ETHERNET "\xee\xa2\xbe" // U+e8be
#define ICON_MS_SETTINGS_HEART "\xef\x94\xa2" // U+f522
#define ICON_MS_SETTINGS_INPUT_ANTENNA "\xee\xa2\xbf" // U+e8bf
#define ICON_MS_SETTINGS_INPUT_COMPONENT "\xee\xa3\x81" // U+e8c1
#define ICON_MS_SETTINGS_INPUT_COMPOSITE "\xee\xa3\x81" // U+e8c1
#define ICON_MS_SETTINGS_INPUT_HDMI "\xee\xa3\x82" // U+e8c2
#define ICON_MS_SETTINGS_INPUT_SVIDEO "\xee\xa3\x83" // U+e8c3
#define ICON_MS_SETTINGS_MOTION_MODE "\xef\xa0\xb3" // U+f833
#define ICON_MS_SETTINGS_NIGHT_SIGHT "\xef\xa0\xb2" // U+f832
#define ICON_MS_SETTINGS_OVERSCAN "\xee\xa3\x84" // U+e8c4
#define ICON_MS_SETTINGS_PANORAMA "\xef\xa0\xb1" // U+f831
#define ICON_MS_SETTINGS_PHONE "\xee\xa3\x85" // U+e8c5
#define ICON_MS_SETTINGS_PHOTO_CAMERA "\xef\xa0\xb4" // U+f834
#define ICON_MS_SETTINGS_POWER "\xee\xa3\x86" // U+e8c6
#define ICON_MS_SETTINGS_REMOTE "\xee\xa3\x87" // U+e8c7
#define ICON_MS_SETTINGS_SLOW_MOTION "\xef\x98\xa3" // U+f623
#define ICON_MS_SETTINGS_SUGGEST "\xef\x81\x9e" // U+f05e
#define ICON_MS_SETTINGS_SYSTEM_DAYDREAM "\xee\x87\x83" // U+e1c3
#define ICON_MS_SETTINGS_TIMELAPSE "\xef\x98\xa2" // U+f622
#define ICON_MS_SETTINGS_VIDEO_CAMERA "\xef\x98\xa1" // U+f621
#define ICON_MS_SETTINGS_VOICE "\xee\xa3\x88" // U+e8c8
#define ICON_MS_SETTOP_COMPONENT "\xee\x8a\xac" // U+e2ac
#define ICON_MS_SEVERE_COLD "\xee\xaf\x93" // U+ebd3
#define ICON_MS_SHADOW "\xee\xa7\x9f" // U+e9df
#define ICON_MS_SHADOW_ADD "\xef\x96\x84" // U+f584
#define ICON_MS_SHADOW_MINUS "\xef\x96\x83" // U+f583
#define ICON_MS_SHAPE_LINE "\xef\xa3\x93" // U+f8d3
#define ICON_MS_SHAPE_RECOGNITION "\xee\xac\x81" // U+eb01
#define ICON_MS_SHAPES "\xee\x98\x82" // U+e602
#define ICON_MS_SHARE "\xee\xa0\x8d" // U+e80d
#define ICON_MS_SHARE_LOCATION "\xef\x81\x9f" // U+f05f
#define ICON_MS_SHARE_OFF "\xef\x9b\x8b" // U+f6cb
#define ICON_MS_SHARE_REVIEWS "\xef\xa2\xa4" // U+f8a4
#define ICON_MS_SHARE_WINDOWS "\xef\x98\x93" // U+f613
#define ICON_MS_SHEETS_RTL "\xef\xa0\xa3" // U+f823
#define ICON_MS_SHELF_AUTO_HIDE "\xef\x9c\x83" // U+f703
#define ICON_MS_SHELF_POSITION "\xef\x9c\x82" // U+f702
#define ICON_MS_SHELVES "\xef\xa1\xae" // U+f86e
#define ICON_MS_SHIELD "\xee\xa7\xa0" // U+e9e0
#define ICON_MS_SHIELD_LOCK "\xef\x9a\x86" // U+f686
#define ICON_MS_SHIELD_LOCKED "\xef\x96\x92" // U+f592
#define ICON_MS_SHIELD_MOON "\xee\xaa\xa9" // U+eaa9
#define ICON_MS_SHIELD_PERSON "\xef\x99\x90" // U+f650
#define ICON_MS_SHIELD_QUESTION "\xef\x94\xa9" // U+f529
#define ICON_MS_SHIELD_WITH_HEART "\xee\x9e\x8f" // U+e78f
#define ICON_MS_SHIELD_WITH_HOUSE "\xee\x9e\x8d" // U+e78d
#define ICON_MS_SHIFT "\xee\x97\xb2" // U+e5f2
#define ICON_MS_SHIFT_LOCK "\xef\x9e\xae" // U+f7ae
#define ICON_MS_SHIFT_LOCK_OFF "\xef\x92\x83" // U+f483
#define ICON_MS_SHOP "\xee\xa3\x89" // U+e8c9
#define ICON_MS_SHOP_2 "\xee\xa3\x8a" // U+e8ca
#define ICON_MS_SHOP_TWO "\xee\xa3\x8a" // U+e8ca
#define ICON_MS_SHOPPING_BAG "\xef\x87\x8c" // U+f1cc
#define ICON_MS_SHOPPING_BASKET "\xee\xa3\x8b" // U+e8cb
#define ICON_MS_SHOPPING_CART "\xee\xa3\x8c" // U+e8cc
#define ICON_MS_SHOPPING_CART_CHECKOUT "\xee\xae\x88" // U+eb88
#define ICON_MS_SHOPPING_CART_OFF "\xef\x93\xb7" // U+f4f7
#define ICON_MS_SHOPPINGMODE "\xee\xbe\xb7" // U+efb7
#define ICON_MS_SHORT_STAY "\xee\x93\x90" // U+e4d0
#define ICON_MS_SHORT_TEXT "\xee\x89\xa1" // U+e261
#define ICON_MS_SHORTCUT "\xef\x95\xba" // U+f57a
#define ICON_MS_SHOW_CHART "\xee\x9b\xa1" // U+e6e1
#define ICON_MS_SHOWER "\xef\x81\xa1" // U+f061
#define ICON_MS_SHUFFLE "\xee\x81\x83" // U+e043
#define ICON_MS_SHUFFLE_ON "\xee\xa7\xa1" // U+e9e1
#define ICON_MS_SHUTTER_SPEED "\xee\x90\xbd" // U+e43d
#define ICON_MS_SHUTTER_SPEED_ADD "\xef\x95\xbe" // U+f57e
#define ICON_MS_SHUTTER_SPEED_MINUS "\xef\x95\xbd" // U+f57d
#define ICON_MS_SICK "\xef\x88\xa0" // U+f220
#define ICON_MS_SIDE_NAVIGATION "\xee\xa7\xa2" // U+e9e2
#define ICON_MS_SIGN_LANGUAGE "\xee\xaf\xa5" // U+ebe5
#define ICON_MS_SIGNAL_CELLULAR_0_BAR "\xef\x82\xa8" // U+f0a8
#define ICON_MS_SIGNAL_CELLULAR_1_BAR "\xef\x82\xa9" // U+f0a9
#define ICON_MS_SIGNAL_CELLULAR_2_BAR "\xef\x82\xaa" // U+f0aa
#define ICON_MS_SIGNAL_CELLULAR_3_BAR "\xef\x82\xab" // U+f0ab
#define ICON_MS_SIGNAL_CELLULAR_4_BAR "\xee\x87\x88" // U+e1c8
#define ICON_MS_SIGNAL_CELLULAR_ADD "\xef\x9e\xa9" // U+f7a9
#define ICON_MS_SIGNAL_CELLULAR_ALT "\xee\x88\x82" // U+e202
#define ICON_MS_SIGNAL_CELLULAR_ALT_1_BAR "\xee\xaf\x9f" // U+ebdf
#define ICON_MS_SIGNAL_CELLULAR_ALT_2_BAR "\xee\xaf\xa3" // U+ebe3
#define ICON_MS_SIGNAL_CELLULAR_CONNECTED_NO_INTERNET_0_BAR "\xef\x82\xac" // U+f0ac
#define ICON_MS_SIGNAL_CELLULAR_CONNECTED_NO_INTERNET_4_BAR "\xee\x87\x8d" // U+e1cd
#define ICON_MS_SIGNAL_CELLULAR_NO_SIM "\xee\x87\x8e" // U+e1ce
#define ICON_MS_SIGNAL_CELLULAR_NODATA "\xef\x81\xa2" // U+f062
#define ICON_MS_SIGNAL_CELLULAR_NULL "\xee\x87\x8f" // U+e1cf
#define ICON_MS_SIGNAL_CELLULAR_OFF "\xee\x87\x90" // U+e1d0
#define ICON_MS_SIGNAL_CELLULAR_PAUSE "\xef\x96\xa7" // U+f5a7
#define ICON_MS_SIGNAL_DISCONNECTED "\xef\x88\xb9" // U+f239
#define ICON_MS_SIGNAL_WIFI_0_BAR "\xef\x82\xb0" // U+f0b0
#define ICON_MS_SIGNAL_WIFI_4_BAR "\xef\x81\xa5" // U+f065
#define ICON_MS_SIGNAL_WIFI_4_BAR_LOCK "\xee\x87\xa1" // U+e1e1
#define ICON_MS_SIGNAL_WIFI_BAD "\xef\x81\xa4" // U+f064
#define ICON_MS_SIGNAL_WIFI_CONNECTED_NO_INTERNET_4 "\xef\x81\xa4" // U+f064
#define ICON_MS_SIGNAL_WIFI_OFF "\xee\x87\x9a" // U+e1da
#define ICON_MS_SIGNAL_WIFI_STATUSBAR_4_BAR "\xef\x81\xa5" // U+f065
#define ICON_MS_SIGNAL_WIFI_STATUSBAR_NOT_CONNECTED "\xef\x83\xaf" // U+f0ef
#define ICON_MS_SIGNAL_WIFI_STATUSBAR_NULL "\xef\x81\xa7" // U+f067
#define ICON_MS_SIGNATURE "\xef\x9d\x8c" // U+f74c
#define ICON_MS_SIGNPOST "\xee\xae\x91" // U+eb91
#define ICON_MS_SIM_CARD "\xee\x8c\xab" // U+e32b
#define ICON_MS_SIM_CARD_ALERT "\xef\x81\x97" // U+f057
#define ICON_MS_SIM_CARD_DOWNLOAD "\xef\x81\xa8" // U+f068
#define ICON_MS_SINGLE_BED "\xee\xa9\x88" // U+ea48
#define ICON_MS_SIP "\xef\x81\xa9" // U+f069
#define ICON_MS_SKATEBOARDING "\xee\x94\x91" // U+e511
#define ICON_MS_SKELETON "\xef\xa2\x99" // U+f899
#define ICON_MS_SKILLET "\xef\x95\x83" // U+f543
#define ICON_MS_SKILLET_COOKTOP "\xef\x95\x84" // U+f544
#define ICON_MS_SKIP_NEXT "\xee\x81\x84" // U+e044
#define ICON_MS_SKIP_PREVIOUS "\xee\x81\x85" // U+e045
#define ICON_MS_SKULL "\xef\xa2\x9a" // U+f89a
#define ICON_MS_SLAB_SERIF "\xef\x92\xab" // U+f4ab
#define ICON_MS_SLEDDING "\xee\x94\x92" // U+e512
#define ICON_MS_SLEEP "\xee\x88\x93" // U+e213
#define ICON_MS_SLEEP_SCORE "\xef\x9a\xb7" // U+f6b7
#define ICON_MS_SLIDE_LIBRARY "\xef\xa0\xa2" // U+f822
#define ICON_MS_SLIDERS "\xee\xa7\xa3" // U+e9e3
#define ICON_MS_SLIDESHOW "\xee\x90\x9b" // U+e41b
#define ICON_MS_SLOW_MOTION_VIDEO "\xee\x81\xa8" // U+e068
#define ICON_MS_SMART_BUTTON "\xef\x87\x81" // U+f1c1
#define ICON_MS_SMART_CARD_READER "\xef\x92\xa5" // U+f4a5
#define ICON_MS_SMART_CARD_READER_OFF "\xef\x92\xa6" // U+f4a6
#define ICON_MS_SMART_DISPLAY "\xef\x81\xaa" // U+f06a
#define ICON_MS_SMART_OUTLET "\xee\xa1\x84" // U+e844
#define ICON_MS_SMART_SCREEN "\xef\x81\xab" // U+f06b
#define ICON_MS_SMART_TOY "\xef\x81\xac" // U+f06c
#define ICON_MS_SMARTPHONE "\xee\x8c\xac" // U+e32c
#define ICON_MS_SMB_SHARE "\xef\x9d\x8b" // U+f74b
#define ICON_MS_SMOKE_FREE "\xee\xad\x8a" // U+eb4a
#define ICON_MS_SMOKING_ROOMS "\xee\xad\x8b" // U+eb4b
#define ICON_MS_SMS "\xee\x98\xa5" // U+e625
#define ICON_MS_SMS_FAILED "\xee\xa1\xbf" // U+e87f
#define ICON_MS_SNIPPET_FOLDER "\xef\x87\x87" // U+f1c7
#define ICON_MS_SNOOZE "\xee\x81\x86" // U+e046
#define ICON_MS_SNOWBOARDING "\xee\x94\x93" // U+e513
#define ICON_MS_SNOWING "\xee\xa0\x8f" // U+e80f
#define ICON_MS_SNOWING_HEAVY "\xef\x98\x9c" // U+f61c
#define ICON_MS_SNOWMOBILE "\xee\x94\x83" // U+e503
#define ICON_MS_SNOWSHOEING "\xee\x94\x94" // U+e514
#define ICON_MS_SOAP "\xef\x86\xb2" // U+f1b2
#define ICON_MS_SOCIAL_DISTANCE "\xee\x87\x8b" // U+e1cb
#define ICON_MS_SOCIAL_LEADERBOARD "\xef\x9a\xa0" // U+f6a0
#define ICON_MS_SOLAR_POWER "\xee\xb0\x8f" // U+ec0f
#define ICON_MS_SORT "\xee\x85\xa4" // U+e164
#define ICON_MS_SORT_BY_ALPHA "\xee\x81\x93" // U+e053
#define ICON_MS_SOS "\xee\xaf\xb7" // U+ebf7
#define ICON_MS_SOUND_DETECTION_DOG_BARKING "\xef\x85\x89" // U+f149
#define ICON_MS_SOUND_DETECTION_GLASS_BREAK "\xef\x85\x8a" // U+f14a
#define ICON_MS_SOUND_DETECTION_LOUD_SOUND "\xef\x85\x8b" // U+f14b
#define ICON_MS_SOUND_SAMPLER "\xef\x9a\xb4" // U+f6b4
#define ICON_MS_SOUP_KITCHEN "\xee\x9f\x93" // U+e7d3
#define ICON_MS_SOURCE "\xef\x87\x88" // U+f1c8
#define ICON_MS_SOURCE_ENVIRONMENT "\xee\x94\xa7" // U+e527
#define ICON_MS_SOURCE_NOTES "\xee\x84\xad" // U+e12d
#define ICON_MS_SOUTH "\xef\x87\xa3" // U+f1e3
#define ICON_MS_SOUTH_AMERICA "\xee\x9f\xa4" // U+e7e4
#define ICON_MS_SOUTH_EAST "\xef\x87\xa4" // U+f1e4
#define ICON_MS_SOUTH_WEST "\xef\x87\xa5" // U+f1e5
#define ICON_MS_SPA "\xee\xad\x8c" // U+eb4c
#define ICON_MS_SPACE_BAR "\xee\x89\x96" // U+e256
#define ICON_MS_SPACE_DASHBOARD "\xee\x99\xab" // U+e66b
#define ICON_MS_SPATIAL_AUDIO "\xee\xaf\xab" // U+ebeb
#define ICON_MS_SPATIAL_AUDIO_OFF "\xee\xaf\xa8" // U+ebe8
#define ICON_MS_SPATIAL_SPEAKER "\xef\x93\x8f" // U+f4cf
#define ICON_MS_SPATIAL_TRACKING "\xee\xaf\xaa" // U+ebea
#define ICON_MS_SPEAKER "\xee\x8c\xad" // U+e32d
#define ICON_MS_SPEAKER_GROUP "\xee\x8c\xae" // U+e32e
#define ICON_MS_SPEAKER_NOTES "\xee\xa3\x8d" // U+e8cd
#define ICON_MS_SPEAKER_NOTES_OFF "\xee\xa4\xaa" // U+e92a
#define ICON_MS_SPEAKER_PHONE "\xee\x83\x92" // U+e0d2
#define ICON_MS_SPECIAL_CHARACTER "\xef\x9d\x8a" // U+f74a
#define ICON_MS_SPECIFIC_GRAVITY "\xef\xa1\xb2" // U+f872
#define ICON_MS_SPEECH_TO_TEXT "\xef\xa2\xa7" // U+f8a7
#define ICON_MS_SPEED "\xee\xa7\xa4" // U+e9e4
#define ICON_MS_SPEED_0_25 "\xef\x93\x94" // U+f4d4
#define ICON_MS_SPEED_0_2X "\xef\x92\x98" // U+f498
#define ICON_MS_SPEED_0_5 "\xef\x93\xa2" // U+f4e2
#define ICON_MS_SPEED_0_5X "\xef\x92\x97" // U+f497
#define ICON_MS_SPEED_0_75 "\xef\x93\x93" // U+f4d3
#define ICON_MS_SPEED_0_7X "\xef\x92\x96" // U+f496
#define ICON_MS_SPEED_1_2 "\xef\x93\xa1" // U+f4e1
#define ICON_MS_SPEED_1_25 "\xef\x93\x92" // U+f4d2
#define ICON_MS_SPEED_1_2X "\xef\x92\x95" // U+f495
#define ICON_MS_SPEED_1_5 "\xef\x93\xa0" // U+f4e0
#define ICON_MS_SPEED_1_5X "\xef\x92\x94" // U+f494
#define ICON_MS_SPEED_1_75 "\xef\x93\x91" // U+f4d1
#define ICON_MS_SPEED_1_7X "\xef\x92\x93" // U+f493
#define ICON_MS_SPEED_2X "\xef\x93\xab" // U+f4eb
#define ICON_MS_SPELLCHECK "\xee\xa3\x8e" // U+e8ce
#define ICON_MS_SPLITSCREEN "\xef\x81\xad" // U+f06d
#define ICON_MS_SPLITSCREEN_ADD "\xef\x93\xbd" // U+f4fd
#define ICON_MS_SPLITSCREEN_BOTTOM "\xef\x99\xb6" // U+f676
#define ICON_MS_SPLITSCREEN_LEFT "\xef\x99\xb5" // U+f675
#define ICON_MS_SPLITSCREEN_RIGHT "\xef\x99\xb4" // U+f674
#define ICON_MS_SPLITSCREEN_TOP "\xef\x99\xb3" // U+f673
#define ICON_MS_SPLITSCREEN_VERTICAL_ADD "\xef\x93\xbc" // U+f4fc
#define ICON_MS_SPO2 "\xef\x9b\x9b" // U+f6db
#define ICON_MS_SPOKE "\xee\xa6\xa7" // U+e9a7
#define ICON_MS_SPORTS "\xee\xa8\xb0" // U+ea30
#define ICON_MS_SPORTS_AND_OUTDOORS "\xee\xbe\xb8" // U+efb8
#define ICON_MS_SPORTS_BAR "\xef\x87\xb3" // U+f1f3
#define ICON_MS_SPORTS_BASEBALL "\xee\xa9\x91" // U+ea51
#define ICON_MS_SPORTS_BASKETBALL "\xee\xa8\xa6" // U+ea26
#define ICON_MS_SPORTS_CRICKET "\xee\xa8\xa7" // U+ea27
#define ICON_MS_SPORTS_ESPORTS "\xee\xa8\xa8" // U+ea28
#define ICON_MS_SPORTS_FOOTBALL "\xee\xa8\xa9" // U+ea29
#define ICON_MS_SPORTS_GOLF "\xee\xa8\xaa" // U+ea2a
#define ICON_MS_SPORTS_GYMNASTICS "\xee\xaf\x84" // U+ebc4
#define ICON_MS_SPORTS_HANDBALL "\xee\xa8\xb3" // U+ea33
#define ICON_MS_SPORTS_HOCKEY "\xee\xa8\xab" // U+ea2b
#define ICON_MS_SPORTS_KABADDI "\xee\xa8\xb4" // U+ea34
#define ICON_MS_SPORTS_MARTIAL_ARTS "\xee\xab\xa9" // U+eae9
#define ICON_MS_SPORTS_MMA "\xee\xa8\xac" // U+ea2c
#define ICON_MS_SPORTS_MOTORSPORTS "\xee\xa8\xad" // U+ea2d
#define ICON_MS_SPORTS_RUGBY "\xee\xa8\xae" // U+ea2e
#define ICON_MS_SPORTS_SCORE "\xef\x81\xae" // U+f06e
#define ICON_MS_SPORTS_SOCCER "\xee\xa8\xaf" // U+ea2f
#define ICON_MS_SPORTS_TENNIS "\xee\xa8\xb2" // U+ea32
#define ICON_MS_SPORTS_VOLLEYBALL "\xee\xa8\xb1" // U+ea31
#define ICON_MS_SPRINKLER "\xee\x8a\x9a" // U+e29a
#define ICON_MS_SPRINT "\xef\xa0\x9f" // U+f81f
#define ICON_MS_SQUARE "\xee\xac\xb6" // U+eb36
#define ICON_MS_SQUARE_FOOT "\xee\xa9\x89" // U+ea49
#define ICON_MS_SSID_CHART "\xee\xad\xa6" // U+eb66
#define ICON_MS_STACK "\xef\x98\x89" // U+f609
#define ICON_MS_STACK_OFF "\xef\x98\x88" // U+f608
#define ICON_MS_STACK_STAR "\xef\x98\x87" // U+f607
#define ICON_MS_STACKED_BAR_CHART "\xee\xa7\xa6" // U+e9e6
#define ICON_MS_STACKED_EMAIL "\xee\x9b\x87" // U+e6c7
#define ICON_MS_STACKED_INBOX "\xee\x9b\x89" // U+e6c9
#define ICON_MS_STACKED_LINE_CHART "\xef\x88\xab" // U+f22b
#define ICON_MS_STACKS "\xef\x94\x80" // U+f500
#define ICON_MS_STADIA_CONTROLLER "\xef\x84\xb5" // U+f135
#define ICON_MS_STADIUM "\xee\xae\x90" // U+eb90
#define ICON_MS_STAIRS "\xef\x86\xa9" // U+f1a9
#define ICON_MS_STAR "\xef\x82\x9a" // U+f09a
#define ICON_MS_STAR_BORDER "\xef\x82\x9a" // U+f09a
#define ICON_MS_STAR_BORDER_PURPLE500 "\xef\x82\x9a" // U+f09a
#define ICON_MS_STAR_HALF "\xee\xa0\xb9" // U+e839
#define ICON_MS_STAR_OUTLINE "\xef\x82\x9a" // U+f09a
#define ICON_MS_STAR_PURPLE500 "\xef\x82\x9a" // U+f09a
#define ICON_MS_STAR_RATE "\xef\x83\xac" // U+f0ec
#define ICON_MS_STAR_RATE_HALF "\xee\xb1\x85" // U+ec45
#define ICON_MS_STARS "\xee\xa3\x90" // U+e8d0
#define ICON_MS_START "\xee\x82\x89" // U+e089
#define ICON_MS_STAT_0 "\xee\x9a\x97" // U+e697
#define ICON_MS_STAT_1 "\xee\x9a\x98" // U+e698
#define ICON_MS_STAT_2 "\xee\x9a\x99" // U+e699
#define ICON_MS_STAT_3 "\xee\x9a\x9a" // U+e69a
#define ICON_MS_STAT_MINUS_1 "\xee\x9a\x9b" // U+e69b
#define ICON_MS_STAT_MINUS_2 "\xee\x9a\x9c" // U+e69c
#define ICON_MS_STAT_MINUS_3 "\xee\x9a\x9d" // U+e69d
#define ICON_MS_STAY_CURRENT_LANDSCAPE "\xee\x83\x93" // U+e0d3
#define ICON_MS_STAY_CURRENT_PORTRAIT "\xee\x83\x94" // U+e0d4
#define ICON_MS_STAY_PRIMARY_LANDSCAPE "\xee\x83\x95" // U+e0d5
#define ICON_MS_STAY_PRIMARY_PORTRAIT "\xee\x83\x96" // U+e0d6
#define ICON_MS_STEP "\xef\x9b\xbe" // U+f6fe
#define ICON_MS_STEP_INTO "\xef\x9c\x81" // U+f701
#define ICON_MS_STEP_OUT "\xef\x9c\x80" // U+f700
#define ICON_MS_STEP_OVER "\xef\x9b\xbf" // U+f6ff
#define ICON_MS_STEPPERS "\xee\xa7\xa7" // U+e9e7
#define ICON_MS_STEPS "\xef\x9b\x9a" // U+f6da
#define ICON_MS_STETHOSCOPE "\xef\xa0\x85" // U+f805
#define ICON_MS_STETHOSCOPE_ARROW "\xef\xa0\x87" // U+f807
#define ICON_MS_STETHOSCOPE_CHECK "\xef\xa0\x86" // U+f806
#define ICON_MS_STICKY_NOTE "\xee\xa7\xa8" // U+e9e8
#define ICON_MS_STICKY_NOTE_2 "\xef\x87\xbc" // U+f1fc
#define ICON_MS_STOCK_MEDIA "\xef\x95\xb0" // U+f570
#define ICON_MS_STOCKPOT "\xef\x95\x85" // U+f545
#define ICON_MS_STOP "\xee\x81\x87" // U+e047
#define ICON_MS_STOP_CIRCLE "\xee\xbd\xb1" // U+ef71
#define ICON_MS_STOP_SCREEN_SHARE "\xee\x83\xa3" // U+e0e3
#define ICON_MS_STORAGE "\xee\x87\x9b" // U+e1db
#define ICON_MS_STORE "\xee\xa3\x91" // U+e8d1
#define ICON_MS_STORE_MALL_DIRECTORY "\xee\xa3\x91" // U+e8d1
#define ICON_MS_STOREFRONT "\xee\xa8\x92" // U+ea12
#define ICON_MS_STORM "\xef\x81\xb0" // U+f070
#define ICON_MS_STRAIGHT "\xee\xae\x95" // U+eb95
#define ICON_MS_STRAIGHTEN "\xee\x90\x9c" // U+e41c
#define ICON_MS_STRATEGY "\xef\x97\x9f" // U+f5df
#define ICON_MS_STREAM "\xee\xa7\xa9" // U+e9e9
#define ICON_MS_STREAM_APPS "\xef\x9e\x9f" // U+f79f
#define ICON_MS_STREETVIEW "\xee\x95\xae" // U+e56e
#define ICON_MS_STRESS_MANAGEMENT "\xef\x9b\x99" // U+f6d9
#define ICON_MS_STRIKETHROUGH_S "\xee\x89\x97" // U+e257
#define ICON_MS_STROKE_FULL "\xef\x9d\x89" // U+f749
#define ICON_MS_STROKE_PARTIAL "\xef\x9d\x88" // U+f748
#define ICON_MS_STROLLER "\xef\x86\xae" // U+f1ae
#define ICON_MS_STYLE "\xee\x90\x9d" // U+e41d
#define ICON_MS_STYLER "\xee\x89\xb3" // U+e273
#define ICON_MS_STYLUS "\xef\x98\x84" // U+f604
#define ICON_MS_STYLUS_LASER_POINTER "\xef\x9d\x87" // U+f747
#define ICON_MS_STYLUS_NOTE "\xef\x98\x83" // U+f603
#define ICON_MS_SUBDIRECTORY_ARROW_LEFT "\xee\x97\x99" // U+e5d9
#define ICON_MS_SUBDIRECTORY_ARROW_RIGHT "\xee\x97\x9a" // U+e5da
#define ICON_MS_SUBHEADER "\xee\xa7\xaa" // U+e9ea
#define ICON_MS_SUBJECT "\xee\xa3\x92" // U+e8d2
#define ICON_MS_SUBSCRIPT "\xef\x84\x91" // U+f111
#define ICON_MS_SUBSCRIPTIONS "\xee\x81\xa4" // U+e064
#define ICON_MS_SUBTITLES "\xee\x81\x88" // U+e048
#define ICON_MS_SUBTITLES_OFF "\xee\xbd\xb2" // U+ef72
#define ICON_MS_SUBWAY "\xee\x95\xaf" // U+e56f
#define ICON_MS_SUMMARIZE "\xef\x81\xb1" // U+f071
#define ICON_MS_SUNNY "\xee\xa0\x9a" // U+e81a
#define ICON_MS_SUNNY_SNOWING "\xee\xa0\x99" // U+e819
#define ICON_MS_SUPERSCRIPT "\xef\x84\x92" // U+f112
#define ICON_MS_SUPERVISED_USER_CIRCLE "\xee\xa4\xb9" // U+e939
#define ICON_MS_SUPERVISED_USER_CIRCLE_OFF "\xef\x98\x8e" // U+f60e
#define ICON_MS_SUPERVISOR_ACCOUNT "\xee\xa3\x93" // U+e8d3
#define ICON_MS_SUPPORT "\xee\xbd\xb3" // U+ef73
#define ICON_MS_SUPPORT_AGENT "\xef\x83\xa2" // U+f0e2
#define ICON_MS_SURFING "\xee\x94\x95" // U+e515
#define ICON_MS_SURGICAL "\xee\x84\xb1" // U+e131
#define ICON_MS_SURROUND_SOUND "\xee\x81\x89" // U+e049
#define ICON_MS_SWAP_CALLS "\xee\x83\x97" // U+e0d7
#define ICON_MS_SWAP_DRIVING_APPS "\xee\x9a\x9e" // U+e69e
#define ICON_MS_SWAP_DRIVING_APPS_WHEEL "\xee\x9a\x9f" // U+e69f
#define ICON_MS_SWAP_HORIZ "\xee\xa3\x94" // U+e8d4
#define ICON_MS_SWAP_HORIZONTAL_CIRCLE "\xee\xa4\xb3" // U+e933
#define ICON_MS_SWAP_VERT "\xee\xa3\x95" // U+e8d5
#define ICON_MS_SWAP_VERTICAL_CIRCLE "\xee\xa3\x96" // U+e8d6
#define ICON_MS_SWEEP "\xee\x9a\xac" // U+e6ac
#define ICON_MS_SWIPE "\xee\xa7\xac" // U+e9ec
#define ICON_MS_SWIPE_DOWN "\xee\xad\x93" // U+eb53
#define ICON_MS_SWIPE_DOWN_ALT "\xee\xac\xb0" // U+eb30
#define ICON_MS_SWIPE_LEFT "\xee\xad\x99" // U+eb59
#define ICON_MS_SWIPE_LEFT_ALT "\xee\xac\xb3" // U+eb33
#define ICON_MS_SWIPE_RIGHT "\xee\xad\x92" // U+eb52
#define ICON_MS_SWIPE_RIGHT_ALT "\xee\xad\x96" // U+eb56
#define ICON_MS_SWIPE_UP "\xee\xac\xae" // U+eb2e
#define ICON_MS_SWIPE_UP_ALT "\xee\xac\xb5" // U+eb35
#define ICON_MS_SWIPE_VERTICAL "\xee\xad\x91" // U+eb51
#define ICON_MS_SWITCH "\xee\x87\xb4" // U+e1f4
#define ICON_MS_SWITCH_ACCESS "\xef\x9b\xbd" // U+f6fd
#define ICON_MS_SWITCH_ACCESS_2 "\xef\x94\x86" // U+f506
#define ICON_MS_SWITCH_ACCESS_SHORTCUT "\xee\x9f\xa1" // U+e7e1
#define ICON_MS_SWITCH_ACCESS_SHORTCUT_ADD "\xee\x9f\xa2" // U+e7e2
#define ICON_MS_SWITCH_ACCOUNT "\xee\xa7\xad" // U+e9ed
#define ICON_MS_SWITCH_CAMERA "\xee\x90\x9e" // U+e41e
#define ICON_MS_SWITCH_LEFT "\xef\x87\x91" // U+f1d1
#define ICON_MS_SWITCH_RIGHT "\xef\x87\x92" // U+f1d2
#define ICON_MS_SWITCH_VIDEO "\xee\x90\x9f" // U+e41f
#define ICON_MS_SWITCHES "\xee\x9c\xb3" // U+e733
#define ICON_MS_SWORD_ROSE "\xef\x97\x9e" // U+f5de
#define ICON_MS_SWORDS "\xef\xa2\x89" // U+f889
#define ICON_MS_SYMPTOMS "\xee\x84\xb2" // U+e132
#define ICON_MS_SYNAGOGUE "\xee\xaa\xb0" // U+eab0
#define ICON_MS_SYNC "\xee\x98\xa7" // U+e627
#define ICON_MS_SYNC_ALT "\xee\xa8\x98" // U+ea18
#define ICON_MS_SYNC_DISABLED "\xee\x98\xa8" // U+e628
#define ICON_MS_SYNC_LOCK "\xee\xab\xae" // U+eaee
#define ICON_MS_SYNC_PROBLEM "\xee\x98\xa9" // U+e629
#define ICON_MS_SYNC_SAVED_LOCALLY "\xef\xa0\xa0" // U+f820
#define ICON_MS_SYRINGE "\xee\x84\xb3" // U+e133
#define ICON_MS_SYSTEM_SECURITY_UPDATE "\xef\x81\xb2" // U+f072
#define ICON_MS_SYSTEM_SECURITY_UPDATE_GOOD "\xef\x81\xb3" // U+f073
#define ICON_MS_SYSTEM_SECURITY_UPDATE_WARNING "\xef\x81\xb4" // U+f074
#define ICON_MS_SYSTEM_UPDATE "\xef\x81\xb2" // U+f072
#define ICON_MS_SYSTEM_UPDATE_ALT "\xee\xa3\x97" // U+e8d7
#define ICON_MS_TAB "\xee\xa3\x98" // U+e8d8
#define ICON_MS_TAB_CLOSE "\xef\x9d\x85" // U+f745
#define ICON_MS_TAB_CLOSE_RIGHT "\xef\x9d\x86" // U+f746
#define ICON_MS_TAB_DUPLICATE "\xef\x9d\x84" // U+f744
#define ICON_MS_TAB_GROUP "\xef\x9d\x83" // U+f743
#define ICON_MS_TAB_MOVE "\xef\x9d\x82" // U+f742
#define ICON_MS_TAB_NEW_RIGHT "\xef\x9d\x81" // U+f741
#define ICON_MS_TAB_RECENT "\xef\x9d\x80" // U+f740
#define ICON_MS_TAB_UNSELECTED "\xee\xa3\x99" // U+e8d9
#define ICON_MS_TABLE "\xef\x86\x91" // U+f191
#define ICON_MS_TABLE_BAR "\xee\xab\x92" // U+ead2
#define ICON_MS_TABLE_CHART "\xee\x89\xa5" // U+e265
#define ICON_MS_TABLE_CHART_VIEW "\xef\x9b\xaf" // U+f6ef
#define ICON_MS_TABLE_LAMP "\xee\x87\xb2" // U+e1f2
#define ICON_MS_TABLE_RESTAURANT "\xee\xab\x86" // U+eac6
#define ICON_MS_TABLE_ROWS "\xef\x84\x81" // U+f101
#define ICON_MS_TABLE_ROWS_NARROW "\xef\x9c\xbf" // U+f73f
#define ICON_MS_TABLE_VIEW "\xef\x86\xbe" // U+f1be
#define ICON_MS_TABLET "\xee\x8c\xaf" // U+e32f
#define ICON_MS_TABLET_ANDROID "\xee\x8c\xb0" // U+e330
#define ICON_MS_TABLET_MAC "\xee\x8c\xb1" // U+e331
#define ICON_MS_TABS "\xee\xa7\xae" // U+e9ee
#define ICON_MS_TACTIC "\xef\x95\xa4" // U+f564
#define ICON_MS_TAG "\xee\xa7\xaf" // U+e9ef
#define ICON_MS_TAG_FACES "\xee\xa8\xa2" // U+ea22
#define ICON_MS_TAKEOUT_DINING "\xee\xa9\xb4" // U+ea74
#define ICON_MS_TAMPER_DETECTION_OFF "\xee\xa0\xae" // U+e82e
#define ICON_MS_TAMPER_DETECTION_ON "\xef\xa3\x88" // U+f8c8
#define ICON_MS_TAP_AND_PLAY "\xee\x98\xab" // U+e62b
#define ICON_MS_TAPAS "\xef\x87\xa9" // U+f1e9
#define ICON_MS_TARGET "\xee\x9c\x99" // U+e719
#define ICON_MS_TASK "\xef\x81\xb5" // U+f075
#define ICON_MS_TASK_ALT "\xee\x8b\xa6" // U+e2e6
#define ICON_MS_TAUNT "\xef\x9a\x9f" // U+f69f
#define ICON_MS_TAXI_ALERT "\xee\xbd\xb4" // U+ef74
#define ICON_MS_TEAM_DASHBOARD "\xee\x80\x93" // U+e013
#define ICON_MS_TEMP_PREFERENCES_CUSTOM "\xef\xa3\x89" // U+f8c9
#define ICON_MS_TEMP_PREFERENCES_ECO "\xef\xa3\x8a" // U+f8ca
#define ICON_MS_TEMPLE_BUDDHIST "\xee\xaa\xb3" // U+eab3
#define ICON_MS_TEMPLE_HINDU "\xee\xaa\xaf" // U+eaaf
#define ICON_MS_TENANCY "\xef\x83\xa3" // U+f0e3
#define ICON_MS_TERMINAL "\xee\xae\x8e" // U+eb8e
#define ICON_MS_TERRAIN "\xee\x95\xa4" // U+e564
#define ICON_MS_TEXT_AD "\xee\x9c\xa8" // U+e728
#define ICON_MS_TEXT_DECREASE "\xee\xab\x9d" // U+eadd
#define ICON_MS_TEXT_FIELDS "\xee\x89\xa2" // U+e262
#define ICON_MS_TEXT_FIELDS_ALT "\xee\xa7\xb1" // U+e9f1
#define ICON_MS_TEXT_FORMAT "\xee\x85\xa5" // U+e165
#define ICON_MS_TEXT_INCREASE "\xee\xab\xa2" // U+eae2
#define ICON_MS_TEXT_ROTATE_UP "\xee\xa4\xba" // U+e93a
#define ICON_MS_TEXT_ROTATE_VERTICAL "\xee\xa4\xbb" // U+e93b
#define ICON_MS_TEXT_ROTATION_ANGLEDOWN "\xee\xa4\xbc" // U+e93c
#define ICON_MS_TEXT_ROTATION_ANGLEUP "\xee\xa4\xbd" // U+e93d
#define ICON_MS_TEXT_ROTATION_DOWN "\xee\xa4\xbe" // U+e93e
#define ICON_MS_TEXT_ROTATION_NONE "\xee\xa4\xbf" // U+e93f
#define ICON_MS_TEXT_SELECT_END "\xef\x9c\xbe" // U+f73e
#define ICON_MS_TEXT_SELECT_JUMP_TO_BEGINNING "\xef\x9c\xbd" // U+f73d
#define ICON_MS_TEXT_SELECT_JUMP_TO_END "\xef\x9c\xbc" // U+f73c
#define ICON_MS_TEXT_SELECT_MOVE_BACK_CHARACTER "\xef\x9c\xbb" // U+f73b
#define ICON_MS_TEXT_SELECT_MOVE_BACK_WORD "\xef\x9c\xba" // U+f73a
#define ICON_MS_TEXT_SELECT_MOVE_DOWN "\xef\x9c\xb9" // U+f739
#define ICON_MS_TEXT_SELECT_MOVE_FORWARD_CHARACTER "\xef\x9c\xb8" // U+f738
#define ICON_MS_TEXT_SELECT_MOVE_FORWARD_WORD "\xef\x9c\xb7" // U+f737
#define ICON_MS_TEXT_SELECT_MOVE_UP "\xef\x9c\xb6" // U+f736
#define ICON_MS_TEXT_SELECT_START "\xef\x9c\xb5" // U+f735
#define ICON_MS_TEXT_SNIPPET "\xef\x87\x86" // U+f1c6
#define ICON_MS_TEXT_TO_SPEECH "\xef\x86\xbc" // U+f1bc
#define ICON_MS_TEXT_UP "\xef\x92\x9e" // U+f49e
#define ICON_MS_TEXTSMS "\xee\x98\xa5" // U+e625
#define ICON_MS_TEXTURE "\xee\x90\xa1" // U+e421
#define ICON_MS_TEXTURE_ADD "\xef\x95\xbc" // U+f57c
#define ICON_MS_TEXTURE_MINUS "\xef\x95\xbb" // U+f57b
#define ICON_MS_THEATER_COMEDY "\xee\xa9\xa6" // U+ea66
#define ICON_MS_THEATERS "\xee\xa3\x9a" // U+e8da
#define ICON_MS_THERMOMETER "\xee\xa1\x86" // U+e846
#define ICON_MS_THERMOMETER_ADD "\xef\x96\x82" // U+f582
#define ICON_MS_THERMOMETER_GAIN "\xef\x9b\x98" // U+f6d8
#define ICON_MS_THERMOMETER_LOSS "\xef\x9b\x97" // U+f6d7
#define ICON_MS_THERMOMETER_MINUS "\xef\x96\x81" // U+f581
#define ICON_MS_THERMOSTAT "\xef\x81\xb6" // U+f076
#define ICON_MS_THERMOSTAT_AUTO "\xef\x81\xb7" // U+f077
#define ICON_MS_THERMOSTAT_CARBON "\xef\x85\xb8" // U+f178
#define ICON_MS_THINGS_TO_DO "\xee\xac\xaa" // U+eb2a
#define ICON_MS_THREAD_UNREAD "\xef\x93\xb9" // U+f4f9
#define ICON_MS_THUMB_DOWN "\xef\x95\xb8" // U+f578
#define ICON_MS_THUMB_DOWN_ALT "\xef\x95\xb8" // U+f578
#define ICON_MS_THUMB_DOWN_FILLED "\xef\x95\xb8" // U+f578
#define ICON_MS_THUMB_DOWN_OFF "\xef\x95\xb8" // U+f578
#define ICON_MS_THUMB_DOWN_OFF_ALT "\xef\x95\xb8" // U+f578
#define ICON_MS_THUMB_UP "\xef\x95\xb7" // U+f577
#define ICON_MS_THUMB_UP_ALT "\xef\x95\xb7" // U+f577
#define ICON_MS_THUMB_UP_FILLED "\xef\x95\xb7" // U+f577
#define ICON_MS_THUMB_UP_OFF "\xef\x95\xb7" // U+f577
#define ICON_MS_THUMB_UP_OFF_ALT "\xef\x95\xb7" // U+f577
#define ICON_MS_THUMBNAIL_BAR "\xef\x9c\xb4" // U+f734
#define ICON_MS_THUMBS_UP_DOWN "\xee\xa3\x9d" // U+e8dd
#define ICON_MS_THUNDERSTORM "\xee\xaf\x9b" // U+ebdb
#define ICON_MS_TIBIA "\xef\xa2\x9b" // U+f89b
#define ICON_MS_TIBIA_ALT "\xef\xa2\x9c" // U+f89c
#define ICON_MS_TIME_AUTO "\xef\x83\xa4" // U+f0e4
#define ICON_MS_TIME_TO_LEAVE "\xee\xbf\xb7" // U+eff7
#define ICON_MS_TIMELAPSE "\xee\x90\xa2" // U+e422
#define ICON_MS_TIMELINE "\xee\xa4\xa2" // U+e922
#define ICON_MS_TIMER "\xee\x90\xa5" // U+e425
#define ICON_MS_TIMER_10 "\xee\x90\xa3" // U+e423
#define ICON_MS_TIMER_10_ALT_1 "\xee\xbe\xbf" // U+efbf
#define ICON_MS_TIMER_10_SELECT "\xef\x81\xba" // U+f07a
#define ICON_MS_TIMER_3 "\xee\x90\xa4" // U+e424
#define ICON_MS_TIMER_3_ALT_1 "\xee\xbf\x80" // U+efc0
#define ICON_MS_TIMER_3_SELECT "\xef\x81\xbb" // U+f07b
#define ICON_MS_TIMER_5 "\xef\x92\xb1" // U+f4b1
#define ICON_MS_TIMER_5_SHUTTER "\xef\x92\xb2" // U+f4b2
#define ICON_MS_TIMER_OFF "\xee\x90\xa6" // U+e426
#define ICON_MS_TIMER_PAUSE "\xef\x92\xbb" // U+f4bb
#define ICON_MS_TIMER_PLAY "\xef\x92\xba" // U+f4ba
#define ICON_MS_TIPS_AND_UPDATES "\xee\x9e\x9a" // U+e79a
#define ICON_MS_TIRE_REPAIR "\xee\xaf\x88" // U+ebc8
#define ICON_MS_TITLE "\xee\x89\xa4" // U+e264
#define ICON_MS_TITLECASE "\xef\x92\x89" // U+f489
#define ICON_MS_TOAST "\xee\xbf\x81" // U+efc1
#define ICON_MS_TOC "\xee\xa3\x9e" // U+e8de
#define ICON_MS_TODAY "\xee\xa3\x9f" // U+e8df
#define ICON_MS_TOGGLE_OFF "\xee\xa7\xb5" // U+e9f5
#define ICON_MS_TOGGLE_ON "\xee\xa7\xb6" // U+e9f6
#define ICON_MS_TOKEN "\xee\xa8\xa5" // U+ea25
#define ICON_MS_TOLL "\xee\xa3\xa0" // U+e8e0
#define ICON_MS_TONALITY "\xee\x90\xa7" // U+e427
#define ICON_MS_TOOLBAR "\xee\xa7\xb7" // U+e9f7
#define ICON_MS_TOOLS_FLAT_HEAD "\xef\xa3\x8b" // U+f8cb
#define ICON_MS_TOOLS_INSTALLATION_KIT "\xee\x8a\xab" // U+e2ab
#define ICON_MS_TOOLS_LADDER "\xee\x8b\x8b" // U+e2cb
#define ICON_MS_TOOLS_LEVEL "\xee\x9d\xbb" // U+e77b
#define ICON_MS_TOOLS_PHILLIPS "\xef\xa3\x8c" // U+f8cc
#define ICON_MS_TOOLS_PLIERS_WIRE_STRIPPER "\xee\x8a\xaa" // U+e2aa
#define ICON_MS_TOOLS_POWER_DRILL "\xee\x87\xa9" // U+e1e9
#define ICON_MS_TOOLS_WRENCH "\xef\xa3\x8d" // U+f8cd
#define ICON_MS_TOOLTIP "\xee\xa7\xb8" // U+e9f8
#define ICON_MS_TOP_PANEL_CLOSE "\xef\x9c\xb3" // U+f733
#define ICON_MS_TOP_PANEL_OPEN "\xef\x9c\xb2" // U+f732
#define ICON_MS_TOPIC "\xef\x87\x88" // U+f1c8
#define ICON_MS_TORNADO "\xee\x86\x99" // U+e199
#define ICON_MS_TOTAL_DISSOLVED_SOLIDS "\xef\xa1\xb7" // U+f877
#define ICON_MS_TOUCH_APP "\xee\xa4\x93" // U+e913
#define ICON_MS_TOUCHPAD_MOUSE "\xef\x9a\x87" // U+f687
#define ICON_MS_TOUCHPAD_MOUSE_OFF "\xef\x93\xa6" // U+f4e6
#define ICON_MS_TOUR "\xee\xbd\xb5" // U+ef75
#define ICON_MS_TOYS "\xee\x8c\xb2" // U+e332
#define ICON_MS_TOYS_AND_GAMES "\xee\xbf\x82" // U+efc2
#define ICON_MS_TOYS_FAN "\xef\xa2\x87" // U+f887
#define ICON_MS_TRACK_CHANGES "\xee\xa3\xa1" // U+e8e1
#define ICON_MS_TRACKPAD_INPUT "\xef\x93\x87" // U+f4c7
#define ICON_MS_TRAFFIC "\xee\x95\xa5" // U+e565
#define ICON_MS_TRAIL_LENGTH "\xee\xad\x9e" // U+eb5e
#define ICON_MS_TRAIL_LENGTH_MEDIUM "\xee\xad\xa3" // U+eb63
#define ICON_MS_TRAIL_LENGTH_SHORT "\xee\xad\xad" // U+eb6d
#define ICON_MS_TRAIN "\xee\x95\xb0" // U+e570
#define ICON_MS_TRAM "\xee\x95\xb1" // U+e571
#define ICON_MS_TRANSCRIBE "\xef\xa3\xac" // U+f8ec
#define ICON_MS_TRANSFER_WITHIN_A_STATION "\xee\x95\xb2" // U+e572
#define ICON_MS_TRANSFORM "\xee\x90\xa8" // U+e428
#define ICON_MS_TRANSGENDER "\xee\x96\x8d" // U+e58d
#define ICON_MS_TRANSIT_ENTEREXIT "\xee\x95\xb9" // U+e579
#define ICON_MS_TRANSITION_CHOP "\xef\x94\x8e" // U+f50e
#define ICON_MS_TRANSITION_DISSOLVE "\xef\x94\x8d" // U+f50d
#define ICON_MS_TRANSITION_FADE "\xef\x94\x8c" // U+f50c
#define ICON_MS_TRANSITION_PUSH "\xef\x94\x8b" // U+f50b
#define ICON_MS_TRANSITION_SLIDE "\xef\x94\x8a" // U+f50a
#define ICON_MS_TRANSLATE "\xee\xa3\xa2" // U+e8e2
#define ICON_MS_TRANSPORTATION "\xee\x88\x9d" // U+e21d
#define ICON_MS_TRAVEL "\xee\xbe\x93" // U+ef93
#define ICON_MS_TRAVEL_EXPLORE "\xee\x8b\x9b" // U+e2db
#define ICON_MS_TRAVEL_LUGGAGE_AND_BAGS "\xee\xbf\x83" // U+efc3
#define ICON_MS_TRENDING_DOWN "\xee\xa3\xa3" // U+e8e3
#define ICON_MS_TRENDING_FLAT "\xee\xa3\xa4" // U+e8e4
#define ICON_MS_TRENDING_UP "\xee\xa3\xa5" // U+e8e5
#define ICON_MS_TRIP "\xee\x9b\xbb" // U+e6fb
#define ICON_MS_TRIP_ORIGIN "\xee\x95\xbb" // U+e57b
#define ICON_MS_TROLLEY "\xef\xa1\xab" // U+f86b
#define ICON_MS_TROPHY "\xee\xa8\xa3" // U+ea23
#define ICON_MS_TROUBLESHOOT "\xee\x87\x92" // U+e1d2
#define ICON_MS_TRY "\xef\x81\xbc" // U+f07c
#define ICON_MS_TSUNAMI "\xee\xaf\x98" // U+ebd8
#define ICON_MS_TSV "\xee\x9b\x96" // U+e6d6
#define ICON_MS_TTY "\xef\x86\xaa" // U+f1aa
#define ICON_MS_TUNE "\xee\x90\xa9" // U+e429
#define ICON_MS_TUNGSTEN "\xef\x81\xbd" // U+f07d
#define ICON_MS_TURN_LEFT "\xee\xae\xa6" // U+eba6
#define ICON_MS_TURN_RIGHT "\xee\xae\xab" // U+ebab
#define ICON_MS_TURN_SHARP_LEFT "\xee\xae\xa7" // U+eba7
#define ICON_MS_TURN_SHARP_RIGHT "\xee\xae\xaa" // U+ebaa
#define ICON_MS_TURN_SLIGHT_LEFT "\xee\xae\xa4" // U+eba4
#define ICON_MS_TURN_SLIGHT_RIGHT "\xee\xae\x9a" // U+eb9a
#define ICON_MS_TURNED_IN "\xee\xa3\xa7" // U+e8e7
#define ICON_MS_TURNED_IN_NOT "\xee\xa3\xa7" // U+e8e7
#define ICON_MS_TV "\xee\x98\xbb" // U+e63b
#define ICON_MS_TV_GEN "\xee\xa0\xb0" // U+e830
#define ICON_MS_TV_GUIDE "\xee\x87\x9c" // U+e1dc
#define ICON_MS_TV_OFF "\xee\x99\x87" // U+e647
#define ICON_MS_TV_OPTIONS_EDIT_CHANNELS "\xee\x87\x9d" // U+e1dd
#define ICON_MS_TV_OPTIONS_INPUT_SETTINGS "\xee\x87\x9e" // U+e1de
#define ICON_MS_TV_REMOTE "\xef\x97\x99" // U+f5d9
#define ICON_MS_TV_SIGNIN "\xee\x9c\x9b" // U+e71b
#define ICON_MS_TV_WITH_ASSISTANT "\xee\x9e\x85" // U+e785
#define ICON_MS_TWO_PAGER "\xef\x94\x9f" // U+f51f
#define ICON_MS_TWO_WHEELER "\xee\xa7\xb9" // U+e9f9
#define ICON_MS_TYPE_SPECIMEN "\xef\xa3\xb0" // U+f8f0
#define ICON_MS_U_TURN_LEFT "\xee\xae\xa1" // U+eba1
#define ICON_MS_U_TURN_RIGHT "\xee\xae\xa2" // U+eba2
#define ICON_MS_ULNA_RADIUS "\xef\xa2\x9d" // U+f89d
#define ICON_MS_ULNA_RADIUS_ALT "\xef\xa2\x9e" // U+f89e
#define ICON_MS_UMBRELLA "\xef\x86\xad" // U+f1ad
#define ICON_MS_UNARCHIVE "\xee\x85\xa9" // U+e169
#define ICON_MS_UNDO "\xee\x85\xa6" // U+e166
#define ICON_MS_UNFOLD_LESS "\xee\x97\x96" // U+e5d6
#define ICON_MS_UNFOLD_LESS_DOUBLE "\xef\xa3\x8f" // U+f8cf
#define ICON_MS_UNFOLD_MORE "\xee\x97\x97" // U+e5d7
#define ICON_MS_UNFOLD_MORE_DOUBLE "\xef\xa3\x90" // U+f8d0
#define ICON_MS_UNGROUP "\xef\x9c\xb1" // U+f731
#define ICON_MS_UNIVERSAL_CURRENCY "\xee\xa7\xba" // U+e9fa
#define ICON_MS_UNIVERSAL_CURRENCY_ALT "\xee\x9c\xb4" // U+e734
#define ICON_MS_UNIVERSAL_LOCAL "\xee\xa7\xbb" // U+e9fb
#define ICON_MS_UNKNOWN_2 "\xef\x92\x9f" // U+f49f
#define ICON_MS_UNKNOWN_5 "\xee\x9a\xa5" // U+e6a5
#define ICON_MS_UNKNOWN_7 "\xef\x92\x9e" // U+f49e
#define ICON_MS_UNKNOWN_DOCUMENT "\xef\xa0\x84" // U+f804
#define ICON_MS_UNKNOWN_MED "\xee\xaa\xbd" // U+eabd
#define ICON_MS_UNLICENSE "\xee\xac\x85" // U+eb05
#define ICON_MS_UNPIN "\xee\x9b\xb9" // U+e6f9
#define ICON_MS_UNPUBLISHED "\xef\x88\xb6" // U+f236
#define ICON_MS_UNSUBSCRIBE "\xee\x83\xab" // U+e0eb
#define ICON_MS_UPCOMING "\xef\x81\xbe" // U+f07e
#define ICON_MS_UPDATE "\xee\xa4\xa3" // U+e923
#define ICON_MS_UPDATE_DISABLED "\xee\x81\xb5" // U+e075
#define ICON_MS_UPGRADE "\xef\x83\xbb" // U+f0fb
#define ICON_MS_UPLOAD "\xef\x82\x9b" // U+f09b
#define ICON_MS_UPLOAD_2 "\xef\x94\xa1" // U+f521
#define ICON_MS_UPLOAD_FILE "\xee\xa7\xbc" // U+e9fc
#define ICON_MS_UPPERCASE "\xef\x92\x88" // U+f488
#define ICON_MS_UROLOGY "\xee\x84\xb7" // U+e137
#define ICON_MS_USB "\xee\x87\xa0" // U+e1e0
#define ICON_MS_USB_OFF "\xee\x93\xba" // U+e4fa
#define ICON_MS_USER_ATTRIBUTES "\xee\x9c\x88" // U+e708
#define ICON_MS_VACCINES "\xee\x84\xb8" // U+e138
#define ICON_MS_VACUUM "\xee\xbf\x85" // U+efc5
#define ICON_MS_VALVE "\xee\x88\xa4" // U+e224
#define ICON_MS_VAPE_FREE "\xee\xaf\x86" // U+ebc6
#define ICON_MS_VAPING_ROOMS "\xee\xaf\x8f" // U+ebcf
#define ICON_MS_VARIABLE_ADD "\xef\x94\x9e" // U+f51e
#define ICON_MS_VARIABLE_INSERT "\xef\x94\x9d" // U+f51d
#define ICON_MS_VARIABLE_REMOVE "\xef\x94\x9c" // U+f51c
#define ICON_MS_VARIABLES "\xef\xa1\x91" // U+f851
#define ICON_MS_VENTILATOR "\xee\x84\xb9" // U+e139
#define ICON_MS_VERIFIED "\xee\xbd\xb6" // U+ef76
#define ICON_MS_VERIFIED_USER "\xef\x80\x93" // U+f013
#define ICON_MS_VERTICAL_ALIGN_BOTTOM "\xee\x89\x98" // U+e258
#define ICON_MS_VERTICAL_ALIGN_CENTER "\xee\x89\x99" // U+e259
#define ICON_MS_VERTICAL_ALIGN_TOP "\xee\x89\x9a" // U+e25a
#define ICON_MS_VERTICAL_DISTRIBUTE "\xee\x81\xb6" // U+e076
#define ICON_MS_VERTICAL_SHADES "\xee\xb0\x8e" // U+ec0e
#define ICON_MS_VERTICAL_SHADES_CLOSED "\xee\xb0\x8d" // U+ec0d
#define ICON_MS_VERTICAL_SPLIT "\xee\xa5\x89" // U+e949
#define ICON_MS_VIBRATION "\xee\x98\xad" // U+e62d
#define ICON_MS_VIDEO_CALL "\xee\x81\xb0" // U+e070
#define ICON_MS_VIDEO_CAMERA_BACK "\xef\x81\xbf" // U+f07f
#define ICON_MS_VIDEO_CAMERA_FRONT "\xef\x82\x80" // U+f080
#define ICON_MS_VIDEO_CAMERA_FRONT_OFF "\xef\xa0\xbb" // U+f83b
#define ICON_MS_VIDEO_CHAT "\xef\xa2\xa0" // U+f8a0
#define ICON_MS_VIDEO_FILE "\xee\xae\x87" // U+eb87
#define ICON_MS_VIDEO_LABEL "\xee\x81\xb1" // U+e071
#define ICON_MS_VIDEO_LIBRARY "\xee\x81\x8a" // U+e04a
#define ICON_MS_VIDEO_SEARCH "\xee\xbf\x86" // U+efc6
#define ICON_MS_VIDEO_SETTINGS "\xee\xa9\xb5" // U+ea75
#define ICON_MS_VIDEO_STABLE "\xef\x82\x81" // U+f081
#define ICON_MS_VIDEOCAM "\xee\x81\x8b" // U+e04b
#define ICON_MS_VIDEOCAM_OFF "\xee\x81\x8c" // U+e04c
#define ICON_MS_VIDEOGAME_ASSET "\xee\x8c\xb8" // U+e338
#define ICON_MS_VIDEOGAME_ASSET_OFF "\xee\x94\x80" // U+e500
#define ICON_MS_VIEW_AGENDA "\xee\xa3\xa9" // U+e8e9
#define ICON_MS_VIEW_ARRAY "\xee\xa3\xaa" // U+e8ea
#define ICON_MS_VIEW_CAROUSEL "\xee\xa3\xab" // U+e8eb
#define ICON_MS_VIEW_COLUMN "\xee\xa3\xac" // U+e8ec
#define ICON_MS_VIEW_COLUMN_2 "\xef\xa1\x87" // U+f847
#define ICON_MS_VIEW_COMFY "\xee\x90\xaa" // U+e42a
#define ICON_MS_VIEW_COMFY_ALT "\xee\xad\xb3" // U+eb73
#define ICON_MS_VIEW_COMPACT "\xee\x90\xab" // U+e42b
#define ICON_MS_VIEW_COMPACT_ALT "\xee\xad\xb4" // U+eb74
#define ICON_MS_VIEW_COZY "\xee\xad\xb5" // U+eb75
#define ICON_MS_VIEW_DAY "\xee\xa3\xad" // U+e8ed
#define ICON_MS_VIEW_HEADLINE "\xee\xa3\xae" // U+e8ee
#define ICON_MS_VIEW_IN_AR "\xee\xbf\x89" // U+efc9
#define ICON_MS_VIEW_IN_AR_NEW "\xee\xbf\x89" // U+efc9
#define ICON_MS_VIEW_IN_AR_OFF "\xef\x98\x9b" // U+f61b
#define ICON_MS_VIEW_KANBAN "\xee\xad\xbf" // U+eb7f
#define ICON_MS_VIEW_LIST "\xee\xa3\xaf" // U+e8ef
#define ICON_MS_VIEW_MODULE "\xee\xa3\xb0" // U+e8f0
#define ICON_MS_VIEW_QUILT "\xee\xa3\xb1" // U+e8f1
#define ICON_MS_VIEW_REAL_SIZE "\xef\x93\x82" // U+f4c2
#define ICON_MS_VIEW_SIDEBAR "\xef\x84\x94" // U+f114
#define ICON_MS_VIEW_STREAM "\xee\xa3\xb2" // U+e8f2
#define ICON_MS_VIEW_TIMELINE "\xee\xae\x85" // U+eb85
#define ICON_MS_VIEW_WEEK "\xee\xa3\xb3" // U+e8f3
#define ICON_MS_VIGNETTE "\xee\x90\xb5" // U+e435
#define ICON_MS_VILLA "\xee\x96\x86" // U+e586
#define ICON_MS_VISIBILITY "\xee\xa3\xb4" // U+e8f4
#define ICON_MS_VISIBILITY_LOCK "\xef\x99\x93" // U+f653
#define ICON_MS_VISIBILITY_OFF "\xee\xa3\xb5" // U+e8f5
#define ICON_MS_VITAL_SIGNS "\xee\x99\x90" // U+e650
#define ICON_MS_VITALS "\xee\x84\xbb" // U+e13b
#define ICON_MS_VO2_MAX "\xef\x92\xaa" // U+f4aa
#define ICON_MS_VOICE_CHAT "\xee\x98\xae" // U+e62e
#define ICON_MS_VOICE_OVER_OFF "\xee\xa5\x8a" // U+e94a
#define ICON_MS_VOICE_SELECTION "\xef\x96\x8a" // U+f58a
#define ICON_MS_VOICEMAIL "\xee\x83\x99" // U+e0d9
#define ICON_MS_VOLCANO "\xee\xaf\x9a" // U+ebda
#define ICON_MS_VOLUME_DOWN "\xee\x81\x8d" // U+e04d
#define ICON_MS_VOLUME_DOWN_ALT "\xee\x9e\x9c" // U+e79c
#define ICON_MS_VOLUME_MUTE "\xee\x81\x8e" // U+e04e
#define ICON_MS_VOLUME_OFF "\xee\x81\x8f" // U+e04f
#define ICON_MS_VOLUME_UP "\xee\x81\x90" // U+e050
#define ICON_MS_VOLUNTEER_ACTIVISM "\xee\xa9\xb0" // U+ea70
#define ICON_MS_VOTING_CHIP "\xef\xa1\x92" // U+f852
#define ICON_MS_VPN_KEY "\xee\x83\x9a" // U+e0da
#define ICON_MS_VPN_KEY_ALERT "\xef\x9b\x8c" // U+f6cc
#define ICON_MS_VPN_KEY_OFF "\xee\xad\xba" // U+eb7a
#define ICON_MS_VPN_LOCK "\xee\x98\xaf" // U+e62f
#define ICON_MS_VR180_CREATE2D "\xee\xbf\x8a" // U+efca
#define ICON_MS_VR180_CREATE2D_OFF "\xef\x95\xb1" // U+f571
#define ICON_MS_VRPANO "\xef\x82\x82" // U+f082
#define ICON_MS_WALL_ART "\xee\xbf\x8b" // U+efcb
#define ICON_MS_WALL_LAMP "\xee\x8a\xb4" // U+e2b4
#define ICON_MS_WALLET "\xef\xa3\xbf" // U+f8ff
#define ICON_MS_WALLPAPER "\xee\x86\xbc" // U+e1bc
#define ICON_MS_WALLPAPER_SLIDESHOW "\xef\x99\xb2" // U+f672
#define ICON_MS_WARD "\xee\x84\xbc" // U+e13c
#define ICON_MS_WAREHOUSE "\xee\xae\xb8" // U+ebb8
#define ICON_MS_WARNING "\xef\x82\x83" // U+f083
#define ICON_MS_WARNING_AMBER "\xef\x82\x83" // U+f083
#define ICON_MS_WARNING_OFF "\xef\x9e\xad" // U+f7ad
#define ICON_MS_WASH "\xef\x86\xb1" // U+f1b1
#define ICON_MS_WATCH "\xee\x8c\xb4" // U+e334
#define ICON_MS_WATCH_BUTTON_PRESS "\xef\x9a\xaa" // U+f6aa
#define ICON_MS_WATCH_LATER "\xee\xbf\x96" // U+efd6
#define ICON_MS_WATCH_OFF "\xee\xab\xa3" // U+eae3
#define ICON_MS_WATCH_SCREENTIME "\xef\x9a\xae" // U+f6ae
#define ICON_MS_WATCH_WAKE "\xef\x9a\xa9" // U+f6a9
#define ICON_MS_WATER "\xef\x82\x84" // U+f084
#define ICON_MS_WATER_BOTTLE "\xef\x9a\x9d" // U+f69d
#define ICON_MS_WATER_BOTTLE_LARGE "\xef\x9a\x9e" // U+f69e
#define ICON_MS_WATER_DAMAGE "\xef\x88\x83" // U+f203
#define ICON_MS_WATER_DO "\xef\xa1\xb0" // U+f870
#define ICON_MS_WATER_DROP "\xee\x9e\x98" // U+e798
#define ICON_MS_WATER_EC "\xef\xa1\xb5" // U+f875
#define ICON_MS_WATER_FULL "\xef\x9b\x96" // U+f6d6
#define ICON_MS_WATER_HEATER "\xee\x8a\x84" // U+e284
#define ICON_MS_WATER_LOCK "\xef\x9a\xad" // U+f6ad
#define ICON_MS_WATER_LOSS "\xef\x9b\x95" // U+f6d5
#define ICON_MS_WATER_LUX "\xef\xa1\xb4" // U+f874
#define ICON_MS_WATER_MEDIUM "\xef\x9b\x94" // U+f6d4
#define ICON_MS_WATER_ORP "\xef\xa1\xb8" // U+f878
#define ICON_MS_WATER_PH "\xef\xa1\xba" // U+f87a
#define ICON_MS_WATER_PUMP "\xef\x97\x98" // U+f5d8
#define ICON_MS_WATER_VOC "\xef\xa1\xbb" // U+f87b
#define ICON_MS_WATERFALL_CHART "\xee\xa8\x80" // U+ea00
#define ICON_MS_WAVES "\xee\x85\xb6" // U+e176
#define ICON_MS_WAVING_HAND "\xee\x9d\xa6" // U+e766
#define ICON_MS_WB_AUTO "\xee\x90\xac" // U+e42c
#define ICON_MS_WB_CLOUDY "\xef\x85\x9c" // U+f15c
#define ICON_MS_WB_INCANDESCENT "\xee\x90\xae" // U+e42e
#define ICON_MS_WB_IRIDESCENT "\xef\x81\xbd" // U+f07d
#define ICON_MS_WB_SHADE "\xee\xa8\x81" // U+ea01
#define ICON_MS_WB_SUNNY "\xee\x90\xb0" // U+e430
#define ICON_MS_WB_TWILIGHT "\xee\x87\x86" // U+e1c6
#define ICON_MS_WC "\xee\x98\xbd" // U+e63d
#define ICON_MS_WEATHER_HAIL "\xef\x99\xbf" // U+f67f
#define ICON_MS_WEATHER_MIX "\xef\x98\x8b" // U+f60b
#define ICON_MS_WEATHER_SNOWY "\xee\x8b\x8d" // U+e2cd
#define ICON_MS_WEB "\xee\x81\x91" // U+e051
#define ICON_MS_WEB_ASSET "\xee\x81\xa9" // U+e069
#define ICON_MS_WEB_ASSET_OFF "\xee\xbd\x87" // U+ef47
#define ICON_MS_WEB_STORIES "\xee\x96\x95" // U+e595
#define ICON_MS_WEB_TRAFFIC "\xee\xa8\x83" // U+ea03
#define ICON_MS_WEBHOOK "\xee\xae\x92" // U+eb92
#define ICON_MS_WEEKEND "\xee\x85\xab" // U+e16b
#define ICON_MS_WEIGHT "\xee\x84\xbd" // U+e13d
#define ICON_MS_WEST "\xef\x87\xa6" // U+f1e6
#define ICON_MS_WHATSHOT "\xee\xa0\x8e" // U+e80e
#define ICON_MS_WHEELCHAIR_PICKUP "\xef\x86\xab" // U+f1ab
#define ICON_MS_WHERE_TO_VOTE "\xee\x85\xb7" // U+e177
#define ICON_MS_WIDGETS "\xee\x86\xbd" // U+e1bd
#define ICON_MS_WIDTH "\xef\x9c\xb0" // U+f730
#define ICON_MS_WIDTH_FULL "\xef\xa3\xb5" // U+f8f5
#define ICON_MS_WIDTH_NORMAL "\xef\xa3\xb6" // U+f8f6
#define ICON_MS_WIDTH_WIDE "\xef\xa3\xb7" // U+f8f7
#define ICON_MS_WIFI "\xee\x98\xbe" // U+e63e
#define ICON_MS_WIFI_1_BAR "\xee\x93\x8a" // U+e4ca
#define ICON_MS_WIFI_2_BAR "\xee\x93\x99" // U+e4d9
#define ICON_MS_WIFI_ADD "\xef\x9e\xa8" // U+f7a8
#define ICON_MS_WIFI_CALLING "\xee\xbd\xb7" // U+ef77
#define ICON_MS_WIFI_CALLING_1 "\xef\x83\xb6" // U+f0f6
#define ICON_MS_WIFI_CALLING_2 "\xef\x83\xb6" // U+f0f6
#define ICON_MS_WIFI_CALLING_3 "\xef\x83\xb6" // U+f0f6
#define ICON_MS_WIFI_CHANNEL "\xee\xad\xaa" // U+eb6a
#define ICON_MS_WIFI_FIND "\xee\xac\xb1" // U+eb31
#define ICON_MS_WIFI_HOME "\xef\x99\xb1" // U+f671
#define ICON_MS_WIFI_LOCK "\xee\x87\xa1" // U+e1e1
#define ICON_MS_WIFI_NOTIFICATION "\xef\x99\xb0" // U+f670
#define ICON_MS_WIFI_OFF "\xee\x99\x88" // U+e648
#define ICON_MS_WIFI_PASSWORD "\xee\xad\xab" // U+eb6b
#define ICON_MS_WIFI_PROTECTED_SETUP "\xef\x83\xbc" // U+f0fc
#define ICON_MS_WIFI_PROXY "\xef\x9e\xa7" // U+f7a7
#define ICON_MS_WIFI_TETHERING "\xee\x87\xa2" // U+e1e2
#define ICON_MS_WIFI_TETHERING_ERROR "\xee\xab\x99" // U+ead9
#define ICON_MS_WIFI_TETHERING_OFF "\xef\x82\x87" // U+f087
#define ICON_MS_WIND_POWER "\xee\xb0\x8c" // U+ec0c
#define ICON_MS_WINDOW "\xef\x82\x88" // U+f088
#define ICON_MS_WINDOW_CLOSED "\xee\x9d\xbe" // U+e77e
#define ICON_MS_WINDOW_OPEN "\xee\x9e\x8c" // U+e78c
#define ICON_MS_WINDOW_SENSOR "\xee\x8a\xbb" // U+e2bb
#define ICON_MS_WINE_BAR "\xef\x87\xa8" // U+f1e8
#define ICON_MS_WOMAN "\xee\x84\xbe" // U+e13e
#define ICON_MS_WOMAN_2 "\xef\xa3\xa7" // U+f8e7
#define ICON_MS_WORK "\xee\xa5\x83" // U+e943
#define ICON_MS_WORK_ALERT "\xef\x97\xb7" // U+f5f7
#define ICON_MS_WORK_HISTORY "\xee\xb0\x89" // U+ec09
#define ICON_MS_WORK_OFF "\xee\xa5\x82" // U+e942
#define ICON_MS_WORK_OUTLINE "\xee\xa5\x83" // U+e943
#define ICON_MS_WORK_UPDATE "\xef\x97\xb8" // U+f5f8
#define ICON_MS_WORKFLOW "\xee\xa8\x84" // U+ea04
#define ICON_MS_WORKSPACE_PREMIUM "\xee\x9e\xaf" // U+e7af
#define ICON_MS_WORKSPACES "\xee\xa8\x8f" // U+ea0f
#define ICON_MS_WORKSPACES_OUTLINE "\xee\xa8\x8f" // U+ea0f
#define ICON_MS_WOUNDS_INJURIES "\xee\x84\xbf" // U+e13f
#define ICON_MS_WRAP_TEXT "\xee\x89\x9b" // U+e25b
#define ICON_MS_WRIST "\xef\x9a\x9c" // U+f69c
#define ICON_MS_WRONG_LOCATION "\xee\xbd\xb8" // U+ef78
#define ICON_MS_WYSIWYG "\xef\x87\x83" // U+f1c3
#define ICON_MS_YARD "\xef\x82\x89" // U+f089
#define ICON_MS_YOUR_TRIPS "\xee\xac\xab" // U+eb2b
#define ICON_MS_YOUTUBE_ACTIVITY "\xef\xa1\x9a" // U+f85a
#define ICON_MS_YOUTUBE_SEARCHED_FOR "\xee\xa3\xba" // U+e8fa
#define ICON_MS_ZONE_PERSON_ALERT "\xee\x9e\x81" // U+e781
#define ICON_MS_ZONE_PERSON_IDLE "\xee\x9d\xba" // U+e77a
#define ICON_MS_ZONE_PERSON_URGENT "\xee\x9e\x88" // U+e788
#define ICON_MS_ZOOM_IN "\xee\xa3\xbf" // U+e8ff
#define ICON_MS_ZOOM_IN_MAP "\xee\xac\xad" // U+eb2d
#define ICON_MS_ZOOM_OUT "\xee\xa4\x80" // U+e900
#define ICON_MS_ZOOM_OUT_MAP "\xee\x95\xab" // U+e56b
================================================
FILE: frontend/settings.cpp
================================================
#include "iris.hpp"
#include "config.hpp"
#include "ps2_elf.h"
#include "ps2_iso9660.h"
#define TOML_EXCEPTIONS 0
#include
namespace iris::settings {
void print_version() {
puts(
"iris (" STR(_IRIS_VERSION) " " STR(_IRIS_OSVERSION) ")\n"
"Copyright (C) 2026 Allkern/Lisandro Alarcon\n\n"
"MIT License\n"
"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n"
"IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n"
"FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n"
"AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n"
"LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n"
"OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n"
"SOFTWARE."
);
}
void print_help() {
puts(
"Usage: iris [OPTION]... \n"
"\n"
" -b, --bios Specify a PlayStation 2 BIOS dump file\n"
" --rom1 Specify a DVD player dump file\n"
" --rom2 Specify a ROM2 dump file\n"
" -d, --boot Specify a direct kernel boot path\n"
" -i, --disc Specify a path to a disc image file\n"
" -x, --executable Specify a path to an ELF executable to be\n"
" loaded on system startup\n"
" --slot1 Specify a path to a memory card file to\n"
" be inserted on slot 1\n"
" --slot2 Specify a path to a memory card file to\n"
" be inserted on slot 2\n"
" --snap Specify a directory for storing screenshots\n"
" -h, --help Display this help and exit\n"
" -v, --version Output version information and exit\n"
);
}
bool parse_mappings_file(iris::instance* iris) {
iris->mappings_path = iris->pref_path + "mappings.toml";
std::ifstream mappings_file(iris->mappings_path);
if (!mappings_file.is_open())
return false;
toml::parse_result result = toml::parse(mappings_file);
if (!result) {
std::string desc(result.error().description());
printf("iris: Couldn't parse mappings file: %s\n", desc.c_str());
return false;
}
toml::table& tbl = result.table();
for (auto& map : tbl) {
printf("input: Parsing input map \"%s\"...\n", map.first.data());
mapping input_mapping {};
input_mapping.name = map.first.data();
input_mapping.map = bidirectional_map();
for (auto& input_map : *map.second.as_table()) {
uint64_t key = std::stoull(input_map.first.data());
uint64_t value = input_map.second.as_integer()->get();
input_mapping.map.insert(key, static_cast(value));
// printf("entry: %d -> %d\n", std::stoull(input_map.first.data()), input_map.second.as_integer()->get());
}
iris->input_maps.push_back(input_mapping);
}
return true;
}
bool parse_toml_settings(iris::instance* iris) {
iris->settings_path = iris->pref_path + "settings.toml";
toml::parse_result result = toml::parse_file(iris->settings_path);
if (!result) {
std::string desc(result.error().description());
printf("iris: Couldn't parse settings file: %s\n", desc.c_str());
return false;
}
toml::table& tbl = result.table();
auto paths = tbl["paths"];
iris->bios_path = paths["bios_path"].value_or("");
iris->rom1_path = paths["rom1_path"].value_or("");
iris->rom2_path = paths["rom2_path"].value_or("");
iris->nvram_path = paths["nvram_path"].value_or("");
iris->mcd0_path = paths["mcd0_path"].value_or("");
iris->mcd1_path = paths["mcd1_path"].value_or("");
iris->snap_path = paths["snap_path"].value_or("snap");
iris->flash_path = paths["flash_path"].value_or("");
iris->gcdb_path = paths["gcdb_path"].value_or("");
auto window = tbl["window"];
iris->window_width = window["window_width"].value_or(960);
iris->window_height = window["window_height"].value_or(720);
iris->fullscreen = window["fullscreen"].value_or(0);
auto display = tbl["display"];
iris->aspect_mode = display["aspect_mode"].value_or(RENDER_ASPECT_AUTO);
iris->filter = display["filter"].value_or(true);
iris->integer_scaling = display["integer_scaling"].value_or(false);
iris->scale = display["scale"].value_or(1.5f);
iris->renderer_backend = display["renderer"].value_or(RENDERER_BACKEND_HARDWARE);
iris->window_width = display["window_width"].value_or(960);
iris->window_height = display["window_height"].value_or(720);
iris->menubar_height = display["menubar_height"].value_or(0);
iris->angle = display["angle"].value_or(0);
iris->flip_x = display["flip_x"].value_or(false);
iris->flip_y = display["flip_y"].value_or(false);
iris->vsync = display["vsync"].value_or(true);
auto audio = tbl["audio"];
iris->mute = audio["mute"].value_or(false);
iris->volume = audio["volume"].value_or(1.0);
iris->mute_adma = audio["mute_adma"].value_or(true);
auto debugger = tbl["debugger"];
iris->show_ee_control = debugger["show_ee_control"].value_or(false);
iris->show_ee_state = debugger["show_ee_state"].value_or(false);
iris->show_ee_logs = debugger["show_ee_logs"].value_or(false);
iris->show_ee_interrupts = debugger["show_ee_interrupts"].value_or(false);
iris->show_ee_dmac = debugger["show_ee_dmac"].value_or(false);
iris->show_iop_control = debugger["show_iop_control"].value_or(false);
iris->show_iop_state = debugger["show_iop_state"].value_or(false);
iris->show_iop_logs = debugger["show_iop_logs"].value_or(false);
iris->show_iop_interrupts = debugger["show_iop_interrupts"].value_or(false);
iris->show_iop_modules = debugger["show_iop_modules"].value_or(false);
iris->show_iop_dma = debugger["show_iop_dma"].value_or(false);
iris->show_gs_debugger = debugger["show_gs_debugger"].value_or(false);
iris->show_spu2_debugger = debugger["show_spu2_debugger"].value_or(false);
iris->show_memory_viewer = debugger["show_memory_viewer"].value_or(false);
iris->show_memory_search = debugger["show_memory_search"].value_or(false);
iris->show_vu_disassembler = debugger["show_vu_disassembler"].value_or(false);
iris->show_status_bar = debugger["show_status_bar"].value_or(true);
iris->show_pad_debugger = debugger["show_pad_debugger"].value_or(false);
iris->show_threads = debugger["show_threads"].value_or(false);
iris->show_sysmem_logs = debugger["show_sysmem_logs"].value_or(false);
iris->show_overlay = debugger["show_overlay"].value_or(false);
// iris->show_symbols = debugger["show_symbols"].value_or(false);
iris->show_breakpoints = debugger["show_breakpoints"].value_or(false);
iris->show_imgui_demo = debugger["show_imgui_demo"].value_or(false);
iris->skip_fmv = debugger["skip_fmv"].value_or(false);
iris->timescale = debugger["timescale"].value_or(8);
auto system = tbl["system"];
iris->system = system["model"].value_or(PS2_SYSTEM_AUTO);
iris->autostart = system["autostart"].value_or(true);
toml::array* mac_array = system["mac_address"].as_array();
if (mac_array && mac_array->size() == 6) {
for (int i = 0; i < 6; i++) {
iris->mac_address[i] = static_cast(mac_array->at(i).as_integer()->get());
}
} else {
// Default MAC address
iris->mac_address[0] = 0x00;
iris->mac_address[1] = 0x1A;
iris->mac_address[2] = 0x2B;
iris->mac_address[3] = 0x3C;
iris->mac_address[4] = 0x4D;
iris->mac_address[5] = 0x5E;
}
auto screenshots = tbl["screenshots"];
iris->screenshot_format = screenshots["format"].value_or(IRIS_SCREENSHOT_FORMAT_PNG);
iris->screenshot_jpg_quality_mode = screenshots["jpg_quality_mode"].value_or(IRIS_SCREENSHOT_JPG_QUALITY_MAXIMUM);
iris->screenshot_jpg_quality = screenshots["jpg_quality"].value_or(50);
iris->screenshot_mode = screenshots["mode"].value_or(IRIS_SCREENSHOT_MODE_INTERNAL);
iris->screenshot_shader_processing = screenshots["shader_processing"].value_or(false);
auto hardware = tbl["hardware"];
iris->hardware_backend_config.super_sampling = hardware["super_sampling"].value_or(0);
iris->hardware_backend_config.force_progressive = hardware["force_progressive"].value_or(false);
iris->hardware_backend_config.overscan = hardware["overscan"].value_or(false);
iris->hardware_backend_config.crtc_offsets = hardware["crtc_offsets"].value_or(false);
iris->hardware_backend_config.disable_mipmaps = hardware["disable_mipmaps"].value_or(false);
iris->hardware_backend_config.unsynced_readbacks = hardware["unsynced_readbacks"].value_or(false);
iris->hardware_backend_config.backbuffer_promotion = hardware["backbuffer_promotion"].value_or(false);
iris->hardware_backend_config.allow_blend_demote = hardware["allow_blend_demote"].value_or(false);
auto vulkan = tbl["vulkan"];
iris->vulkan_physical_device = vulkan["physical_device"].value_or(-1);
iris->vulkan_enable_validation_layers = vulkan["enable_validation_layers"].value_or(false);
auto ui = tbl["ui"];
iris->theme = ui["theme"].value_or(IRIS_THEME_GRANITE);
iris->codeview_font_scale = ui["codeview_font_scale"].value_or(1.0f);
iris->codeview_color_scheme = ui["codeview_color_scheme"].value_or(IRIS_CODEVIEW_COLOR_SCHEME_SOLARIZED_DARK);
iris->codeview_use_theme_background = ui["codeview_use_theme_background"].value_or(true);
iris->ui_scale = ui["scale"].value_or(1.0f);
iris->imgui_enable_viewports = ui["enable_viewports"].value_or(false);
toml::array* bgcolor = tbl["ui"]["bgcolor"].as_array();
if (bgcolor && bgcolor->size() == 3) {
iris->clear_value.color.float32[0] = (float)bgcolor->at(0).as_floating_point()->get();
iris->clear_value.color.float32[1] = (float)bgcolor->at(1).as_floating_point()->get();
iris->clear_value.color.float32[2] = (float)bgcolor->at(2).as_floating_point()->get();
} else {
iris->clear_value.color.float32[0] = 0.11f;
iris->clear_value.color.float32[1] = 0.11f;
iris->clear_value.color.float32[2] = 0.11f;
}
#ifdef _WIN32
iris->windows_titlebar_style = tbl["ui"]["windows_titlebar_style"].value_or(IRIS_TITLEBAR_DEFAULT);
iris->windows_enable_borders = tbl["ui"]["windows_enable_borders"].value_or(true);
iris->windows_dark_mode = tbl["ui"]["windows_dark_mode"].value_or(true);
#endif
toml::array* recents = tbl["recents"]["array"].as_array();
if (recents) {
for (int i = 0; i < recents->size(); i++) {
toml::table* entry = recents->at(i).as_table();
if (!entry) {
// Provided for backcompat with older settings files
std::string str = recents->at(i).as_string()->get();
iris->recents.push_back({ str, 0 });
continue;
}
iris::recent r = {
entry->operator[]("path").value_or(std::string()),
entry->operator[]("type").value_or(0)
};
iris->recents.push_back(r);
}
}
toml::array* shaders = tbl["shaders"]["array"].as_array();
iris->enable_shaders = tbl["shaders"]["enable"].value_or(false);
if (shaders) {
for (int i = 0; i < shaders->size(); i++)
iris->shader_passes_pending.push_back(shaders->at(i).as_string()->get());
}
return parse_mappings_file(iris);
}
bool check_for_quick_exit(int argc, const char* argv[]) {
for (int i = 1; i < argc; i++) {
std::string a(argv[i]);
if (a == "-h" || a == "--help") {
print_help();
return true;
} else if (a == "-v" || a == "--version") {
print_version();
return true;
}
}
return false;
}
void parse_cli_settings(iris::instance* iris, int argc, const char* argv[]) {
std::string bios_path;
std::string rom1_path;
std::string rom2_path;
for (int i = 1; i < argc; i++) {
std::string a(argv[i]);
if (a == "-x" || a == "--executable") {
iris->elf_path = argv[i+1];
++i;
} else if (a == "-d" || a == "--boot") {
iris->boot_path = argv[i+1];
++i;
} else if (a == "-b" || a == "--bios") {
bios_path = argv[i+1];
++i;
} else if (a == "--rom1") {
rom1_path = argv[i+1];
++i;
} else if (a == "--rom2") {
rom2_path = argv[i+1];
++i;
} else if (a == "-i" || a == "--disc") {
iris->disc_path = argv[i+1];
++i;
} else if (a == "--slot1") {
iris->mcd0_path = argv[i+1];
++i;
} else if (a == "--slot2") {
iris->mcd1_path = argv[i+1];
++i;
} else {
iris->disc_path = argv[i];
}
}
if (bios_path.size()) {
if (!ps2_load_bios(iris->ps2, bios_path.c_str())) {
// push_info(iris, "Couldn't load BIOS");
iris->show_bios_setting_window = true;
}
} else {
if (iris->bios_path.size()) {
if (!ps2_load_bios(iris->ps2, iris->bios_path.c_str())) {
// push_info(iris, "Couldn't load BIOS");
iris->show_bios_setting_window = true;
}
} else {
iris->show_bios_setting_window = true;
}
}
if (rom1_path.size()) {
if (!ps2_load_rom1(iris->ps2, rom1_path.c_str())) {
// push_info(iris, "Couldn't load ROM1");
}
} else {
if (iris->rom1_path.size()) {
if (!ps2_load_rom1(iris->ps2, iris->rom1_path.c_str())) {
// push_info(iris, "Couldn't load ROM1");
}
}
}
if (rom2_path.size()) {
if (!ps2_load_rom2(iris->ps2, rom2_path.c_str())) {
// push_info(iris, "Couldn't load ROM2");
}
} else {
if (iris->rom2_path.size()) {
if (!ps2_load_rom2(iris->ps2, iris->rom2_path.c_str())) {
// push_info(iris, "Couldn't load ROM2");
}
}
}
if (iris->elf_path.size()) {
ps2_set_system(iris->ps2, iris->system);
ps2_load_bios(iris->ps2, iris->bios_path.c_str());
ps2_elf_load(iris->ps2, iris->elf_path.c_str());
iris->loaded = iris->elf_path;
}
if (iris->boot_path.size()) {
ps2_set_system(iris->ps2, iris->system);
ps2_load_bios(iris->ps2, iris->bios_path.c_str());
ps2_boot_file(iris->ps2, iris->boot_path.c_str());
iris->loaded = iris->boot_path;
}
if (iris->disc_path.size()) {
if (ps2_cdvd_open(iris->ps2->cdvd, iris->disc_path.c_str(), 0))
return;
char* boot_file = disc_get_boot_path(iris->ps2->cdvd->disc);
if (!boot_file)
return;
ps2_set_system(iris->ps2, iris->system);
ps2_load_bios(iris->ps2, iris->bios_path.c_str());
ps2_boot_file(iris->ps2, boot_file);
iris->loaded = iris->disc_path;
}
}
bool init(iris::instance* iris, int argc, const char* argv[]) {
int r = parse_toml_settings(iris);
parse_cli_settings(iris, argc, argv);
if (iris->nvram_path.size())
ps2_cdvd_load_nvram(iris->ps2->cdvd, iris->nvram_path.c_str());
if (iris->mcd0_path.size())
emu::attach_memory_card(iris, 0, iris->mcd0_path.c_str());
if (iris->mcd1_path.size())
emu::attach_memory_card(iris, 1, iris->mcd1_path.c_str());
// Apply settings loaded from file/CLI
ps2_set_timescale(iris->ps2, iris->timescale);
ee_set_fmv_skip(iris->ps2->ee, iris->skip_fmv);
ps2_set_system(iris->ps2, iris->system);
ps2_speed_load_flash(iris->ps2->speed, iris->flash_path.c_str());
ps2_speed_set_mac_address(iris->ps2->speed, iris->mac_address);
return true;
}
void close(iris::instance* iris) {
if (!iris->dump_to_file)
return;
std::ofstream file(iris->settings_path);
std::ofstream mappings_file(iris->pref_path + "mappings.toml");
file << "# File auto-generated by " IRIS_TITLE "\n\n";
auto tbl = toml::table {
{ "system", toml::table {
{ "model", iris->system },
{ "mac_address", toml::array {
iris->mac_address[0],
iris->mac_address[1],
iris->mac_address[2],
iris->mac_address[3],
iris->mac_address[4],
iris->mac_address[5]
} },
{ "autostart", iris->autostart }
} },
{ "input", toml::table {
{ "slot1_device", iris->input_devices[0] ? iris->input_devices[0]->get_type() : 0 },
{ "slot2_device", iris->input_devices[1] ? iris->input_devices[1]->get_type() : 0 },
{ "slot1_mapping", iris->input_map[0] },
{ "slot2_mapping", iris->input_map[1] }
} },
{ "screenshots", toml::table {
{ "format", iris->screenshot_format },
{ "mode", iris->screenshot_mode },
{ "jpg_quality_mode", iris->screenshot_jpg_quality_mode },
{ "jpg_quality", iris->screenshot_jpg_quality },
{ "shader_processing", iris->screenshot_shader_processing }
} },
// To-do: Change this to "backends" and use dotted entries
// e.g.
// [backend_settings]
// hardware.super_sampling = 2
// etc.
{ "hardware", toml::table {
{ "super_sampling", iris->hardware_backend_config.super_sampling },
{ "force_progressive", iris->hardware_backend_config.force_progressive },
{ "overscan", iris->hardware_backend_config.overscan },
{ "crtc_offsets", iris->hardware_backend_config.crtc_offsets },
{ "disable_mipmaps", iris->hardware_backend_config.disable_mipmaps },
{ "unsynced_readbacks", iris->hardware_backend_config.unsynced_readbacks },
{ "backbuffer_promotion", iris->hardware_backend_config.backbuffer_promotion },
{ "allow_blend_demote", iris->hardware_backend_config.allow_blend_demote }
} },
{ "vulkan", toml::table {
{ "physical_device", iris->vulkan_physical_device },
{ "enable_validation_layers", iris->vulkan_enable_validation_layers }
} },
{ "debugger", toml::table {
{ "show_ee_control", iris->show_ee_control },
{ "show_ee_state", iris->show_ee_state },
{ "show_ee_logs", iris->show_ee_logs },
{ "show_ee_interrupts", iris->show_ee_interrupts },
{ "show_ee_dmac", iris->show_ee_dmac },
{ "show_iop_control", iris->show_iop_control },
{ "show_iop_state", iris->show_iop_state },
{ "show_iop_logs", iris->show_iop_logs },
{ "show_iop_interrupts", iris->show_iop_interrupts },
{ "show_iop_modules", iris->show_iop_modules},
{ "show_iop_dma", iris->show_iop_dma },
{ "show_gs_debugger", iris->show_gs_debugger },
{ "show_spu2_debugger", iris->show_spu2_debugger },
{ "show_memory_viewer", iris->show_memory_viewer },
{ "show_memory_search", iris->show_memory_search },
{ "show_vu_disassembler", iris->show_vu_disassembler },
{ "show_status_bar", iris->show_status_bar },
{ "show_pad_debugger", iris->show_pad_debugger },
{ "show_breakpoints", iris->show_breakpoints },
{ "show_threads", iris->show_threads },
{ "show_sysmem_logs", iris->show_sysmem_logs },
{ "show_imgui_demo", iris->show_imgui_demo },
{ "show_overlay", iris->show_overlay },
{ "skip_fmv", iris->skip_fmv },
{ "timescale", iris->timescale }
} },
{ "display", toml::table {
{ "scale", iris->scale },
{ "aspect_mode", iris->aspect_mode },
{ "integer_scaling", iris->integer_scaling },
{ "fullscreen", iris->fullscreen },
{ "filter", iris->filter },
{ "renderer", iris->renderer_backend },
{ "window_width", iris->window_width },
{ "window_height", iris->window_height },
{ "menubar_height", iris->menubar_height },
{ "angle", iris->angle },
{ "flip_x", iris->flip_x },
{ "flip_y", iris->flip_y },
{ "vsync", iris->vsync }
} },
{ "ui", toml::table {
{ "theme", iris->theme },
{ "codeview_color_scheme", iris->codeview_color_scheme },
{ "codeview_font_scale", iris->codeview_font_scale },
{ "codeview_use_theme_background", iris->codeview_use_theme_background },
{ "scale", iris->ui_scale },
{ "bgcolor", toml::array {
iris->clear_value.color.float32[0],
iris->clear_value.color.float32[1],
iris->clear_value.color.float32[2]
} },
{ "enable_viewports", iris->imgui_enable_viewports },
#ifdef _WIN32
{ "windows_titlebar_style", iris->windows_titlebar_style },
{ "windows_enable_borders", iris->windows_enable_borders },
{ "windows_dark_mode", iris->windows_dark_mode },
#endif
} },
{ "audio", toml::table {
{ "mute", iris->mute },
{ "mute_adma", iris->mute_adma },
{ "volume", iris->volume }
} },
{ "paths", toml::table {
{ "bios_path", iris->bios_path },
{ "rom1_path", iris->rom1_path },
{ "rom2_path", iris->rom2_path },
{ "nvram_path", iris->nvram_path },
{ "mcd0_path", iris->mcd0_path },
{ "mcd1_path", iris->mcd1_path },
{ "snap_path", iris->snap_path },
{ "flash_path", iris->flash_path },
{ "gcdb_path", iris->gcdb_path },
} },
{ "recents", toml::table {
{ "array", toml::array() }
} },
{ "shaders", toml::table {
{ "enable", iris->enable_shaders },
{ "array", toml::array() }
} },
};
toml::array* recents = tbl["recents"]["array"].as_array();
for (const auto& s : iris->recents)
recents->push_back(toml::table { { "type", s.type }, { "path", s.path } });
toml::array* shaders = tbl["shaders"]["array"].as_array();
for (auto& s : shaders::vector(iris))
shaders->push_back(s->get_id());
// Generate input mappings file
mappings_file << "# File auto-generated by " IRIS_TITLE "\n\n";
toml::table mappings_tbl {};
for (auto& map : iris->input_maps) {
toml::table map_tbl { { map.name, toml::table {} } };
for (auto& entry : map.map.forward_map()) {
toml::table t {
{ std::to_string(entry.first), entry.second }
};
t.is_inline(true);
map_tbl[map.name].as_table()->insert(std::to_string(entry.first), entry.second);
}
mappings_tbl.insert(map.name, map_tbl[map.name]);
}
file << tbl;
mappings_file << mappings_tbl;
}
}
================================================
FILE: frontend/shaders.cpp
================================================
#include
#include
#include "config.hpp"
#include "iris.hpp"
// INCBIN stuff
#define INCBIN_PREFIX g_
#define INCBIN_STYLE INCBIN_STYLE_SNAKE
#include "incbin.h"
INCBIN(encoder_frag_shader, "../shaders/encoder.spv");
INCBIN(decoder_frag_shader, "../shaders/decoder.spv");
INCBIN(curvature_frag_shader, "../shaders/curvature.spv");
INCBIN(scanlines_frag_shader, "../shaders/scanlines.spv");
INCBIN(noise_frag_shader, "../shaders/noise.spv");
namespace iris::shaders {
bool pass::init(iris::instance* iris, const void* data, size_t size, std::string id) {
m_vert_shader = iris->default_vert_shader;
m_iris = iris;
m_id = id;
VkShaderModuleCreateInfo create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
create_info.pCode = (const uint32_t*)data;
create_info.codeSize = size;
if (vkCreateShaderModule(m_iris->device, &create_info, nullptr, &m_frag_shader) != VK_SUCCESS) {
fprintf(stderr, "render: Failed to create fragment shader module\n");
return false;
}
return rebuild();
}
pass::pass(iris::instance* iris, const void* data, size_t size, std::string id) {
init(iris, data, size, id);
}
pass::pass(pass&& other) {
m_pipeline_layout = other.m_pipeline_layout;
m_pipeline = other.m_pipeline;
m_render_pass = other.m_render_pass;
m_input = other.m_input;
m_vert_shader = other.m_vert_shader;
m_frag_shader = other.m_frag_shader;
m_iris = other.m_iris;
m_id = other.m_id;
bypass = other.bypass;
other.m_pipeline_layout = VK_NULL_HANDLE;
other.m_pipeline = VK_NULL_HANDLE;
other.m_render_pass = VK_NULL_HANDLE;
other.m_input = VK_NULL_HANDLE;
other.m_vert_shader = VK_NULL_HANDLE;
other.m_frag_shader = VK_NULL_HANDLE;
other.m_iris = nullptr;
other.m_id = "";
other.bypass = false;
}
pass& pass::operator=(pass&& other) {
if (this != &other) {
m_pipeline_layout = other.m_pipeline_layout;
m_pipeline = other.m_pipeline;
m_render_pass = other.m_render_pass;
m_input = other.m_input;
m_vert_shader = other.m_vert_shader;
m_frag_shader = other.m_frag_shader;
m_iris = other.m_iris;
m_id = other.m_id;
bypass = other.bypass;
}
other.m_pipeline_layout = VK_NULL_HANDLE;
other.m_pipeline = VK_NULL_HANDLE;
other.m_render_pass = VK_NULL_HANDLE;
other.m_input = VK_NULL_HANDLE;
other.m_vert_shader = VK_NULL_HANDLE;
other.m_frag_shader = VK_NULL_HANDLE;
other.m_iris = nullptr;
other.m_id = "";
other.bypass = false;
return *this;
}
void pass::destroy() {
if (!m_iris)
return;
vulkan::wait_idle(m_iris);
if (m_pipeline_layout) vkDestroyPipelineLayout(m_iris->device, m_pipeline_layout, nullptr);
if (m_pipeline) vkDestroyPipeline(m_iris->device, m_pipeline, nullptr);
if (m_render_pass) vkDestroyRenderPass(m_iris->device, m_render_pass, nullptr);
if (m_frag_shader) vkDestroyShaderModule(m_iris->device, m_frag_shader, nullptr);
if (m_vert_shader != m_iris->default_vert_shader)
if (m_vert_shader) vkDestroyShaderModule(m_iris->device, m_vert_shader, nullptr);
}
pass::~pass() {
destroy();
}
bool pass::rebuild() {
vulkan::wait_idle(m_iris);
if (m_pipeline_layout) vkDestroyPipelineLayout(m_iris->device, m_pipeline_layout, nullptr);
if (m_pipeline) vkDestroyPipeline(m_iris->device, m_pipeline, nullptr);
if (m_render_pass) vkDestroyRenderPass(m_iris->device, m_render_pass, nullptr);
VkPushConstantRange push_constant_range = {};
push_constant_range.offset = 0;
push_constant_range.size = sizeof(push_constants);
push_constant_range.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
VkPipelineLayoutCreateInfo pipeline_layout_info = {};
pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipeline_layout_info.setLayoutCount = 1;
pipeline_layout_info.pSetLayouts = &m_iris->shader_descriptor_set_layout;
pipeline_layout_info.pushConstantRangeCount = 1;
pipeline_layout_info.pPushConstantRanges = &push_constant_range;
if (vkCreatePipelineLayout(m_iris->device, &pipeline_layout_info, nullptr, &m_pipeline_layout) != VK_SUCCESS) {
fprintf(stderr, "render: Failed to create shader pipeline layout\n");
destroy();
return false;
}
// Create render pass
VkAttachmentDescription color_attachment = {};
color_attachment.format = m_iris->image.format;
color_attachment.samples = VK_SAMPLE_COUNT_1_BIT;
color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
color_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
color_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
color_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
color_attachment.finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
VkAttachmentReference color_attachment_ref = {};
color_attachment_ref.attachment = 0;
color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkSubpassDescription subpass = {};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &color_attachment_ref;
VkSubpassDependency dependency = {};
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
dependency.dstSubpass = 0;
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.srcAccessMask = 0;
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
VkRenderPass render_pass = VK_NULL_HANDLE;
VkRenderPassCreateInfo render_pass_info = {};
render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
render_pass_info.attachmentCount = 1;
render_pass_info.pAttachments = &color_attachment;
render_pass_info.subpassCount = 1;
render_pass_info.pSubpasses = &subpass;
render_pass_info.dependencyCount = 1;
render_pass_info.pDependencies = &dependency;
if (vkCreateRenderPass(m_iris->device, &render_pass_info, nullptr, &m_render_pass) != VK_SUCCESS) {
fprintf(stderr, "render: Failed to create shader render pass\n");
destroy();
return false;
}
// Create graphics pipeline
VkPipelineShaderStageCreateInfo shader_stages[2] = {};
shader_stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shader_stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
shader_stages[0].module = m_iris->default_vert_shader;
shader_stages[0].pName = "main";
shader_stages[0].pNext = VK_NULL_HANDLE;
shader_stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shader_stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
shader_stages[1].module = m_frag_shader;
shader_stages[1].pName = "main";
shader_stages[1].pNext = VK_NULL_HANDLE;
static const VkDynamicState dynamic_states[] = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR
};
VkPipelineDynamicStateCreateInfo dynamic_state_info = {};
dynamic_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamic_state_info.dynamicStateCount = 2;
dynamic_state_info.pDynamicStates = dynamic_states;
VkPipelineVertexInputStateCreateInfo vertex_input_info = {};
vertex_input_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertex_input_info.vertexBindingDescriptionCount = 0;
vertex_input_info.pVertexBindingDescriptions = VK_NULL_HANDLE;
vertex_input_info.vertexAttributeDescriptionCount = 0;
vertex_input_info.pVertexAttributeDescriptions = VK_NULL_HANDLE;
VkPipelineInputAssemblyStateCreateInfo input_assembly_info = {};
input_assembly_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
input_assembly_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
input_assembly_info.primitiveRestartEnable = VK_FALSE;
VkViewport viewport = {};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = (float)m_iris->image.width;
viewport.height = (float)m_iris->image.height;
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
VkExtent2D extent = {};
extent.width = m_iris->image.width;
extent.height = m_iris->image.height;
VkRect2D scissor = {};
scissor.offset = {0, 0};
scissor.extent = extent;
VkPipelineViewportStateCreateInfo viewport_state_info = {};
viewport_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewport_state_info.viewportCount = 1;
viewport_state_info.pViewports = &viewport;
viewport_state_info.scissorCount = 1;
viewport_state_info.pScissors = &scissor;
VkPipelineRasterizationStateCreateInfo rasterizer_info = {};
rasterizer_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizer_info.depthClampEnable = VK_FALSE;
rasterizer_info.rasterizerDiscardEnable = VK_FALSE;
rasterizer_info.polygonMode = VK_POLYGON_MODE_FILL;
rasterizer_info.lineWidth = 1.0f;
rasterizer_info.cullMode = VK_CULL_MODE_NONE;
rasterizer_info.frontFace = VK_FRONT_FACE_CLOCKWISE;
rasterizer_info.depthBiasEnable = VK_FALSE;
VkPipelineColorBlendAttachmentState blend_attachment_state = {};
blend_attachment_state.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
blend_attachment_state.blendEnable = VK_FALSE;
VkPipelineColorBlendStateCreateInfo blend_state_info{};
blend_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
blend_state_info.logicOpEnable = VK_FALSE;
blend_state_info.attachmentCount = 1;
blend_state_info.pAttachments = &blend_attachment_state;
VkPipelineMultisampleStateCreateInfo multisampling_state_info = {};
multisampling_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampling_state_info.sampleShadingEnable = VK_FALSE;
multisampling_state_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
VkGraphicsPipelineCreateInfo pipeline_info = {};
pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipeline_info.stageCount = 2;
pipeline_info.pStages = shader_stages;
pipeline_info.pVertexInputState = &vertex_input_info;
pipeline_info.pInputAssemblyState = &input_assembly_info;
pipeline_info.pViewportState = &viewport_state_info;
pipeline_info.pRasterizationState = &rasterizer_info;
pipeline_info.pMultisampleState = &multisampling_state_info;
pipeline_info.pDepthStencilState = nullptr; // Optional
pipeline_info.pColorBlendState = &blend_state_info;
pipeline_info.pDynamicState = &dynamic_state_info;
pipeline_info.layout = m_pipeline_layout;
pipeline_info.renderPass = m_render_pass;
pipeline_info.subpass = 0;
pipeline_info.pTessellationState = VK_NULL_HANDLE;
pipeline_info.basePipelineHandle = VK_NULL_HANDLE; // Optional
pipeline_info.basePipelineIndex = -1; // Optional
if (vkCreateGraphicsPipelines(m_iris->device, VK_NULL_HANDLE, 1, &pipeline_info, nullptr, &m_pipeline) != VK_SUCCESS) {
fprintf(stderr, "render: Failed to create shader pipeline\n");
destroy();
return false;
}
return true;
}
bool pass::ready() {
return m_pipeline_layout &&
m_pipeline &&
m_render_pass &&
m_vert_shader &&
m_frag_shader &&
m_iris;
}
VkPipelineLayout& pass::get_pipeline_layout() {
return m_pipeline_layout;
}
VkPipeline& pass::get_pipeline() {
return m_pipeline;
}
VkRenderPass& pass::get_render_pass() {
return m_render_pass;
}
VkImageView& pass::get_input() {
return m_input;
}
VkShaderModule& pass::get_vert_shader() {
return m_vert_shader;
}
VkShaderModule& pass::get_frag_shader() {
return m_frag_shader;
}
std::string pass::get_id() const {
return m_id;
}
std::unordered_map > g_builtin_shaders = {
{ "iris-ntsc-encoder", { (void*)g_encoder_frag_shader_data, (size_t)g_encoder_frag_shader_size } },
{ "iris-ntsc-decoder", { (void*)g_decoder_frag_shader_data, (size_t)g_decoder_frag_shader_size } },
{ "iris-ntsc-curvature", { (void*)g_curvature_frag_shader_data, (size_t)g_curvature_frag_shader_size } },
{ "iris-ntsc-scanlines", { (void*)g_scanlines_frag_shader_data, (size_t)g_scanlines_frag_shader_size } },
{ "iris-ntsc-noise", { (void*)g_noise_frag_shader_data, (size_t)g_noise_frag_shader_size } }
};
void push(iris::instance* iris, void* data, size_t size, std::string id) {
iris->shader_passes.push_back(new pass(iris, data, size, id));
}
void push(iris::instance* iris, std::string id) {
auto s = g_builtin_shaders.find(id);
if (s != g_builtin_shaders.end()) {
push(iris, s->second.first, s->second.second, id);
}
}
void pop(iris::instance* iris) {
delete iris->shader_passes.back();
iris->shader_passes.pop_back();
}
void insert(iris::instance* iris, int i, void* data, size_t size, std::string id) {
iris->shader_passes.insert(
iris->shader_passes.begin() + i,
new pass(iris, data, size, id)
);
}
void erase(iris::instance* iris, int i) {
delete iris->shader_passes.at(i);
iris->shader_passes.erase(iris->shader_passes.begin() + i);
}
pass* at(iris::instance* iris, int i) {
return iris->shader_passes.at(i);
}
void swap(iris::instance* iris, int a, int b) {
pass* t = iris->shader_passes.at(a);
iris->shader_passes[a] = iris->shader_passes[b];
iris->shader_passes[b] = t;
}
pass* front(iris::instance* iris) {
return iris->shader_passes.front();
}
pass* back(iris::instance* iris) {
return iris->shader_passes.back();
}
size_t count(iris::instance* iris) {
return iris->shader_passes.size();
}
void clear(iris::instance* iris) {
for (auto& pass : iris->shader_passes) {
delete pass;
}
iris->shader_passes.clear();
}
std::vector & vector(iris::instance* iris) {
return iris->shader_passes;
}
}
================================================
FILE: frontend/ui/about.cpp
================================================
#include
#include
#include
#include "iris.hpp"
#include "config.hpp"
#include "res/IconsMaterialSymbols.h"
namespace iris {
void show_about_window(iris::instance* iris) {
using namespace ImGui;
static ImGuiWindowFlags flags =
ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoDocking |
ImGuiWindowFlags_AlwaysAutoResize;
if (GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable && !GetIO().ConfigViewportsNoDecoration)
flags |= ImGuiWindowFlags_NoTitleBar;
if (Begin("About", &iris->show_about_window, flags)) {
if (BeginChild("##iconchild", ImVec2(100.0, 220.0), ImGuiChildFlags_AutoResizeY)) {
Image((ImTextureID)(intptr_t)iris->iris_icon.descriptor_set, ImVec2(100.0, 100.0));
} EndChild(); SameLine(0.0, 10.0);
if (BeginChild("##textchild", ImVec2(350.0, 0.0))) {
PushFont(iris->font_heading);
Text(IRIS_TITLE);
PopFont();
Separator();
Text("Experimental PlayStation 2 emulator");
Text("");
Text("Available at "); SameLine(0.0, 0.0);
TextLinkOpenURL("https://github.com/allkern/iris", "https://github.com/allkern/iris");
Text("");
TextWrapped(
"Special thanks to: The emudev Discord server, Ziemas, "
"refraction, ncarrillo, cakehonolulu, Layle, and "
"the PCSX2 team for their kind support."
);
Text("");
Text("Please file any issues to "); SameLine(0.0, 0.0);
TextLinkOpenURL("our GitHub issues page", "https://github.com/allkern/iris/issues"); SameLine(0.0, 0.0);
Text(".");
} EndChild();
} End();
}
}
================================================
FILE: frontend/ui/bios_setting.cpp
================================================
#include "iris.hpp"
#include "res/IconsMaterialSymbols.h"
#include "portable-file-dialogs.h"
#include "rom.h"
namespace iris {
int stage = 0;
bool bios_checked = false;
int bios_valid = false;
int is_valid(const char* path) {
FILE* f = fopen(path, "rb");
if (!f)
return 0;
fseek(f, 0, SEEK_END);
size_t size = ftell(f);
fseek(f, 0, SEEK_SET);
uint8_t* buf = new uint8_t[size];
fread(buf, 1, size, f);
int valid = ps2_rom0_is_valid(buf, size);
fclose(f);
delete[] buf;
return valid ? 2 : 1;
}
void show_memory_card_stage(iris::instance* iris) {
using namespace ImGui;
if (BeginChild("##iconchild", ImVec2(100.0, 0.0), ImGuiChildFlags_AutoResizeY)) {
Image((ImTextureID)(intptr_t)iris->iris_icon.descriptor_set, ImVec2(100.0, 100.0));
} EndChild(); SameLine(0.0, 10.0);
if (BeginChild("##textchild", ImVec2(360.0, 0.0), ImGuiChildFlags_AutoResizeY)) {
PushFont(iris->font_heading);
Text("Done!");
PopFont();
Separator();
Text("You may now close this window and start using Iris.\n\n"
"Additionally you might want to check out the settings\n"
"menu to configure memory cards, display, audio, and\n"
"input options.\n\n"
);
Text(ICON_MS_WARNING " Please note that Iris is a work-in-progress emulator.\n"
"You might encounter bugs or experience poor performance\n"
"in general. We will appreciate any feedback or bug reports\n"
"you may provide.\n\n"
);
Text("You can report issues or provide feedback at:");
TextLinkOpenURL("https://github.com/allkern/iris/issues");
Separator();
static bool open_settings = true;
if (Button("Done")) {
CloseCurrentPopup();
iris->show_bios_setting_window = false;
if (open_settings) {
iris->show_settings = true;
}
} SameLine();
Checkbox("Open settings menu", &open_settings);
} EndChild();
}
void show_bios_stage(iris::instance* iris) {
using namespace ImGui;
static char buf[512];
PushFont(iris->font_heading);
Text("Welcome to Iris!");
PopFont();
Separator();
Text(
"Iris requires a PlayStation 2 BIOS file in order to run.\n\n"
"Please select one using the box below or provide one using the\n"
"command line arguments."
);
if (InputTextWithHint("##BIOS", "e.g. scph10000.bin", buf, 512, ImGuiInputTextFlags_EscapeClearsAll | ImGuiInputTextFlags_EnterReturnsTrue)) {
bios_checked = true;
bios_valid = is_valid(buf);
}
SameLine();
if (Button(ICON_MS_FOLDER)) {
audio::mute(iris);
auto f = pfd::open_file("Select BIOS file", "", {
"All File Types (*.bin; *.rom0)", "*.bin *.rom0",
"All Files (*.*)", "*"
});
while (!f.ready());
audio::unmute(iris);
if (f.result().size()) {
strncpy(buf, f.result().at(0).c_str(), 512);
bios_checked = true;
bios_valid = is_valid(buf);
}
}
if (bios_checked) {
ImVec4 col = bios_valid ? ImVec4(0.0f, 1.0f, 0.0f, 1.0f) : ImVec4(1.0f, 1.0f, 0.0f, 1.0f);
const char* text = nullptr;
switch (bios_valid) {
case 0: {
col = ImVec4(0.86f, 0.19f, 0.18f, 1.0f);
text = ICON_MS_CLOSE " Couldn't open the specified file.";
} break;
case 1: {
col = ImVec4(0.90f, 0.73f, 0.2f, 1.0f);
text = ICON_MS_WARNING " BIOS is unknown, it might not work as expected.";
} break;
case 2: {
col = ImVec4(0.42f, 0.85f, 0.1f, 1.0f);
text = ICON_MS_CHECK " BIOS is known!";
} break;
}
TextColored(col, text);
}
// To-do: Add file validation
Separator();
BeginDisabled(!bios_valid || !buf[0]);
if (Button("Next")) {
iris->bios_path = buf;
iris->dump_to_file = true;
if (!ps2_load_bios(iris->ps2, iris->bios_path.c_str())) {
push_info(iris, "Couldn't load BIOS");
stage = 0;
} else {
stage = 1;
}
}
EndDisabled();
}
void show_bios_setting_window(iris::instance* iris) {
using namespace ImGui;
OpenPopup("Welcome");
// Always center this window when appearing
ImVec2 center = GetMainViewport()->GetCenter();
SetNextWindowPos(center, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
if (BeginPopupModal("Welcome", NULL, ImGuiWindowFlags_AlwaysAutoResize)) {
switch (stage) {
case 0: show_bios_stage(iris); break;
case 1: show_memory_card_stage(iris); break;
}
EndPopup();
}
}
}
================================================
FILE: frontend/ui/breakpoints.cpp
================================================
#include
#include
#include
#include "iris.hpp"
#include "res/IconsMaterialSymbols.h"
namespace iris {
const char* cpu_names[] = {
"EE",
"IOP"
};
static breakpoint* selected = nullptr;
static breakpoint editable;
void show_breakpoints_table(iris::instance* iris) {
using namespace ImGui;
if (BeginTable("##breakpoints", 5, ImGuiTableFlags_RowBg | ImGuiTableFlags_Sortable)) {
PushFont(iris->font_small_code);
TableSetupColumn("Address");
TableSetupColumn("CPU");
TableSetupColumn("Flags");
TableSetupColumn("Size");
TableSetupColumn("Actions");
TableHeadersRow();
PopFont();
TableNextRow();
int i = 0;
for (breakpoint& b : iris->breakpoints) {
TableSetColumnIndex(0);
char buf[16]; sprintf(buf, "##d%x", i);
if (Selectable(buf, &b == selected, ImGuiSelectableFlags_AllowOverlap | ImGuiSelectableFlags_SpanAllColumns)) {
selected = &b;
} SameLine(0.0, 0.0);
PushFont(iris->font_code);
if (b.symbol) {
Text("%s", b.symbol);
} else {
Text("%08x", b.addr);
}
PopFont();
TableSetColumnIndex(1);
Text(b.cpu == BKPT_CPU_EE ? "EE" : "IOP");
TableSetColumnIndex(2);
PushFont(iris->font_code);
Text("%c%c%c",
b.cond_r ? 'R' : '.',
b.cond_w ? 'W' : '.',
b.cond_x ? 'X' : '.'
);
PopFont();
TableSetColumnIndex(3);
Text("%d", b.size);
TableSetColumnIndex(4);
sprintf(buf, b.enabled ? ICON_MS_CHECK "##%x" : "##%x", i);
if (Selectable(buf, false, 0, ImVec2(20, 0))) {
b.enabled = !b.enabled;
} SameLine();
sprintf(buf, ICON_MS_DELETE "##%x", i);
if (Selectable(buf, false, 0, ImVec2(20, 0))) {
selected = nullptr;
iris->breakpoints.erase(iris->breakpoints.begin() + i);
}
i++;
TableNextRow();
}
EndTable();
}
}
uint32_t parse_address(iris::instance* iris, const char* buf, const char** name) {
if (!buf || !buf[0])
return 0;
if (isalpha(buf[0]) || buf[0] == '_') {
for (iris::elf_symbol& sym : iris->symbols) {
if (strcmp(sym.name, buf) == 0) {
*name = sym.name;
return sym.addr;
}
}
} else {
*name = nullptr;
return strtoul(buf, NULL, 16);
}
return 0;
}
void show_breakpoint_editor(iris::instance* iris) {
using namespace ImGui;
if (BeginCombo("CPU", cpu_names[editable.cpu], ImGuiComboFlags_HeightSmall)) {
for (int i = 0; i < 2; i++) {
if (Selectable(cpu_names[i], editable.cpu == i)) {
editable.cpu = i;
}
}
EndCombo();
}
static char buf[512];
PushFont(iris->font_code);
if (InputText("##address", buf, 512, ImGuiInputTextFlags_EnterReturnsTrue)) {
editable.addr = parse_address(iris, buf, &editable.symbol);
} SameLine();
PopFont();
Text("Address");
Checkbox("##enabled", &editable.enabled); SameLine();
Checkbox("##r", &editable.cond_r); SameLine();
Checkbox("##w", &editable.cond_w); SameLine();
Checkbox("Flags", &editable.cond_x);
BeginDisabled(!selected);
if (Button("Edit breakpoint")) {
editable.addr = parse_address(iris, buf, &editable.symbol);
*selected = editable;
} SameLine();
EndDisabled();
if (Button("New breakpoint")) {
editable.addr = parse_address(iris, buf, &editable.symbol);
iris->breakpoints.push_back(editable);
selected = &iris->breakpoints.back();
}
}
void show_breakpoints(iris::instance* iris) {
using namespace ImGui;
if (imgui::BeginEx("Breakpoints", &iris->show_breakpoints, ImGuiWindowFlags_MenuBar)) {
if (BeginMenuBar()) {
MenuItem("Settings");
EndMenuBar();
}
if (Button(ICON_MS_DELETE, ImVec2(50, 0))) {
selected = nullptr;
iris->breakpoints.clear();
} SameLine();
if (IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_DelayNormal)) {
SetTooltip("Clear all");
}
if (Button(ICON_MS_REMOVE_SELECTION)) {
for (breakpoint& b : iris->breakpoints) {
b.enabled = false;
}
} SameLine();
if (IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_DelayNormal)) {
SetTooltip("Disable all");
}
if (Button(ICON_MS_SELECT)) {
for (breakpoint& b : iris->breakpoints) {
b.enabled = true;
}
}
if (IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_DelayNormal)) {
SetTooltip("Enable all");
}
Separator();
if (BeginChild("##tablechild", ImVec2(0, GetContentRegionAvail().y / 2.0f))) {
show_breakpoints_table(iris);
} EndChild();
SeparatorText("Add breakpoint");
if (BeginChild("##tablechild2")) {
show_breakpoint_editor(iris);
} EndChild();
} End();
}
}
================================================
FILE: frontend/ui/control.cpp
================================================
#include
#include
#include
#include
#include "iris.hpp"
#include "res/IconsMaterialSymbols.h"
#include "ee/ee_dis.h"
#include "ee/ee_def.hpp"
#include "ee/vu_def.hpp"
#include "iop/iop_dis.h"
#define IM_RGB(r, g, b) ImVec4(((float)r / 255.0f), ((float)g / 255.0f), ((float)b / 255.0f), 1.0)
namespace iris {
struct ee_dis_state g_ee_dis_state;
struct iop_dis_state g_iop_dis_state;
void print_highlighted(iris::instance* iris, const char* buf) {
using namespace ImGui;
std::vector tokens;
std::string text;
while (*buf) {
text.clear();
if (isalpha(*buf)) {
while (isalpha(*buf) || isdigit(*buf) || (*buf == '.'))
text.push_back(*buf++);
} else if (isxdigit(*buf) || (*buf == '-')) {
while (isxdigit(*buf) || (*buf == 'x') || (*buf == '-'))
text.push_back(*buf++);
} else if (*buf == '$') {
while (*buf == '$' || isdigit(*buf) || isalpha(*buf) || *buf == '_')
text.push_back(*buf++);
} else if (*buf == ',') {
while (*buf == ',')
text.push_back(*buf++);
} else if (*buf == '(') {
while (*buf == '(')
text.push_back(*buf++);
} else if (*buf == ')') {
while (*buf == ')')
text.push_back(*buf++);
} else if (*buf == '<') {
while (*buf != '>')
text.push_back(*buf++);
text.push_back(*buf++);
} else if (*buf == '_') {
text.push_back(*buf++);
} else if (*buf == '.') {
text.push_back(*buf++);
} else {
printf("unhandled char %c (%d) \"%s\"\n", *buf, *buf, buf);
exit(1);
}
while (isspace(*buf))
text.push_back(*buf++);
tokens.push_back(text);
}
for (const std::string& t : tokens) {
if (isalpha(t[0])) {
ImVec4 col = ImVec4(
iris->codeview_color_mnemonic.Value.x,
iris->codeview_color_mnemonic.Value.y,
iris->codeview_color_mnemonic.Value.z,
iris->codeview_color_mnemonic.Value.w
);
TextColored(col, "%s", t.c_str());
} else if (isdigit(t[0]) || t[0] == '-') {
ImVec4 col = ImVec4(
iris->codeview_color_number.Value.x,
iris->codeview_color_number.Value.y,
iris->codeview_color_number.Value.z,
iris->codeview_color_number.Value.w
);
TextColored(col, "%s", t.c_str());
} else if (t[0] == '$') {
ImVec4 col = ImVec4(
iris->codeview_color_register.Value.x,
iris->codeview_color_register.Value.y,
iris->codeview_color_register.Value.z,
iris->codeview_color_register.Value.w
);
TextColored(col, "%s", t.c_str());
} else if (t[0] == '<') {
ImVec4 col = ImVec4(
iris->codeview_color_other.Value.x,
iris->codeview_color_other.Value.y,
iris->codeview_color_other.Value.z,
iris->codeview_color_other.Value.w
);
TextColored(col, "%s", t.c_str());
} else {
Text("%s", t.c_str());
}
SameLine(0.0f, 0.0f);
}
NewLine();
}
static void show_ee_disassembly_view(iris::instance* iris) {
using namespace ImGui;
float font_scale = GetStyle().FontScaleMain;
GetStyle().FontScaleMain = iris->codeview_font_scale;
PushFont(iris->font_code);
if (!iris->codeview_use_theme_background) {
PushStyleColor(ImGuiCol_TableRowBg, ImVec4(
iris->codeview_color_background.Value.x,
iris->codeview_color_background.Value.y,
iris->codeview_color_background.Value.z,
iris->codeview_color_background.Value.w
));
PushStyleColor(ImGuiCol_TableRowBgAlt, ImVec4(
iris->codeview_color_background.Value.x,
iris->codeview_color_background.Value.y,
iris->codeview_color_background.Value.z,
iris->codeview_color_background.Value.w
));
PushStyleColor(ImGuiCol_Text, ImVec4(
iris->codeview_color_text.Value.x,
iris->codeview_color_text.Value.y,
iris->codeview_color_text.Value.z,
iris->codeview_color_text.Value.w
));
PushStyleColor(ImGuiCol_TextDisabled, ImVec4(
iris->codeview_color_comment.Value.x,
iris->codeview_color_comment.Value.y,
iris->codeview_color_comment.Value.z,
iris->codeview_color_comment.Value.w
));
}
if (BeginTable("table1", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit)) {
TableSetupColumn("a", ImGuiTableColumnFlags_NoResize, 15.0f);
TableSetupColumn("b", ImGuiTableColumnFlags_NoResize, 15.0f);
TableSetupColumn("c", ImGuiTableColumnFlags_NoResize);
for (int row = -64; row < 64; row++) {
if (iris->ee_control_follow_pc) {
g_ee_dis_state.pc = iris->ps2->ee->pc + (row * 4);
} else {
g_ee_dis_state.pc = iris->ee_control_address + (row * 4);
}
TableNextRow();
TableSetColumnIndex(0);
PushFont(iris->font_icons);
auto v = std::find_if(iris->breakpoints.begin(), iris->breakpoints.end(), [](breakpoint& a) {
return a.addr == g_ee_dis_state.pc && a.cpu == BKPT_CPU_EE;
});
if (v != iris->breakpoints.end()) {
Text(" " ICON_MS_FIBER_MANUAL_RECORD " ");
}
TableSetColumnIndex(1);
if (g_ee_dis_state.pc == iris->ps2->ee->pc)
Text(ICON_MS_CHEVRON_RIGHT " ");
PopFont();
TableSetColumnIndex(2);
for (elf_symbol& sym : iris->symbols) {
if (sym.addr == g_ee_dis_state.pc) {
ImVec4 col = ImVec4(
iris->codeview_color_mnemonic.Value.x,
iris->codeview_color_mnemonic.Value.y,
iris->codeview_color_mnemonic.Value.z,
iris->codeview_color_mnemonic.Value.w
);
PushFont(iris->font_icons);
TextColored(col, ICON_MS_STAT_0); SameLine();
PopFont();
TextColored(col, "%s", sym.name);
break;
}
}
uint32_t opcode = ee_bus_read32(iris->ps2->ee_bus, g_ee_dis_state.pc & 0x1fffffff);
char buf[128], id[16];
char addr_str[9]; sprintf(addr_str, "%08x", g_ee_dis_state.pc);
char opcode_str[9]; sprintf(opcode_str, "%08x", opcode);
char* disassembly = ee_disassemble(buf, opcode, &g_ee_dis_state);
sprintf(id, "##%d", row);
Selectable(id, false, ImGuiSelectableFlags_AllowDoubleClick | ImGuiSelectableFlags_SpanAllColumns);
if (IsItemHovered() && IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
breakpoint b;
b.addr = g_ee_dis_state.pc;
b.cond_r = false;
b.cond_w = false;
b.cond_x = true;
b.cpu = BKPT_CPU_EE;
b.size = 4;
b.enabled = true;
auto addr = std::find_if(iris->breakpoints.begin(), iris->breakpoints.end(), [](breakpoint& a) {
return a.addr == g_ee_dis_state.pc && a.cpu == BKPT_CPU_EE;
});
if (addr == iris->breakpoints.end()) {
iris->breakpoints.push_back(b);
} else {
iris->breakpoints.erase(addr);
}
} SameLine();
if (BeginPopupContextItem()) {
PushFont(iris->font_small_code);
TextDisabled("0x%08x", g_ee_dis_state.pc);
PopFont();
PushFont(iris->font_body);
if (BeginMenu(ICON_MS_CONTENT_COPY " Copy")) {
if (Selectable(ICON_MS_SORT " Address")) {
SDL_SetClipboardText(addr_str);
}
if (Selectable(ICON_MS_SORT " Opcode")) {
SDL_SetClipboardText(opcode_str);
}
if (Selectable(ICON_MS_SORT " Disassembly")) {
SDL_SetClipboardText(disassembly);
}
ImGui::EndMenu();
}
auto addr = std::find_if(iris->breakpoints.begin(), iris->breakpoints.end(), [](breakpoint& a) {
return a.addr == g_ee_dis_state.pc && a.cpu == BKPT_CPU_EE;
});
if (addr != iris->breakpoints.end()) {
if (MenuItem(ICON_MS_CANCEL " Remove this breakpoint")) {
iris->breakpoints.erase(addr);
}
} else {
if (MenuItem(ICON_MS_ADD_CIRCLE " Add breakpoint here")) {
breakpoint b;
b.addr = g_ee_dis_state.pc;
b.cond_r = false;
b.cond_w = false;
b.cond_x = true;
b.cpu = BKPT_CPU_EE;
b.size = 4;
b.enabled = true;
iris->breakpoints.push_back(b);
}
}
PopFont();
EndPopup();
}
Text("%s ", addr_str); SameLine();
TextDisabled("%s ", opcode_str); SameLine();
if (true) {
print_highlighted(iris, disassembly);
} else {
Text("%s", disassembly);
}
if (iris->ee_control_follow_pc) {
if (g_ee_dis_state.pc == iris->ps2->ee->pc)
SetScrollHereY(0.5f);
} else {
if (g_ee_dis_state.pc == iris->ee_control_address)
SetScrollHereY(0.5f);
}
}
EndTable();
}
if (!iris->codeview_use_theme_background) {
PopStyleColor(4);
}
PopFont();
GetStyle().FontScaleMain = font_scale;
}
static void show_iop_disassembly_view(iris::instance* iris) {
using namespace ImGui;
float font_scale = GetStyle().FontScaleMain;
GetStyle().FontScaleMain = iris->codeview_font_scale;
PushFont(iris->font_code);
if (!iris->codeview_use_theme_background) {
PushStyleColor(ImGuiCol_TableRowBg, ImVec4(
iris->codeview_color_background.Value.x,
iris->codeview_color_background.Value.y,
iris->codeview_color_background.Value.z,
iris->codeview_color_background.Value.w
));
PushStyleColor(ImGuiCol_TableRowBgAlt, ImVec4(
iris->codeview_color_background.Value.x,
iris->codeview_color_background.Value.y,
iris->codeview_color_background.Value.z,
iris->codeview_color_background.Value.w
));
PushStyleColor(ImGuiCol_Text, ImVec4(
iris->codeview_color_text.Value.x,
iris->codeview_color_text.Value.y,
iris->codeview_color_text.Value.z,
iris->codeview_color_text.Value.w
));
PushStyleColor(ImGuiCol_TextDisabled, ImVec4(
iris->codeview_color_comment.Value.x,
iris->codeview_color_comment.Value.y,
iris->codeview_color_comment.Value.z,
iris->codeview_color_comment.Value.w
));
}
if (BeginTable("table2", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit)) {
TableSetupColumn("a", ImGuiTableColumnFlags_NoResize, 15.0f);
TableSetupColumn("b", ImGuiTableColumnFlags_NoResize, 15.0f);
TableSetupColumn("c", ImGuiTableColumnFlags_NoResize);
for (int row = -64; row < 64; row++) {
if (iris->iop_control_follow_pc) {
g_iop_dis_state.addr = iris->ps2->iop->pc + (row * 4);
} else {
g_iop_dis_state.addr = iris->iop_control_address + (row * 4);
}
TableNextRow();
TableSetColumnIndex(0);
PushFont(iris->font_icons);
auto v = std::find_if(iris->breakpoints.begin(), iris->breakpoints.end(), [](breakpoint& a) {
return a.addr == g_iop_dis_state.addr && a.cpu == BKPT_CPU_IOP;
});
if (v != iris->breakpoints.end()) {
Text(" " ICON_MS_FIBER_MANUAL_RECORD " ");
}
TableSetColumnIndex(1);
if (g_iop_dis_state.addr == iris->ps2->iop->pc)
Text(ICON_MS_CHEVRON_RIGHT " ");
PopFont();
TableSetColumnIndex(2);
uint32_t opcode = iop_bus_read32(iris->ps2->iop_bus, g_iop_dis_state.addr & 0x1fffffff);
char buf[128];
char addr_str[9]; sprintf(addr_str, "%08x", g_iop_dis_state.addr);
char opcode_str[9]; sprintf(opcode_str, "%08x", opcode);
char* disassembly = iop_disassemble(buf, opcode, &g_iop_dis_state);
Text("%s ", addr_str); SameLine();
TextDisabled("%s ", opcode_str); SameLine();
char id[16];
sprintf(id, "##%d", row);
Selectable(id, false, ImGuiSelectableFlags_AllowDoubleClick | ImGuiSelectableFlags_SpanAllColumns);
if (IsItemHovered() && IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
breakpoint b;
b.addr = g_iop_dis_state.addr;
b.cond_r = false;
b.cond_w = false;
b.cond_x = true;
b.cpu = BKPT_CPU_IOP;
b.size = 4;
b.enabled = true;
auto addr = std::find_if(iris->breakpoints.begin(), iris->breakpoints.end(), [](breakpoint& a) {
return a.addr == g_iop_dis_state.addr && a.cpu == BKPT_CPU_IOP;
});
if (addr == iris->breakpoints.end()) {
iris->breakpoints.push_back(b);
} else {
iris->breakpoints.erase(addr);
}
} SameLine();
if (BeginPopupContextItem()) {
PushFont(iris->font_small_code);
TextDisabled("0x%08x", g_iop_dis_state.addr);
PopFont();
PushFont(iris->font_body);
if (BeginMenu(ICON_MS_CONTENT_COPY " Copy")) {
if (Selectable(ICON_MS_SORT " Address")) {
SDL_SetClipboardText(addr_str);
}
if (Selectable(ICON_MS_SORT " Opcode")) {
SDL_SetClipboardText(opcode_str);
}
if (Selectable(ICON_MS_SORT " Disassembly")) {
SDL_SetClipboardText(disassembly);
}
ImGui::EndMenu();
}
auto addr = std::find_if(iris->breakpoints.begin(), iris->breakpoints.end(), [](breakpoint& a) {
return a.addr == g_iop_dis_state.addr && a.cpu == BKPT_CPU_IOP;
});
if (addr != iris->breakpoints.end()) {
if (MenuItem(ICON_MS_CANCEL " Remove this breakpoint")) {
iris->breakpoints.erase(addr);
}
} else {
if (MenuItem(ICON_MS_ADD_CIRCLE " Add breakpoint here")) {
breakpoint b;
b.addr = g_iop_dis_state.addr;
b.cond_r = false;
b.cond_w = false;
b.cond_x = true;
b.cpu = BKPT_CPU_IOP;
b.size = 4;
b.enabled = true;
iris->breakpoints.push_back(b);
}
}
PopFont();
EndPopup();
}
if (true) {
print_highlighted(iris, disassembly);
} else {
Text("%s", disassembly);
}
if (iris->iop_control_follow_pc) {
if (g_iop_dis_state.addr == iris->ps2->iop->pc)
SetScrollHereY(0.5f);
} else {
if (g_iop_dis_state.addr == iris->iop_control_address)
SetScrollHereY(0.5f);
}
} EndTable();
}
PopFont();
if (!iris->codeview_use_theme_background) {
PopStyleColor(4);
}
GetStyle().FontScaleMain = font_scale;
}
void show_ee_control(iris::instance* iris) {
using namespace ImGui;
PushFont(iris->font_icons);
if (imgui::BeginEx("EE (R5900)", &iris->show_ee_control)) {
if (Button(iris->pause ? ICON_MS_PLAY_ARROW : ICON_MS_PAUSE, ImVec2(50, 0))) {
iris->pause = !iris->pause;
} SameLine();
if (Button(ICON_MS_STEP_INTO)) {
iris->pause = true;
iris->step = true;
} SameLine();
if (Button(ICON_MS_STEP_OVER)) {
iris->step_over = true;
iris->step_over_addr = iris->ps2->ee->pc + 4;
iris->pause = false;
} SameLine();
if (Button(ICON_MS_STEP_OUT)) {
iris->step_out = true;
iris->pause = false;
} SameLine();
if (Button(ICON_MS_MOVE_DOWN)) {
iris->ee_control_follow_pc = true;
} SameLine();
if (Button(ICON_MS_AUTORENEW)) {
ee_flush_cache(iris->ps2->ee);
}
if (iris->symbols.size()) {
TextDisabled("Current function:"); SameLine();
const char* func = "";
for (elf_symbol& sym : iris->symbols) {
if (iris->ps2->ee->pc >= sym.addr && iris->ps2->ee->pc < (sym.addr + sym.size)) {
func = sym.name;
break;
}
}
Text("%s", func);
}
SeparatorText("Disassembly");
if (BeginChild("ee##disassembly")) {
show_ee_disassembly_view(iris);
} EndChild();
} End();
PopFont();
}
void show_iop_control(iris::instance* iris) {
using namespace ImGui;
PushFont(iris->font_icons);
if (imgui::BeginEx("IOP (R3000)", &iris->show_iop_control)) {
if (Button(iris->pause ? ICON_MS_PLAY_ARROW : ICON_MS_PAUSE, ImVec2(50, 0))) {
iris->pause = !iris->pause;
} SameLine();
if (Button(ICON_MS_STEP)) {
iris->pause = true;
ps2_step_iop(iris->ps2);
}
if (InputInt("Address", (int32_t*)&iris->iop_control_address, 0, 0, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue)) {
iris->iop_control_follow_pc = false;
}
if (Button(ICON_MS_MOVE_DOWN)) {
iris->iop_control_follow_pc = true;
} SameLine();
SeparatorText("Disassembly");
if (BeginChild("iop##disassembly")) {
show_iop_disassembly_view(iris);
} EndChild();
} End();
PopFont();
}
}
================================================
FILE: frontend/ui/dma.cpp
================================================
#include
#include
#include
#include "iris.hpp"
#include "res/IconsMaterialSymbols.h"
namespace iris {
void show_ee_dmac(iris::instance* iris) {
using namespace ImGui;
if (imgui::BeginEx("EE DMAC", &iris->show_ee_dmac)) {
} End();
}
void show_iop_dma(iris::instance* iris) {
using namespace ImGui;
if (imgui::BeginEx("IOP DMA", &iris->show_iop_dma)) {
} End();
}
}
================================================
FILE: frontend/ui/gs.cpp
================================================
#include
#include
#include
#include "iris.hpp"
#include "res/IconsMaterialSymbols.h"
namespace iris {
static const char* gs_internal_reg_names[] = {
"prim",
"rgbaq",
"st",
"uv",
"xyzf2",
"xyz2",
"fog",
"xyzf3",
"xyz3",
"prmodecont",
"prmode",
"texclut",
"scanmsk",
"texa",
"fogcol",
"texflush",
"dimx",
"dthe",
"colclamp",
"pabe",
"bitbltbuf",
"trxpos",
"trxreg",
"trxdir",
"hwreg",
"signal",
"finish",
"label"
};
static const char* gs_gp_reg_names[] = {
"frame",
"zbuf",
"tex0",
"tex1",
"tex2",
"miptbp1",
"miptbp2",
"clamp",
"test",
"alpha",
"xyoffset",
"scissor",
"fba"
};
static const char* gs_privileged_reg_names[] = {
"pmode",
"smode1",
"smode2",
"srfsh",
"synch1",
"synch2",
"syncv",
"dispfb1",
"display1",
"dispfb2",
"display2",
"extbuf",
"extdata",
"extwrite",
"bgcolor",
"csr",
"imr",
"busdir",
"siglblid"
};
static const char* gs_debug_names[] = {
"Registers",
"Buffers",
"Textures",
"Queue",
"Memory"
};
static const char* sizing_combo_items[] = {
ICON_MS_FIT_WIDTH " Fixed fit",
ICON_MS_FULLSCREEN " Fixed same",
ICON_MS_FULLSCREEN " Stretch prop",
ICON_MS_FULLSCREEN " Stretch same"
};
static ImGuiTableFlags table_sizing_flags[] = {
ImGuiTableFlags_SizingFixedFit,
ImGuiTableFlags_SizingFixedSame,
ImGuiTableFlags_SizingStretchProp,
ImGuiTableFlags_SizingStretchSame
};
static int gs_table_sizing = 3;
static int gs_sizing_index = 0;
static int gs_debug_index = 0;
static const char* gs_context_names[] = {
"Privileged",
"Context 1",
"Context 2",
"Internal"
};
static int gs_context = 0;
void show_privileged_registers(iris::instance* iris) {
using namespace ImGui;
struct ps2_gs* gs = iris->ps2->gs;
PushFont(iris->font_code);
uint64_t* regs = &gs->pmode;
if (BeginTable("table6", 2, ImGuiTableFlags_RowBg)) {
PushFont(iris->font_small_code);
TableSetupColumn("Register");
TableSetupColumn("Value");
TableHeadersRow();
PopFont();
for (int i = 0; i < 19; i++) {
TableNextRow();
TableSetColumnIndex(0);
Text("%s", gs_privileged_reg_names[i]);
TableSetColumnIndex(1);
Text("%08lx%08lx", regs[i] >> 32, regs[i] & 0xffffffff);
}
EndTable();
}
PopFont();
}
void show_context_registers(iris::instance* iris, int ctx) {
using namespace ImGui;
struct ps2_gs* gs = iris->ps2->gs;
PushFont(iris->font_code);
uint64_t* regs = &gs->context[ctx].frame;
if (BeginTable("table9", 2, ImGuiTableFlags_RowBg)) {
PushFont(iris->font_small_code);
TableSetupColumn("Register");
TableSetupColumn("Value");
TableHeadersRow();
PopFont();
for (int i = 0; i < 13; i++) {
TableNextRow();
TableSetColumnIndex(0);
Text("%s", gs_gp_reg_names[i]);
TableSetColumnIndex(1);
Text("%08lx%08lx", regs[i] >> 32, regs[i] & 0xffffffff);
}
EndTable();
}
PopFont();
}
void show_internal_registers(iris::instance* iris) {
using namespace ImGui;
struct ps2_gs* gs = iris->ps2->gs;
PushFont(iris->font_code);
uint64_t* regs = &gs->prim;
if (BeginTable("table6", 2, ImGuiTableFlags_RowBg)) {
PushFont(iris->font_small_code);
TableSetupColumn("Register");
TableSetupColumn("Value");
TableHeadersRow();
PopFont();
for (int i = 0; i < 28; i++) {
TableNextRow();
TableSetColumnIndex(0);
Text("%s", gs_internal_reg_names[i]);
TableSetColumnIndex(1);
Text("%08lx%08lx", regs[i] >> 32, regs[i] & 0xffffffff);
}
EndTable();
}
PopFont();
}
static inline void show_gs_vertex_xy(iris::instance* iris, const gs_vertex* vtx) {
using namespace ImGui;
TableSetColumnIndex(0);
Text("Position (XY)");
TableSetColumnIndex(1);
PushFont(iris->font_code);
Text("0x%04lx, 0x%04lx", vtx->xyz & 0xffff, (vtx->xyz >> 16) & 0xffff);
TableSetColumnIndex(2);
Text("%d.%04ld, %d.%04ld", vtx->x, (vtx->xyz & 0xf) * 625, vtx->y, ((vtx->xyz >> 16) & 0xf) * 625);
PopFont();
TableNextRow();
}
static inline void show_gs_vertex_z(iris::instance* iris, const gs_vertex* vtx) {
using namespace ImGui;
TableSetColumnIndex(0);
Text("Depth (Z)");
TableSetColumnIndex(1);
PushFont(iris->font_code);
Text("0x%08x", vtx->z);
TableSetColumnIndex(2);
Text("%d", vtx->z);
PopFont();
TableNextRow();
}
static inline void show_gs_vertex_stq(iris::instance* iris, const gs_vertex* vtx) {
using namespace ImGui;
TableSetColumnIndex(0);
Text("STQ");
TableSetColumnIndex(1);
PushFont(iris->font_code);
Text("0x%08lx, 0x%08lx, 0x%08lx", vtx->st & 0xffffffff, vtx->st >> 32, vtx->rgbaq >> 32);
TableSetColumnIndex(2);
Text("%f, %f, %f", vtx->s, vtx->t, vtx->q);
PopFont();
TableNextRow();
}
static inline void show_gs_vertex_uv(iris::instance* iris, const gs_vertex* vtx) {
using namespace ImGui;
TableSetColumnIndex(0);
Text("UV");
TableSetColumnIndex(1);
PushFont(iris->font_code);
Text("0x%08lx, 0x%08lx",
vtx->uv & 0x3fff,
(vtx->uv >> 16) & 0x3fff
);
TableSetColumnIndex(2);
Text("%d.%04ld, %d.%04ld",
vtx->u, (vtx->uv & 0xf) * 625,
vtx->v, ((vtx->uv >> 16) & 0xf) * 625
);
PopFont();
TableNextRow();
}
static inline void show_gs_vertex_rgba(iris::instance* iris, const gs_vertex* vtx) {
using namespace ImGui;
TableSetColumnIndex(0);
Text("Color (RGBA)");
TableSetColumnIndex(1);
PushFont(iris->font_code);
Text("0x%08lx", vtx->rgbaq & 0xffffffff);
TableSetColumnIndex(2);
Text("0x%02lx, 0x%02lx, 0x%02lx, 0x%02lx",
vtx->rgbaq & 0xff,
(vtx->rgbaq >> 8) & 0xff,
(vtx->rgbaq >> 16) & 0xff,
(vtx->rgbaq >> 24) & 0xff
); SameLine();
ImVec4 color = ImVec4(
(float)(vtx->rgbaq & 0xff) / 255.0f,
(float)((vtx->rgbaq >> 8) & 0xff) / 255.0f,
(float)((vtx->rgbaq >> 16) & 0xff) / 255.0f,
(float)((vtx->rgbaq >> 24) & 0xff) / 255.0f
);
ColorEdit4("MyColor##3", (float*)&color,
ImGuiColorEditFlags_NoInputs |
ImGuiColorEditFlags_NoLabel |
ImGuiColorEditFlags_NoPicker |
ImGuiColorEditFlags_AlphaPreviewHalf
);
PopFont();
TableNextRow();
}
void show_gs_vertex(iris::instance* iris, const gs_vertex* vtx) {
using namespace ImGui;
if (BeginTable("table10", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit)) {
PushFont(iris->font_small_code);
TableSetupColumn("Attribute");
TableSetupColumn("Raw");
TableSetupColumn("Value");
TableHeadersRow();
PopFont();
TableNextRow();
show_gs_vertex_xy(iris, vtx);
show_gs_vertex_z(iris, vtx);
show_gs_vertex_stq(iris, vtx);
show_gs_vertex_uv(iris, vtx);
show_gs_vertex_rgba(iris, vtx);
EndTable();
}
}
void show_gs_queue(iris::instance* iris) {
using namespace ImGui;
struct ps2_gs* gs = iris->ps2->gs;
for (unsigned int i = 0; i < gs->vqi; i++) {
char buf[32]; sprintf(buf, "Vertex %d", i+1);
if (TreeNode(buf)) {
show_gs_vertex(iris, &gs->vq[i]);
TreePop();
}
}
if (TreeNode("Uninitialized")) {
for (int i = gs->vqi; i < 4; i++) {
char buf[32]; sprintf(buf, "Vertex %d", i+1);
if (TreeNode(buf)) {
show_gs_vertex(iris, &gs->vq[i]);
TreePop();
}
}
TreePop();
}
}
void show_gs_registers(iris::instance* iris) {
using namespace ImGui;
if (BeginTable("table6", 4, ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_BordersInnerV)) {
TableNextRow();
for (int i = 0; i < 4; i++) {
TableSetColumnIndex(i);
if (Selectable(gs_context_names[i], gs_context == i)) {
gs_context = i;
}
}
EndTable();
}
if (BeginChild("##gsr")) {
switch (gs_context) {
case 0: {
show_privileged_registers(iris);
} break;
case 1: {
show_context_registers(iris, 0);
} break;
case 2: {
show_context_registers(iris, 1);
} break;
case 3: {
show_internal_registers(iris);
} break;
}
} EndChild();
}
static uint32_t addr = 0, width = 0, height = 0;
static bool unswizzle = false;
static float scale = 1.0f;
const char* format_names[] = {
"32-bit/24-bit",
"16-bit"
};
int format = 0;
static SDL_GPUTexture* tex = nullptr;
void show_gs_memory(iris::instance* iris) {
using namespace ImGui;
struct ps2_gs* gs = iris->ps2->gs;
static char buf0[16];
static char buf1[16];
static char buf2[16];
static char buf3[16];
if (InputText("Address##", buf0, 9, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue)) {
if (buf0[0]) {
addr = strtoul(buf0, NULL, 16);
}
}
if (InputText("Width##", buf1, 9, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue)) {
if (buf1[0]) {
width = strtoul(buf1, NULL, 0);
}
}
if (InputText("Height##", buf2, 9, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue)) {
if (buf2[0]) {
height = strtoul(buf2, NULL, 0);
}
}
sprintf(buf3, "%.1f", scale);
if (BeginCombo("Scale", buf3, ImGuiComboFlags_HeightSmall)) {
char buf4[16];
for (int i = 0; i < 6; i++) {
sprintf(buf4, "%.1f", 1.0f + (0.5f * i));
if (Selectable(buf4, scale == 1.0f + (0.5f * i))) {
scale = 1.0f + (0.5f * i);
}
}
EndCombo();
}
if (BeginCombo("Format", format_names[format], ImGuiComboFlags_HeightSmall)) {
for (int i = 0; i < 2; i++) {
if (Selectable(format_names[i], i == format)) {
format = i;
}
}
EndCombo();
}
SDL_GPUTextureFormat fmt;
int stride = 0;
switch (format) {
case 0: {
fmt = SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM;
stride = 4;
} break;
case 1: {
fmt = SDL_GPU_TEXTUREFORMAT_B5G5R5A1_UNORM;
stride = 2;
} break;
}
// To-do: GS memory viewer
// if (Button("View")) {
// addr = strtoul(buf0, NULL, 16);
// width = strtoul(buf1, NULL, 0);
// height = strtoul(buf2, NULL, 0);
// if (tex) {
// SDL_ReleaseGPUTexture(iris->device, tex);
// }
// SDL_GPUTextureCreateInfo tci = {};
// tci.format = fmt;
// tci.type = SDL_GPU_TEXTURETYPE_2D;
// tci.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER;
// tci.width = width;
// tci.height = height;
// tci.layer_count_or_depth = 1;
// tci.num_levels = 1;
// tci.sample_count = SDL_GPU_SAMPLECOUNT_1;
// tex = SDL_CreateGPUTexture(iris->device, &tci);
// SDL_GPUSamplerCreateInfo sci = {
// .min_filter = SDL_GPU_FILTER_NEAREST,
// .mag_filter = SDL_GPU_FILTER_NEAREST,
// .mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_NEAREST,
// .address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE,
// .address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE,
// .address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE,
// };
// }
// if (!tex)
// return;
// SDL_GPUCommandBuffer* cb = SDL_AcquireGPUCommandBuffer(iris->device);
// SDL_GPUCopyPass* cp = SDL_BeginGPUCopyPass(cb);
// SDL_GPUTransferBufferCreateInfo ttbci = {
// .usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD,
// .size = (width * stride) * height
// };
// SDL_GPUTransferBuffer* ttb = SDL_CreateGPUTransferBuffer(iris->device, &ttbci);
// // fill the transfer buffer
// switch (format) {
// case 0: {
// uint32_t* ptr = (uint32_t*)(((uint8_t*)gs->vram) + addr);
// uint32_t* data = (uint32_t*)SDL_MapGPUTransferBuffer(iris->device, ttb, false);
// for (int y = 0; y < height; y++) {
// for (int x = 0; x < width; x++) {
// data[x + (y * width)] = ptr[x + (y * width)] | 0xff000000;
// }
// }
// } break;
// case 1: {
// uint16_t* ptr = (uint16_t*)(((uint8_t*)gs->vram) + addr);
// uint16_t* data = (uint16_t*)SDL_MapGPUTransferBuffer(iris->device, ttb, false);
// for (int y = 0; y < height; y++) {
// for (int x = 0; x < width; x++) {
// data[x + (y * width)] = ptr[x + (y * width)] | 0x8000;
// }
// }
// } break;
// }
// // SDL_memcpy(data, ptr, (width * stride) * height);
// SDL_UnmapGPUTransferBuffer(iris->device, ttb);
// SDL_GPUTextureTransferInfo tti = {
// .transfer_buffer = ttb,
// .offset = 0,
// };
// SDL_GPUTextureRegion tr = {
// .texture = tex,
// .w = width,
// .h = height,
// .d = 1,
// };
// SDL_UploadToGPUTexture(cp, &tti, &tr, false);
// // end the copy pass
// SDL_EndGPUCopyPass(cp);
// SDL_SubmitGPUCommandBuffer(cb);
// ImageWithBg((ImTextureID)(intptr_t)tex, ImVec2(width*scale, height*scale), ImVec2(0, 0), ImVec2(1, 1), ImVec4(0, 0, 0, 1), ImVec4(1, 1, 1, 1));
}
void show_gs_debugger(iris::instance* iris) {
using namespace ImGui;
if (imgui::BeginEx("GS", &iris->show_gs_debugger, ImGuiWindowFlags_MenuBar)) {
if (BeginMenuBar()) {
if (BeginMenu("Settings")) {
if (BeginMenu(ICON_MS_CROP " Sizing")) {
for (int i = 0; i < 4; i++) {
if (Selectable(sizing_combo_items[i], i == gs_sizing_index)) {
gs_table_sizing = table_sizing_flags[i];
gs_sizing_index = i;
}
}
ImGui::EndMenu();
}
ImGui::EndMenu();
}
EndMenuBar();
}
if (BeginTable("table5", 5, ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_BordersInnerV)) {
TableNextRow();
for (int i = 0; i < 5; i++) {
TableSetColumnIndex(i);
if (Selectable(gs_debug_names[i], gs_debug_index == i)) {
gs_debug_index = i;
}
}
EndTable();
}
if (BeginChild("##gschild")) {
switch (gs_debug_index) {
case 0: {
show_gs_registers(iris);
} break;
case 3: {
show_gs_queue(iris);
} break;
case 4: {
show_gs_memory(iris);
} break;
}
} EndChild();
} End();
}
}
================================================
FILE: frontend/ui/intc.cpp
================================================
#include
#include
#include
#include "iris.hpp"
#include "res/IconsMaterialSymbols.h"
namespace iris {
const char* ee_irq_sources[] = {
"GS",
"SBUS",
"Vblank In",
"Vblank Out",
"VIF0",
"VIF1",
"VU0",
"VU1",
"IPU",
"Timer 0",
"Timer 1",
"Timer 2",
"Timer 3",
"SFIFO",
"VU0 Watchdog"
};
const char* iop_irq_sources[] = {
"Vblank In",
"GPU",
"CDVD",
"DMA",
"Timer 0",
"Timer 1",
"Timer 2",
"SIO0",
"SIO1",
"SPU2",
"PIO",
"Vblank Out",
"DVD",
"PCMCIA",
"Timer 3",
"Timer 4",
"Timer 5",
"SIO2",
"HTR0",
"HTR1",
"HTR2",
"HTR3",
"USB",
"EXTR",
"FWRE",
"FDMA"
};
void show_ee_intc_interrupts(iris::instance* iris) {
using namespace ImGui;
struct ps2_intc* intc = iris->ps2->ee_intc;
if (BeginTable("##eeintc", 3, ImGuiTableFlags_RowBg)) {
PushFont(iris->font_small_code);
TableSetupColumn("Source");
TableSetupColumn("Status");
TableSetupColumn("Mask");
TableHeadersRow();
PopFont();
for (int i = 0; i < 15; i++) {
TableNextRow();
TableSetColumnIndex(0);
Text("%s", ee_irq_sources[i]);
TableSetColumnIndex(1);
int status = intc->stat & (1 << i);
int mask = intc->mask & (1 << i);
char label[16];
sprintf(label, "%s##s%x", status ? ICON_MS_CHECK : "", i);
if (Selectable(label)) {
intc->stat ^= 1 << i;
}
TableSetColumnIndex(2);
sprintf(label, "%s##m%x", mask ? ICON_MS_CHECK : "", i);
if (Selectable(label)) {
intc->mask ^= 1 << i;
}
}
EndTable();
}
}
void show_ee_interrupts(iris::instance* iris) {
using namespace ImGui;
struct ps2_intc* intc = iris->ps2->ee_intc;
if (imgui::BeginEx("EE Interrupts", &iris->show_ee_interrupts)) {
if (Button(ICON_MS_REMOVE_SELECTION)) {
intc->mask = 0;
} SameLine();
if (IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_DelayNormal)) {
SetTooltip("Disable all");
}
if (Button(ICON_MS_SELECT)) {
intc->mask |= 0xffff;
}
if (IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_DelayNormal)) {
SetTooltip("Enable all");
}
if (BeginChild("##eeintcchild")) {
show_ee_intc_interrupts(iris);
} EndChild();
} End();
}
void show_iop_intc_interrupts(iris::instance* iris) {
using namespace ImGui;
struct ps2_iop_intc* intc = iris->ps2->iop_intc;
if (BeginTable("##iopintc", 3, ImGuiTableFlags_RowBg)) {
PushFont(iris->font_small_code);
TableSetupColumn("Source");
TableSetupColumn("Status");
TableSetupColumn("Mask");
TableHeadersRow();
PopFont();
for (int i = 0; i < 26; i++) {
TableNextRow();
TableSetColumnIndex(0);
Text("%s", iop_irq_sources[i]);
TableSetColumnIndex(1);
int status = intc->stat & (1 << i);
int mask = intc->mask & (1 << i);
char label[16];
sprintf(label, "%s##s%x", status ? ICON_MS_CHECK : "", i);
if (Selectable(label)) {
intc->stat ^= 1 << i;
}
TableSetColumnIndex(2);
sprintf(label, "%s##m%x", mask ? ICON_MS_CHECK : "", i);
if (Selectable(label)) {
intc->mask ^= 1 << i;
}
}
EndTable();
}
}
void show_iop_interrupts(iris::instance* iris) {
using namespace ImGui;
struct ps2_iop_intc* intc = iris->ps2->iop_intc;
if (imgui::BeginEx("IOP Interrupts", &iris->show_iop_interrupts)) {
if (Button(ICON_MS_REMOVE_SELECTION)) {
intc->mask = 0;
} SameLine();
if (IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_DelayNormal)) {
SetTooltip("Disable all");
}
if (Button(ICON_MS_SELECT)) {
intc->mask |= 0xffff;
}
if (IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_DelayNormal)) {
SetTooltip("Enable all");
}
if (BeginChild("##iopintcchild")) {
show_iop_intc_interrupts(iris);
} EndChild();
} End();
}
}
================================================
FILE: frontend/ui/logs.cpp
================================================
#include
#include
#include
#include "iris.hpp"
#include "res/IconsMaterialSymbols.h"
namespace iris {
bool ee_follow = true;
bool iop_follow = true;
bool sysmem_follow = true;
void show_logs(iris::instance* iris, const std::vector & logs, bool follow) {
using namespace ImGui;
PushFont(iris->font_code);
if (BeginTable("##logstable", 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit)) {
for (unsigned int i = 0; i < logs.size(); i++) {
TableNextRow();
TableSetColumnIndex(0);
Text(" %-3d ", i+1);
TableSetColumnIndex(1);
char buf[16]; sprintf(buf, "##l%d", i);
if (Selectable(buf, false, ImGuiSelectableFlags_AllowOverlap | ImGuiSelectableFlags_SpanAllColumns)) {
// Do something with text
} SameLine(0.0, 0.0);
Text("%s", logs[i].c_str());
}
if (follow) {
SetScrollHereY(1.0f);
}
EndTable();
}
PopFont();
}
void show_ee_logs(iris::instance* iris) {
using namespace ImGui;
if (imgui::BeginEx("EE logs", &iris->show_ee_logs)) {
if (Button(ICON_MS_DELETE)) {
iris->ee_log.clear();
} SameLine();
if (Button(ICON_MS_CONTENT_COPY)) {
std::string buf;
for (const std::string& s : iris->ee_log) {
buf.append(s);
buf.push_back('\n');
}
SDL_SetClipboardText(buf.c_str());
}
if (BeginChild("##eelog")) {
show_logs(iris, iris->ee_log, ee_follow);
} EndChild();
} End();
}
void show_iop_logs(iris::instance* iris) {
using namespace ImGui;
if (imgui::BeginEx("IOP logs", &iris->show_iop_logs, ImGuiWindowFlags_MenuBar)) {
if (BeginMenuBar()) {
if (BeginMenu("Settings")) {
if (MenuItem(iop_follow ? ICON_MS_CHECK_BOX " Follow" : ICON_MS_CHECK_BOX_OUTLINE_BLANK " Follow", nullptr)) {
iop_follow = !iop_follow;
}
ImGui::EndMenu();
}
EndMenuBar();
}
if (Button(ICON_MS_DELETE)) {
iris->iop_log.clear();
} SameLine();
if (Button(ICON_MS_CONTENT_COPY)) {
std::string buf;
for (const std::string& s : iris->iop_log) {
buf.append(s);
buf.push_back('\n');
}
SDL_SetClipboardText(buf.c_str());
}
if (BeginChild("##ioplog")) {
show_logs(iris, iris->iop_log, iop_follow);
} EndChild();
} End();
}
void show_sysmem_logs(iris::instance* iris) {
using namespace ImGui;
if (imgui::BeginEx("SYSMEM logs", &iris->show_sysmem_logs, ImGuiWindowFlags_MenuBar)) {
if (BeginMenuBar()) {
if (BeginMenu("Settings")) {
if (MenuItem(sysmem_follow ? ICON_MS_CHECK_BOX " Follow" : ICON_MS_CHECK_BOX_OUTLINE_BLANK " Follow", nullptr)) {
sysmem_follow = !sysmem_follow;
}
ImGui::EndMenu();
}
EndMenuBar();
}
if (Button(ICON_MS_DELETE)) {
iris->sysmem_log.clear();
} SameLine();
if (Button(ICON_MS_CONTENT_COPY)) {
std::string buf;
for (const std::string& s : iris->sysmem_log) {
buf.append(s);
buf.push_back('\n');
}
SDL_SetClipboardText(buf.c_str());
}
if (BeginChild("##sysmemlog")) {
show_logs(iris, iris->sysmem_log, sysmem_follow);
} EndChild();
} End();
}
}
================================================
FILE: frontend/ui/memory.cpp
================================================
#include
#include
#include
#include "iris.hpp"
#include "ee/ee_def.hpp"
#include "ee/vu_def.hpp"
#include "res/IconsMaterialSymbols.h"
#include "memory_viewer.h"
namespace iris {
static MemoryEditor editor;
void show_memory_viewer(iris::instance* iris) {
using namespace ImGui;
editor.FontOptions = iris->font_body;
struct ps2_state* ps2 = iris->ps2;
if (imgui::BeginEx("Memory", &iris->show_memory_viewer)) {
if (BeginTabBar("##tabbar")) {
if (BeginTabItem("EE")) {
PushFont(iris->font_code);
editor.DrawContents(ps2->ee_ram->buf, ps2->ee_ram->size, 0);
PopFont();
EndTabItem();
}
if (BeginTabItem("EE SPR")) {
PushFont(iris->font_code);
editor.DrawContents(ps2->ee->spr->buf, 0x4000, 0);
PopFont();
EndTabItem();
}
if (BeginTabItem("IOP")) {
PushFont(iris->font_code);
editor.DrawContents(ps2->iop_ram->buf, ps2->iop_ram->size, 0);
PopFont();
EndTabItem();
}
if (BeginTabItem("IOP SPR")) {
PushFont(iris->font_code);
editor.DrawContents(ps2->iop_spr->buf, RAM_SIZE_1KB, 0);
PopFont();
EndTabItem();
}
if (BeginTabItem("VRAM")) {
PushFont(iris->font_code);
editor.DrawContents(ps2->gs->vram, 0x400000, 0);
PopFont();
EndTabItem();
}
if (BeginTabItem("SPU2")) {
PushFont(iris->font_code);
editor.DrawContents(ps2->spu2->ram, RAM_SIZE_2MB, 0);
PopFont();
EndTabItem();
}
if (BeginTabItem("VU0 IMEM")) {
PushFont(iris->font_code);
editor.DrawContents(ps2->vu0->micro_mem, 0x1000, 0);
PopFont();
EndTabItem();
}
if (BeginTabItem("VU0 DMEM")) {
PushFont(iris->font_code);
editor.DrawContents(ps2->vu0->vu_mem, 0x1000, 0);
PopFont();
EndTabItem();
}
if (BeginTabItem("VU1 IMEM")) {
PushFont(iris->font_code);
editor.DrawContents(ps2->vu1->micro_mem, 0x4000, 0);
PopFont();
EndTabItem();
}
if (BeginTabItem("VU1 DMEM")) {
PushFont(iris->font_code);
editor.DrawContents(ps2->vu1->vu_mem, 0x4000, 0);
PopFont();
EndTabItem();
}
if (ps2->s14x_sram) {
if (BeginTabItem("S14X SRAM")) {
PushFont(iris->font_code);
editor.DrawContents(ps2->s14x_sram->buf, 0x8000, 0);
PopFont();
EndTabItem();
}
}
if (ps2->s14x_link) {
if (BeginTabItem("CircLink RAM")) {
PushFont(iris->font_code);
editor.DrawContents(ps2->s14x_link->ram, 1024, 0);
PopFont();
EndTabItem();
}
}
EndTabBar();
}
} End();
}
}
================================================
FILE: frontend/ui/memory_card_tool.cpp
================================================
#include
#include
#include
#include
#include "iris.hpp"
#include "res/IconsMaterialSymbols.h"
#include "portable-file-dialogs.h"
namespace iris {
enum : int {
MEMCARD_TYPE_PS1,
MEMCARD_TYPE_PS2,
MEMCARD_TYPE_POCKETSTATION
};
static const char* const type_names[] = {
"PS1 Memory Card",
"PS2 Memory Card",
"PocketStation"
};
int size = 0;
int type = MEMCARD_TYPE_PS2;
int slot = 0;
const char* fpath = nullptr;
void show_memory_card_tool(iris::instance* iris) {
using namespace ImGui;
SetNextWindowSizeConstraints(ImVec2(350, 320), ImVec2(FLT_MAX, FLT_MAX));
if (imgui::BeginEx("Create memory card", &iris->show_memory_card_tool, ImGuiWindowFlags_NoCollapse)) {
Text("Type");
if (BeginCombo("##type", type_names[type])) {
for (int i = 0; i < 3; i++) {
if (Selectable(type_names[i], i == type)) {
type = i;
}
}
EndCombo();
}
Text("Size");
if (type == MEMCARD_TYPE_PS2) {
char buf[16]; sprintf(buf, "%d MB", 8 << size);
if (BeginCombo("##size", buf)) {
for (int i = 0; i < 5; i++) {
sprintf(buf, "%d MB", 8 << i);
if (Selectable(buf, i == size)) {
size = i;
}
}
EndCombo();
}
} else {
BeginDisabled(true);
BeginCombo("##size", "128 KiB");
EndDisabled();
}
Text("Attach to");
if (BeginCombo("##slot", slot == -1 ? "None" : slot == 0 ? "Slot 1" : "Slot 2")) {
if (Selectable("None", slot == -1)) {
slot = -1;
}
if (Selectable("Slot 1", slot == 0)) {
slot = 0;
}
if (Selectable("Slot 2", slot == 1)) {
slot = 1;
}
EndCombo();
}
if (Button("Create")) {
// Create memory card file
int size_in_bytes;
if (type == MEMCARD_TYPE_PS2) {
// Calculate data + ECC area (nsects*512 + nsects*16)
size_in_bytes = 0x840000 << size;
} else {
size_in_bytes = 128 * 1024;
}
audio::mute(iris);
std::string default_path = iris->pref_path + "image.mcd";
if (type == MEMCARD_TYPE_POCKETSTATION) {
default_path = iris->pref_path + "image.psm";
}
auto f = pfd::save_file("Save Memory Card image", default_path, {
"Iris Memory Card Image (*.mcd)", "*.mcd",
"PCSX2 Memory Card Image (*.ps2)", "*.ps2",
"PocketStation Image (*.psm; *.pocket)", "*.psm *.pocket",
"All Files (*.*)", "*"
});
while (!f.ready());
audio::unmute(iris);
if (f.result().size()) {
FILE* file = fopen(f.result().c_str(), "wb");
const int shift = 4;
void* buf = malloc(size_in_bytes >> shift);
memset(buf, 0xff, size_in_bytes >> shift);
fseek(file, 0, SEEK_SET);
for (int i = 0; i < 1 << shift; i++) {
fwrite(buf, size_in_bytes >> shift, 1, file);
}
fclose(file);
char msg[1024];
sprintf(msg, "Created memory card image: \"%s\"", f.result().c_str());
push_info(iris, std::string(msg));
free(buf);
if (slot != -1) {
if (iris->mcd_slot_type[slot]) {
fpath = f.result().c_str();
OpenPopup("Confirm detach");
} else {
// Attach memory card to slot
if (emu::attach_memory_card(iris, slot, f.result().c_str())) {
push_info(iris, "Memory card attached successfully.");
if (slot == 0) {
iris->mcd0_path = f.result();
} else {
iris->mcd1_path = f.result();
}
} else {
push_info(iris, "Failed to attach memory card.");
}
}
}
}
}
if (fpath && imgui::BeginEx("Confirm detach", NULL, ImGuiWindowFlags_AlwaysAutoResize)) {
Text("A memory card is already attached to this slot. Do you want to detach it?");
if (Button("Yes")) {
if (emu::attach_memory_card(iris, slot, fpath)) {
if (slot == 0) {
iris->mcd0_path = std::string(fpath);
} else {
iris->mcd1_path = std::string(fpath);
}
push_info(iris, "Memory card attached successfully.");
} else {
push_info(iris, "Failed to attach memory card.");
}
fpath = nullptr;
}
SameLine();
if (Button("No")) {
fpath = nullptr;
}
End();
}
} End();
}
}
================================================
FILE: frontend/ui/memory_search.cpp
================================================
#include
#include
#include
#include
#include
#include "iris.hpp"
#include "ee/ee_def.hpp"
#include "ee/vu_def.hpp"
#include "res/IconsMaterialSymbols.h"
#include "portable-file-dialogs.h"
namespace iris {
enum {
SEARCH_CMP_EQUAL,
SEARCH_CMP_NOT_EQUAL,
SEARCH_CMP_LESS_THAN,
SEARCH_CMP_GREATER_THAN,
SEARCH_CMP_LESS_EQUAL,
SEARCH_CMP_GREATER_EQUAL
};
const char* search_cmp_names[] = {
"Equal",
"Not Equal",
"Less Than",
"Greater Than",
"Less Than or Equal",
"Greater Than or Equal"
};
enum {
SEARCH_CPU_EE,
SEARCH_CPU_IOP
};
const char* search_cpu_names[] = {
"EE",
"IOP"
};
enum {
SEARCH_TYPE_U8,
SEARCH_TYPE_U16,
SEARCH_TYPE_U32,
SEARCH_TYPE_U64,
SEARCH_TYPE_S8,
SEARCH_TYPE_S16,
SEARCH_TYPE_S32,
SEARCH_TYPE_S64,
SEARCH_TYPE_F32,
SEARCH_TYPE_F64
};
const char* search_type_names[] = {
"Uint8",
"Uint16",
"Uint32",
"Uint64",
"Sint8",
"Sint16",
"Sint32",
"Sint64",
"Float32",
"Float64"
};
union value {
uint8_t u8[8];
uint16_t u16[4];
uint32_t u32[2];
uint64_t u64;
int8_t s8[8];
int16_t s16[4];
int32_t s32[2];
int64_t s64;
float f32[2];
double f64;
};
struct match {
uint32_t address;
value prev_value, curr_value;
std::string description;
int cpu;
int type;
};
std::vector search_matches;
std::vector address_list;
int search_type = SEARCH_TYPE_U32;
int search_cmp = SEARCH_CMP_EQUAL;
int search_cpu = SEARCH_CPU_EE;
bool display_hex = false;
bool search_aligned = true;
template bool compare_values(int cmp, T a, T b) {
switch (cmp) {
case SEARCH_CMP_EQUAL:
return a == b;
case SEARCH_CMP_NOT_EQUAL:
return a != b;
case SEARCH_CMP_LESS_THAN:
return a < b;
case SEARCH_CMP_GREATER_THAN:
return a > b;
case SEARCH_CMP_LESS_EQUAL:
return a <= b;
case SEARCH_CMP_GREATER_EQUAL:
return a >= b;
}
return false;
}
void search_memory(struct ps2_state* ps2, int cpu, int type, int cmp, const char* value_str, bool aligned) {
search_matches.clear();
struct ps2_ram* mem = cpu == SEARCH_CPU_EE ? ps2->ee_ram : ps2->iop_ram;
int size = 0;
switch (type) {
case SEARCH_TYPE_U8:
case SEARCH_TYPE_S8:
size = 1;
break;
case SEARCH_TYPE_U16:
case SEARCH_TYPE_S16:
size = 2;
break;
case SEARCH_TYPE_U32:
case SEARCH_TYPE_S32:
case SEARCH_TYPE_F32:
size = 4;
break;
case SEARCH_TYPE_U64:
case SEARCH_TYPE_S64:
case SEARCH_TYPE_F64:
size = 8;
break;
}
for (int addr = 0; addr < mem->size - size; addr += (aligned ? size : 1)) {
match m;
m.address = addr;
m.curr_value.u64 = *(uint64_t*)&mem->buf[addr];
m.prev_value = m.curr_value;
m.cpu = cpu;
m.type = type;
switch (type) {
case SEARCH_TYPE_U8: {
uint8_t val = (uint8_t)std::strtoul(value_str, nullptr, 0);
if (compare_values(cmp, m.curr_value.u8[0], val)) {
search_matches.push_back(m);
}
} break;
case SEARCH_TYPE_U16: {
uint16_t val = (uint16_t)std::strtoul(value_str, nullptr, 0);
if (compare_values(cmp, m.curr_value.u16[0], val)) {
search_matches.push_back(m);
}
} break;
case SEARCH_TYPE_U32: {
uint32_t val = (uint32_t)std::strtoul(value_str, nullptr, 0);
if (compare_values(cmp, m.curr_value.u32[0], val)) {
search_matches.push_back(m);
}
} break;
case SEARCH_TYPE_U64: {
uint64_t val = (uint64_t)std::strtoull(value_str, nullptr, 0);
if (compare_values(cmp, m.curr_value.u64, val)) {
search_matches.push_back(m);
}
} break;
case SEARCH_TYPE_S8: {
int8_t val = (int8_t)std::strtol(value_str, nullptr, 0);
if (compare_values(cmp, m.curr_value.s8[0], val)) {
search_matches.push_back(m);
}
} break;
case SEARCH_TYPE_S16: {
int16_t val = (int16_t)std::strtol(value_str, nullptr, 0);
if (compare_values(cmp, m.curr_value.s16[0], val)) {
search_matches.push_back(m);
}
} break;
case SEARCH_TYPE_S32: {
int32_t val = (int32_t)std::strtol(value_str, nullptr, 0);
if (compare_values(cmp, m.curr_value.s32[0], val)) {
search_matches.push_back(m);
}
} break;
case SEARCH_TYPE_S64: {
int64_t val = (int64_t)std::strtoll(value_str, nullptr, 0);
if (compare_values(cmp, m.curr_value.s64, val)) {
search_matches.push_back(m);
}
} break;
case SEARCH_TYPE_F32: {
float val = std::strtof(value_str, nullptr);
if (compare_values(cmp, m.curr_value.f32[0], val)) {
search_matches.push_back(m);
}
} break;
case SEARCH_TYPE_F64: {
double val = std::strtod(value_str, nullptr);
if (compare_values(cmp, m.curr_value.f64, val)) {
search_matches.push_back(m);
}
} break;
}
}
}
void filter_results(int type, int cmp, const char* value_str) {
// TODO
switch (type) {
case SEARCH_TYPE_U8: {
uint8_t val = (uint8_t)std::strtoul(value_str, nullptr, 0);
std::erase_if(search_matches, [cmp, val](const match& m) {
return !compare_values(cmp, m.curr_value.u8[0], val);
});
} break;
case SEARCH_TYPE_U16: {
uint16_t val = (uint16_t)std::strtoul(value_str, nullptr, 0);
std::erase_if(search_matches, [cmp, val](const match& m) {
return !compare_values(cmp, m.curr_value.u16[0], val);
});
} break;
case SEARCH_TYPE_U32: {
uint32_t val = (uint32_t)std::strtoul(value_str, nullptr, 0);
std::erase_if(search_matches, [cmp, val](const match& m) {
return !compare_values(cmp, m.curr_value.u32[0], val);
});
} break;
case SEARCH_TYPE_U64: {
uint64_t val = (uint64_t)std::strtoull(value_str, nullptr, 0);
std::erase_if(search_matches, [cmp, val](const match& m) {
return !compare_values(cmp, m.curr_value.u64, val);
});
} break;
case SEARCH_TYPE_S8: {
int8_t val = (int8_t)std::strtol(value_str, nullptr, 0);
std::erase_if(search_matches, [cmp, val](const match& m) {
return !compare_values(cmp, m.curr_value.s8[0], val);
});
} break;
case SEARCH_TYPE_S16: {
int16_t val = (int16_t)std::strtol(value_str, nullptr, 0);
std::erase_if(search_matches, [cmp, val](const match& m) {
return !compare_values(cmp, m.curr_value.s16[0], val);
});
} break;
case SEARCH_TYPE_S32: {
int32_t val = (int32_t)std::strtol(value_str, nullptr, 0);
std::erase_if(search_matches, [cmp, val](const match& m) {
return !compare_values(cmp, m.curr_value.s32[0], val);
});
} break;
case SEARCH_TYPE_S64: {
int64_t val = (int64_t)std::strtoll(value_str, nullptr, 0);
std::erase_if(search_matches, [cmp, val](const match& m) {
return !compare_values(cmp, m.curr_value.s64, val);
});
} break;
case SEARCH_TYPE_F32: {
float val = std::strtof(value_str, nullptr);
std::erase_if(search_matches, [cmp, val](const match& m) {
return !compare_values(cmp, m.curr_value.f32[0], val);
});
} break;
case SEARCH_TYPE_F64: {
double val = std::strtod(value_str, nullptr);
std::erase_if(search_matches, [cmp, val](const match& m) {
return !compare_values(cmp, m.curr_value.f64, val);
});
} break;
}
}
void write_match_value(struct ps2_state* ps2, int cpu, match& m, int type) {
struct ps2_ram* mem = cpu == SEARCH_CPU_EE ? ps2->ee_ram : ps2->iop_ram;
switch (type) {
case SEARCH_TYPE_U8:
case SEARCH_TYPE_S8: {
*(uint8_t*)&mem->buf[m.address] = m.curr_value.u8[0];
} break;
case SEARCH_TYPE_U16:
case SEARCH_TYPE_S16: {
*(uint16_t*)&mem->buf[m.address] = m.curr_value.u16[0];
} break;
case SEARCH_TYPE_U32:
case SEARCH_TYPE_F32:
case SEARCH_TYPE_S32: {
*(uint32_t*)&mem->buf[m.address] = m.curr_value.u32[0];
} break;
case SEARCH_TYPE_U64:
case SEARCH_TYPE_F64:
case SEARCH_TYPE_S64: {
*(uint64_t*)&mem->buf[m.address] = m.curr_value.u64;
} break;
}
}
void sprintf_match(const value& v, char* buf, size_t size, int type, int hex) {
switch (type) {
case SEARCH_TYPE_U8:
snprintf(buf, size, hex ? "0x%02x" : "%u", v.u8[0]);
break;
case SEARCH_TYPE_U16:
snprintf(buf, size, hex ? "0x%04x" : "%u", v.u16[0]);
break;
case SEARCH_TYPE_U32:
snprintf(buf, size, hex ? "0x%08x" : "%u", v.u32[0]);
break;
case SEARCH_TYPE_U64:
snprintf(buf, size, hex ? "0x%016llx" : "%llu", v.u64);
break;
case SEARCH_TYPE_S8:
snprintf(buf, size, "%d", v.s8[0]);
break;
case SEARCH_TYPE_S16:
snprintf(buf, size, "%d", v.s16[0]);
break;
case SEARCH_TYPE_S32:
snprintf(buf, size, "%d", v.s32[0]);
break;
case SEARCH_TYPE_S64:
snprintf(buf, size, "%lld", v.s64);
break;
case SEARCH_TYPE_F32:
snprintf(buf, size, "%f", v.f32[0]);
break;
case SEARCH_TYPE_F64:
snprintf(buf, size, "%lf", v.f64);
break;
}
}
void show_match_change_dialog(iris::instance* iris, match& m, char* label, int search_type, int search_cpu) {
using namespace ImGui;
struct ps2_state* ps2 = iris->ps2;
static char new_value[32];
PushFont(iris->font_small);
TextDisabled("Edit "); SameLine(0.0, 0.0);
PushFont(iris->font_small_code);
Text("%s", label);
PopFont();
PopFont();
PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0, 2.0));
PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0, 8.0));
PushFont(iris->font_body);
PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.35, 0.35, 0.35, 0.35));
AlignTextToFramePadding();
Text(ICON_MS_EDIT); SameLine();
SetNextItemWidth(100);
PushFont(iris->font_code);
if (InputText("##", new_value, 32, ImGuiInputTextFlags_EnterReturnsTrue)) {
if (new_value[0]) {
if (strchr(new_value, '.')) {
if (search_type == SEARCH_TYPE_F64) {
m.curr_value.f64 = std::strtod(new_value, nullptr);
} else {
m.curr_value.f32[0] = std::strtof(new_value, nullptr);
}
} else {
m.curr_value.u64 = std::strtoull(new_value, nullptr, 0);
}
write_match_value(ps2, search_cpu, const_cast(m), search_type);
}
CloseCurrentPopup();
}
PopFont();
if (Button("Change")) {
if (new_value[0]) {
if (strchr(new_value, '.')) {
if (search_type == SEARCH_TYPE_F64) {
m.curr_value.f64 = std::strtod(new_value, nullptr);
} else {
m.curr_value.f32[0] = std::strtof(new_value, nullptr);
}
} else {
m.curr_value.u64 = std::strtoull(new_value, nullptr, 0);
}
write_match_value(ps2, search_cpu, const_cast(m), search_type);
}
CloseCurrentPopup();
} SameLine();
if (Button("Cancel"))
CloseCurrentPopup();
PopStyleColor();
PopStyleVar(2);
PopFont();
}
void show_description_change_dialog(iris::instance* iris, match& m) {
using namespace ImGui;
struct ps2_state* ps2 = iris->ps2;
static char new_value[32];
PushFont(iris->font_small);
TextDisabled("Edit description");
PopFont();
PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0, 2.0));
PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0, 8.0));
PushFont(iris->font_body);
PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.35, 0.35, 0.35, 0.35));
AlignTextToFramePadding();
Text(ICON_MS_EDIT); SameLine();
SetNextItemWidth(100);
static char desc_buf[512];
if (desc_buf[0] == '\0') {
strncpy(desc_buf, m.description.c_str(), sizeof(desc_buf));
}
desc_buf[sizeof(desc_buf) - 1] = '\0';
if (InputText("##desc", desc_buf, sizeof(desc_buf), ImGuiInputTextFlags_EnterReturnsTrue)) {
m.description = "\"" + std::string(desc_buf) + "\"";
CloseCurrentPopup();
}
if (Button("Change")) {
m.description = "\"" + std::string(desc_buf) + "\"";
CloseCurrentPopup();
} SameLine();
if (Button("Cancel"))
CloseCurrentPopup();
PopStyleColor();
PopStyleVar(2);
PopFont();
}
int frame = 0;
void show_search_table(iris::instance* iris, struct ps2_state* ps2, int type, int cpu) {
using namespace ImGui;
static uint32_t selected_address = 0;
if (search_matches.empty()) {
SeparatorText("Search results");
} else {
char buf[256];
if (search_matches.size() > 9999) {
snprintf(buf, sizeof(buf), "Search results (9999+ matches)");
} else {
snprintf(buf, sizeof(buf), "Search results (%zu matches)", search_matches.size());
}
SeparatorText(buf);
}
if (BeginTable("Matches", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY | ImGuiTableFlags_SizingStretchProp)) {
TableSetupColumn("Address");
TableSetupColumn("Previous Value");
TableSetupColumn("Current Value");
TableHeadersRow();
for (match& m : search_matches) {
TableNextRow();
TableSetColumnIndex(0);
char addr_label[16];
char prev_value_label[64];
char curr_value_label[64];
sprintf(addr_label, "0x%08x", m.address);
sprintf_match(m.prev_value, prev_value_label, sizeof(prev_value_label), type, display_hex);
sprintf_match(m.curr_value, curr_value_label, sizeof(curr_value_label), type, display_hex);
PushFont(iris->font_code);
Selectable(addr_label, false, ImGuiSelectableFlags_AllowOverlap | ImGuiSelectableFlags_SpanAllColumns);
if (IsItemClicked(ImGuiMouseButton_Right)) {
OpenPopup("context_menu");
selected_address = m.address;
}
if (IsItemHovered() && IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
OpenPopup("edit_value_popup");
selected_address = m.address;
}
if (selected_address == m.address) if (BeginPopup("context_menu")) {
PushFont(iris->font_small_code);
TextDisabled("0x%08x", m.address);
PopFont();
PushFont(iris->font_body);
if (BeginMenu(ICON_MS_CONTENT_COPY " Copy")) {
if (Selectable("Address")) {
ImGui::SetClipboardText(addr_label);
}
if (Selectable("Previous Value")) {
ImGui::SetClipboardText(prev_value_label);
}
if (Selectable("Current Value")) {
ImGui::SetClipboardText(curr_value_label);
}
ImGui::EndMenu();
}
if (BeginMenu(ICON_MS_EDIT " Edit value")) {
show_match_change_dialog(iris, m, addr_label, type, cpu);
ImGui::EndMenu();
}
bool in_address_list = false;
for (const match& am : address_list) {
if (am.address == m.address) {
in_address_list = true;
break;
}
}
if (Selectable(in_address_list ? ICON_MS_REMOVE " Remove from address list" : ICON_MS_ADD " Add to address list")) {
if (in_address_list) {
std::erase_if(address_list, [m](const match& am) {
return am.address == m.address;
});
} else {
m.description = "\"No description\"";
m.cpu = cpu;
m.type = type;
address_list.push_back(m);
}
}
PopFont();
EndPopup();
}
if (selected_address == m.address) if (BeginPopup("edit_value_popup")) {
show_match_change_dialog(iris, m, addr_label, type, cpu);
EndPopup();
}
TableSetColumnIndex(1);
Text("%s", prev_value_label);
TableSetColumnIndex(2);
Text("%s", curr_value_label);
PopFont();
}
EndTable();
}
}
void show_address_list(iris::instance* iris) {
using namespace ImGui;
struct ps2_state* ps2 = iris->ps2;
static uint32_t selected_address = 0;
if (BeginTable("Addresses", 6, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY | ImGuiTableFlags_Resizable)) {
TableSetupColumn("Address", ImGuiTableColumnFlags_WidthFixed, 100.0f);
TableSetupColumn("CPU", ImGuiTableColumnFlags_WidthFixed, 20.0f);
TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed, 30.0f);
TableSetupColumn("Description");
TableSetupColumn("Previous Value", ImGuiTableColumnFlags_WidthFixed, 150.0f);
TableSetupColumn("Current Value");
TableHeadersRow();
for (match& m : address_list) {
TableNextRow();
TableSetColumnIndex(0);
char addr_label[16];
char prev_value_label[64];
char curr_value_label[64];
sprintf(addr_label, "0x%08x", m.address);
sprintf_match(m.prev_value, prev_value_label, sizeof(prev_value_label), m.type, display_hex);
sprintf_match(m.curr_value, curr_value_label, sizeof(curr_value_label), m.type, display_hex);
PushFont(iris->font_code);
Selectable(addr_label, false, ImGuiSelectableFlags_AllowOverlap | ImGuiSelectableFlags_SpanAllColumns);
if (IsItemClicked(ImGuiMouseButton_Right)) {
OpenPopup("context_menu_al");
selected_address = m.address;
}
if (IsItemHovered() && IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
OpenPopup("edit_value_popup_al");
selected_address = m.address;
}
if (selected_address == m.address) if (BeginPopup("context_menu_al")) {
PushFont(iris->font_small_code);
TextDisabled("0x%08x", m.address);
PopFont();
PushFont(iris->font_body);
if (BeginMenu(ICON_MS_CONTENT_COPY " Copy")) {
if (Selectable("Address")) {
ImGui::SetClipboardText(addr_label);
}
if (Selectable("Previous Value")) {
ImGui::SetClipboardText(prev_value_label);
}
if (Selectable("Current Value")) {
ImGui::SetClipboardText(curr_value_label);
}
ImGui::EndMenu();
}
if (BeginMenu(ICON_MS_EDIT " Edit value")) {
show_match_change_dialog(iris, m, addr_label, m.type, m.cpu);
ImGui::EndMenu();
}
if (BeginMenu(ICON_MS_EDIT " Edit description")) {
show_description_change_dialog(iris, m);
ImGui::EndMenu();
}
if (BeginMenu(ICON_MS_EDIT " Edit type")) {
for (int i = 0; i < IM_ARRAYSIZE(search_type_names); i++) {
if (MenuItem(search_type_names[i], nullptr, m.type == i)) {
m.type = i;
}
}
ImGui::EndMenu();
}
if (Selectable(ICON_MS_REMOVE " Remove from address list")) {
std::erase_if(address_list, [m](const match& am) {
return am.address == m.address;
});
}
PopFont();
EndPopup();
}
if (selected_address == m.address) if (BeginPopup("edit_value_popup_al")) {
show_match_change_dialog(iris, m, addr_label, m.type, m.cpu);
EndPopup();
}
TableSetColumnIndex(1);
Text("%s", m.cpu == SEARCH_CPU_EE ? "EE" : "IOP");
TableSetColumnIndex(2);
Text("%s", search_type_names[m.type]);
TableSetColumnIndex(3);
PushFont(iris->font_body);
Text("%s", m.description.c_str());
PopFont();
TableSetColumnIndex(4);
Text("%s", prev_value_label);
TableSetColumnIndex(5);
Text("%s", curr_value_label);
PopFont();
}
}
EndTable();
}
void show_search_options(iris::instance* iris) {
using namespace ImGui;
struct ps2_state* ps2 = iris->ps2;
SeparatorText("Search options");
PushItemWidth(-FLT_MIN);
Text("Type");
if (BeginCombo("##type", search_type_names[search_type])) {
for (int i = 0; i < IM_ARRAYSIZE(search_type_names); i++) {
if (Selectable(search_type_names[i], search_type == i)) {
search_type = i;
}
}
EndCombo();
}
PushStyleVarY(ImGuiStyleVar_FramePadding, 2.0f);
Checkbox("Aligned", &search_aligned);
PopStyleVar();
Text("Comparison");
if (BeginCombo("##comparison", search_cmp_names[search_cmp])) {
for (int i = 0; i < IM_ARRAYSIZE(search_cmp_names); i++) {
if (Selectable(search_cmp_names[i], search_cmp == i)) {
search_cmp = i;
}
}
EndCombo();
}
Text("Memory");
if (BeginCombo("##memory", search_cpu_names[search_cpu])) {
for (int i = 0; i < IM_ARRAYSIZE(search_cpu_names); i++) {
if (Selectable(search_cpu_names[i], search_cpu == i)) {
search_cpu = i;
}
}
EndCombo();
}
static char buf[64];
Text("Value");
if (InputText("##value", buf, sizeof(buf), ImGuiInputTextFlags_EnterReturnsTrue)) {
search_memory(ps2, search_cpu, search_type, search_cmp, buf, search_aligned);
}
PopItemWidth();
BeginDisabled(buf[0] == '\0');
if (Button("Search")) {
search_memory(ps2, search_cpu, search_type, search_cmp, buf, search_aligned);
} SameLine();
EndDisabled();
BeginDisabled(buf[0] == '\0' || search_matches.empty());
if (Button("Filter")) {
filter_results(search_type, search_cmp, buf);
}
EndDisabled();
}
void update_search_matches(struct ps2_state* ps2, int cpu) {
if (frame != 4) {
frame++;
return;
}
for (match& m : search_matches) {
struct ps2_ram* mem = m.cpu == SEARCH_CPU_EE ? ps2->ee_ram : ps2->iop_ram;
m.curr_value.u64 = *(uint64_t*)&mem->buf[m.address];
}
for (match& m : address_list) {
struct ps2_ram* mem = m.cpu == SEARCH_CPU_EE ? ps2->ee_ram : ps2->iop_ram;
m.curr_value.u64 = *(uint64_t*)&mem->buf[m.address];
}
frame = 0;
}
std::string serialize_address_list() {
std::stringstream ss;
for (const match& m : address_list) {
ss << "0x" << std::hex << m.address << "," << m.description << "," << m.cpu << "," << m.type << "\n";
}
return ss.str();
}
void import_address_list_from_stream(std::istream& stream) {
address_list.clear();
std::string line;
while (std::getline(stream, line)) {
std::stringstream linestream(line);
std::string address_str;
std::string description;
std::string cpu_str;
std::string type_str;
bool valid = true;
valid = std::getline(linestream, address_str, ',') &&
std::getline(linestream, description, ',') &&
std::getline(linestream, cpu_str, ',') &&
std::getline(linestream, type_str);
if (valid) {
match m;
m.address = (uint32_t)std::strtoul(address_str.c_str(), nullptr, 0);
m.description = description;
m.prev_value.u64 = 0;
m.curr_value.u64 = 0;
m.cpu = std::strtoul(cpu_str.c_str(), nullptr, 0);
m.type = std::strtoul(type_str.c_str(), nullptr, 0);
// To-do: Remove double quotes from description if present
address_list.push_back(m);
}
}
}
void show_memory_search(iris::instance* iris) {
using namespace ImGui;
struct ps2_state* ps2 = iris->ps2;
update_search_matches(ps2, search_cpu);
SetNextWindowSizeConstraints(ImVec2(600, 560), ImVec2(FLT_MAX, FLT_MAX));
int top_shelf_height = GetContentRegionAvail().y - 220;
if (imgui::BeginEx("Memory search", &iris->show_memory_search, ImGuiWindowFlags_MenuBar)) {
if (BeginMenuBar()) {
if (BeginMenu("File")) {
if (BeginMenu("Address list")) {
if (MenuItem("Export to file...")) {
std::string str = serialize_address_list();
pfd::save_file file("Export Address List", "address_list.csv", { "CSV Files (*.csv)", "*.csv", "All files (*.*)", "" });
if (!file.result().empty()) {
FILE* f = fopen(file.result().c_str(), "w");
if (f) {
fwrite(str.c_str(), 1, str.size(), f);
fclose(f);
} else {
push_info(iris, "Failed to open file for writing address list");
}
}
}
if (MenuItem("Import from file...")) {
pfd::open_file file("Import Address List", "", { "CSV Files (*.csv)", "*.csv", "All Files (*.*)", "*" });
if (!file.result().empty()) {
std::ifstream f(file.result().at(0));
if (f.is_open()) {
import_address_list_from_stream(f);
} else {
push_info(iris, "Failed to open file for reading address list");
}
}
}
if (MenuItem("Export to clipboard")) {
std::string str = serialize_address_list();
SDL_SetClipboardText(str.c_str());
}
if (MenuItem("Import from clipboard")) {
if (SDL_HasClipboardText()) {
std::string clip_text = SDL_GetClipboardText();
std::istringstream iss(clip_text);
import_address_list_from_stream(iss);
}
}
if (MenuItem("Clear")) {
address_list.clear();
}
ImGui::EndMenu();
}
ImGui::EndMenu();
}
if (BeginMenu("View")) {
MenuItem("Display as hex", nullptr, &display_hex);
ImGui::EndMenu();
}
EndMenuBar();
}
if (BeginChild("##search_table", ImVec2(GetContentRegionAvail().x - 225, GetContentRegionAvail().y - 220))) {
show_search_table(iris, ps2, search_type, search_cpu);
} EndChild(); SameLine();
if (BeginChild("##search_options", ImVec2(0, GetContentRegionAvail().y - 220))) {
show_search_options(iris);
} EndChild();
SeparatorText("Address list");
if (BeginChild("##address_list")) {
show_address_list(iris);
} EndChild();
} End();
}
}
================================================
FILE: frontend/ui/memory_viewer.h
================================================
// Mini memory editor for Dear ImGui (to embed in your game/tools)
// Get latest version at http://www.github.com/ocornut/imgui_club
// Licensed under The MIT License (MIT)
// Right-click anywhere to access the Options menu!
// You can adjust the keyboard repeat delay/rate in ImGuiIO.
// The code assume a mono-space font for simplicity!
// If you don't use the default font, use ImGui::PushFont()/PopFont() to switch to a mono-space font before calling this.
//
// Usage:
// // Create a window and draw memory editor inside it:
// static MemoryEditor mem_edit_1;
// static char data[0x10000];
// size_t data_size = 0x10000;
// mem_edit_1.DrawWindow("Memory Editor", data, data_size);
//
// Usage:
// // If you already have a window, use DrawContents() instead:
// static MemoryEditor mem_edit_2;
// ImGui::Begin("MyWindow")
// mem_edit_2.DrawContents(this, sizeof(*this), (size_t)this);
// ImGui::End();
//
// Changelog:
// - v0.10: initial version
// - v0.23 (2017/08/17): added to github. fixed right-arrow triggering a byte write.
// - v0.24 (2018/06/02): changed DragInt("Rows" to use a %d data format (which is desirable since imgui 1.61).
// - v0.25 (2018/07/11): fixed wording: all occurrences of "Rows" renamed to "Columns".
// - v0.26 (2018/08/02): fixed clicking on hex region
// - v0.30 (2018/08/02): added data preview for common data types
// - v0.31 (2018/10/10): added OptUpperCaseHex option to select lower/upper casing display [@samhocevar]
// - v0.32 (2018/10/10): changed signatures to use void* instead of unsigned char*
// - v0.33 (2018/10/10): added OptShowOptions option to hide all the interactive option setting.
// - v0.34 (2019/05/07): binary preview now applies endianness setting [@nicolasnoble]
// - v0.35 (2020/01/29): using ImGuiDataType available since Dear ImGui 1.69.
// - v0.36 (2020/05/05): minor tweaks, minor refactor.
// - v0.40 (2020/10/04): fix misuse of ImGuiListClipper API, broke with Dear ImGui 1.79. made cursor position appears on left-side of edit box. option popup appears on mouse release. fix MSVC warnings where _CRT_SECURE_NO_WARNINGS wasn't working in recent versions.
// - v0.41 (2020/10/05): fix when using with keyboard/gamepad navigation enabled.
// - v0.42 (2020/10/14): fix for . character in ASCII view always being greyed out.
// - v0.43 (2021/03/12): added OptFooterExtraHeight to allow for custom drawing at the bottom of the editor [@leiradel]
// - v0.44 (2021/03/12): use ImGuiInputTextFlags_AlwaysOverwrite in 1.82 + fix hardcoded width.
// - v0.50 (2021/11/12): various fixes for recent dear imgui versions (fixed misuse of clipper, relying on SetKeyboardFocusHere() handling scrolling from 1.85). added default size.
// - v0.51 (2024/02/22): fix for layout change in 1.89 when using IMGUI_DISABLE_OBSOLETE_FUNCTIONS. (#34)
// - v0.52 (2024/03/08): removed unnecessary GetKeyIndex() calls, they are a no-op since 1.87.
// - v0.53 (2024/05/27): fixed right-click popup from not appearing when using DrawContents(). warning fixes. (#35)
// - v0.54 (2024/07/29): allow ReadOnly mode to still select and preview data. (#46) [@DeltaGW2])
// - v0.55 (2024/08/19): added BgColorFn to allow setting background colors independently from highlighted selection. (#27) [@StrikerX3]
// added MouseHoveredAddr public readable field. (#47, #27) [@StrikerX3]
// fixed a data preview crash with 1.91.0 WIP. fixed contiguous highlight color when using data preview.
// *BREAKING* added UserData field passed to all optional function handlers: ReadFn, WriteFn, HighlightFn, BgColorFn. (#50) [@silverweed]
// - v0.56 (2024/11/04): fixed MouseHovered, MouseHoveredAddr not being set when hovering a byte being edited. (#54)
//
// TODO:
// - This is generally old/crappy code, it should work but isn't very good.. to be rewritten some day.
// - PageUp/PageDown are not supported because we use _NoNav. This is a good test scenario for working out idioms of how to mix natural nav and our own...
// - Arrows are being sent to the InputText() about to disappear which for LeftArrow makes the text cursor appear at position 1 for one frame.
// - Using InputText() is awkward and maybe overkill here, consider implementing something custom.
#pragma once
#include // sprintf, scanf
#include // uint8_t, etc.
#if defined(_MSC_VER) || defined(_UCRT)
#define _PRISizeT "I"
#define ImSnprintf _snprintf
#else
#define _PRISizeT "z"
#define ImSnprintf snprintf
#endif
#if defined(_MSC_VER) || defined(_UCRT)
#pragma warning (push)
#pragma warning (disable: 4996) // warning C4996: 'sprintf': This function or variable may be unsafe.
#endif
struct MemoryEditor
{
enum DataFormat
{
DataFormat_Bin = 0,
DataFormat_Dec = 1,
DataFormat_Hex = 2,
DataFormat_COUNT
};
ImFont* FontOptions = nullptr;
// Settings
bool Open; // = true // set to false when DrawWindow() was closed. ignore if not using DrawWindow().
bool ReadOnly; // = false // disable any editing.
int Cols; // = 16 // number of columns to display.
bool OptShowOptions; // = true // display options button/context menu. when disabled, options will be locked unless you provide your own UI for them.
bool OptShowDataPreview; // = false // display a footer previewing the decimal/binary/hex/float representation of the currently selected bytes.
bool OptShowHexII; // = false // display values in HexII representation instead of regular hexadecimal: hide null/zero bytes, ascii values as ".X".
bool OptShowAscii; // = true // display ASCII representation on the right side.
bool OptGreyOutZeroes; // = true // display null/zero bytes using the TextDisabled color.
bool OptUpperCaseHex; // = true // display hexadecimal values as "FF" instead of "ff".
int OptMidColsCount; // = 8 // set to 0 to disable extra spacing between every mid-cols.
int OptAddrDigitsCount; // = 0 // number of addr digits to display (default calculated based on maximum displayed addr).
float OptFooterExtraHeight; // = 0 // space to reserve at the bottom of the widget to add custom widgets
ImU32 HighlightColor; // // background color of highlighted bytes.
// Function handlers
ImU8 (*ReadFn)(const ImU8* mem, size_t off, void* user_data); // = 0 // optional handler to read bytes.
void (*WriteFn)(ImU8* mem, size_t off, ImU8 d, void* user_data); // = 0 // optional handler to write bytes.
bool (*HighlightFn)(const ImU8* mem, size_t off, void* user_data); // = 0 // optional handler to return Highlight property (to support non-contiguous highlighting).
ImU32 (*BgColorFn)(const ImU8* mem, size_t off, void* user_data); // = 0 // optional handler to return custom background color of individual bytes.
void* UserData; // = NULL // user data forwarded to the function handlers
// Public read-only data
bool MouseHovered; // set when mouse is hovering a value.
size_t MouseHoveredAddr; // the address currently being hovered if MouseHovered is set.
// [Internal State]
bool ContentsWidthChanged;
size_t DataPreviewAddr;
size_t DataEditingAddr;
bool DataEditingTakeFocus;
char DataInputBuf[32];
char AddrInputBuf[32];
size_t GotoAddr;
size_t HighlightMin, HighlightMax;
int PreviewEndianness;
ImGuiDataType PreviewDataType;
MemoryEditor()
{
// Settings
Open = true;
ReadOnly = false;
Cols = 16;
OptShowOptions = true;
OptShowDataPreview = true;
OptShowHexII = false;
OptShowAscii = true;
OptGreyOutZeroes = true;
OptUpperCaseHex = true;
OptMidColsCount = 8;
OptAddrDigitsCount = 0;
OptFooterExtraHeight = 0.0f;
ReadFn = nullptr;
WriteFn = nullptr;
HighlightFn = nullptr;
BgColorFn = nullptr;
UserData = nullptr;
FontOptions = nullptr;
// State/Internals
ContentsWidthChanged = false;
DataPreviewAddr = DataEditingAddr = (size_t)-1;
DataEditingTakeFocus = false;
memset(DataInputBuf, 0, sizeof(DataInputBuf));
memset(AddrInputBuf, 0, sizeof(AddrInputBuf));
GotoAddr = (size_t)-1;
MouseHovered = false;
MouseHoveredAddr = 0;
HighlightMin = HighlightMax = (size_t)-1;
PreviewEndianness = 0;
PreviewDataType = ImGuiDataType_S32;
}
void GotoAddrAndHighlight(size_t addr_min, size_t addr_max)
{
GotoAddr = addr_min;
HighlightMin = addr_min;
HighlightMax = addr_max;
}
struct Sizes
{
int AddrDigitsCount;
float LineHeight;
float GlyphWidth;
float HexCellWidth;
float SpacingBetweenMidCols;
float PosHexStart;
float PosHexEnd;
float PosAsciiStart;
float PosAsciiEnd;
float WindowWidth;
Sizes() { memset(this, 0, sizeof(*this)); }
};
void CalcSizes(Sizes& s, size_t mem_size, size_t base_display_addr)
{
ImGuiStyle& style = ImGui::GetStyle();
s.AddrDigitsCount = OptAddrDigitsCount;
if (s.AddrDigitsCount == 0)
for (size_t n = base_display_addr + mem_size - 1; n > 0; n >>= 4)
s.AddrDigitsCount++;
s.LineHeight = ImGui::GetTextLineHeight();
s.GlyphWidth = ImGui::CalcTextSize("F").x + 1; // We assume the font is mono-space
s.HexCellWidth = (float)(int)(s.GlyphWidth * 2.5f); // "FF " we include trailing space in the width to easily catch clicks everywhere
s.SpacingBetweenMidCols = (float)(int)(s.HexCellWidth * 0.25f); // Every OptMidColsCount columns we add a bit of extra spacing
s.PosHexStart = (s.AddrDigitsCount + 2) * s.GlyphWidth;
s.PosHexEnd = s.PosHexStart + (s.HexCellWidth * Cols);
s.PosAsciiStart = s.PosAsciiEnd = s.PosHexEnd;
if (OptShowAscii)
{
s.PosAsciiStart = s.PosHexEnd + s.GlyphWidth * 1;
if (OptMidColsCount > 0)
s.PosAsciiStart += (float)((Cols + OptMidColsCount - 1) / OptMidColsCount) * s.SpacingBetweenMidCols;
s.PosAsciiEnd = s.PosAsciiStart + Cols * s.GlyphWidth;
}
s.WindowWidth = s.PosAsciiEnd + style.ScrollbarSize + style.WindowPadding.x * 2 + s.GlyphWidth;
}
// Standalone Memory Editor window
void DrawWindow(const char* title, void* mem_data, size_t mem_size, size_t base_display_addr = 0x0000)
{
Sizes s;
CalcSizes(s, mem_size, base_display_addr);
ImGui::SetNextWindowSize(ImVec2(s.WindowWidth, s.WindowWidth * 0.60f), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, 0.0f), ImVec2(s.WindowWidth, FLT_MAX));
Open = true;
if (ImGui::Begin(title, &Open, ImGuiWindowFlags_NoScrollbar))
{
DrawContents(mem_data, mem_size, base_display_addr);
if (ContentsWidthChanged)
{
CalcSizes(s, mem_size, base_display_addr);
ImGui::SetWindowSize(ImVec2(s.WindowWidth, ImGui::GetWindowSize().y));
}
}
ImGui::End();
}
// Memory Editor contents only
void DrawContents(void* mem_data_void, size_t mem_size, size_t base_display_addr = 0x0000)
{
HighlightColor = ImGui::GetColorU32(ImGuiCol_Text, 0.25f);
if (Cols < 1)
Cols = 1;
ImU8* mem_data = (ImU8*)mem_data_void;
Sizes s;
CalcSizes(s, mem_size, base_display_addr);
ImGuiStyle& style = ImGui::GetStyle();
const ImVec2 contents_pos_start = ImGui::GetCursorScreenPos();
// We begin into our scrolling region with the 'ImGuiWindowFlags_NoMove' in order to prevent click from moving the window.
// This is used as a facility since our main click detection code doesn't assign an ActiveId so the click would normally be caught as a window-move.
const float height_separator = style.ItemSpacing.y + 4.0f;
float footer_height = OptFooterExtraHeight;
if (OptShowOptions)
footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1;
if (OptShowDataPreview)
footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1 + ImGui::GetTextLineHeightWithSpacing() * 3;
ImGui::BeginChild("##scrolling", ImVec2(-FLT_MIN, -footer_height), ImGuiChildFlags_None, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
ImDrawList* draw_list = ImGui::GetWindowDrawList();
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
// We are not really using the clipper API correctly here, because we rely on visible_start_addr/visible_end_addr for our scrolling function.
const int line_total_count = (int)((mem_size + Cols - 1) / Cols);
ImGuiListClipper clipper;
clipper.Begin(line_total_count, s.LineHeight);
bool data_next = false;
if (DataEditingAddr >= mem_size)
DataEditingAddr = (size_t)-1;
if (DataPreviewAddr >= mem_size)
DataPreviewAddr = (size_t)-1;
size_t preview_data_type_size = OptShowDataPreview ? DataTypeGetSize(PreviewDataType) : 0;
size_t data_editing_addr_next = (size_t)-1;
if (DataEditingAddr != (size_t)-1)
{
// Move cursor but only apply on next frame so scrolling with be synchronized (because currently we can't change the scrolling while the window is being rendered)
if (ImGui::IsKeyPressed(ImGuiKey_UpArrow) && (ptrdiff_t)DataEditingAddr >= (ptrdiff_t)Cols) { data_editing_addr_next = DataEditingAddr - Cols; }
else if (ImGui::IsKeyPressed(ImGuiKey_DownArrow) && (ptrdiff_t)DataEditingAddr < (ptrdiff_t)mem_size - Cols){ data_editing_addr_next = DataEditingAddr + Cols; }
else if (ImGui::IsKeyPressed(ImGuiKey_LeftArrow) && (ptrdiff_t)DataEditingAddr > (ptrdiff_t)0) { data_editing_addr_next = DataEditingAddr - 1; }
else if (ImGui::IsKeyPressed(ImGuiKey_RightArrow) && (ptrdiff_t)DataEditingAddr < (ptrdiff_t)mem_size - 1) { data_editing_addr_next = DataEditingAddr + 1; }
}
// Draw vertical separator
ImVec2 window_pos = ImGui::GetWindowPos();
if (OptShowAscii)
draw_list->AddLine(ImVec2(window_pos.x + s.PosAsciiStart - s.GlyphWidth, window_pos.y), ImVec2(window_pos.x + s.PosAsciiStart - s.GlyphWidth, window_pos.y + 9999), ImGui::GetColorU32(ImGuiCol_Border));
const ImU32 color_text = ImGui::GetColorU32(ImGuiCol_Text);
const ImU32 color_disabled = OptGreyOutZeroes ? ImGui::GetColorU32(ImGuiCol_TextDisabled) : color_text;
const char* format_address = OptUpperCaseHex ? "%0*" _PRISizeT "X " : "%0*" _PRISizeT "x ";
const char* format_data = OptUpperCaseHex ? "%0*" _PRISizeT "X" : "%0*" _PRISizeT "x";
const char* format_byte = OptUpperCaseHex ? "%02X" : "%02x";
const char* format_byte_space = OptUpperCaseHex ? "%02X " : "%02x ";
MouseHovered = false;
MouseHoveredAddr = 0;
while (clipper.Step())
for (int line_i = clipper.DisplayStart; line_i < clipper.DisplayEnd; line_i++) // display only visible lines
{
size_t addr = (size_t)line_i * Cols;
ImGui::Text(format_address, s.AddrDigitsCount, base_display_addr + addr);
// Draw Hexadecimal
for (int n = 0; n < Cols && addr < mem_size; n++, addr++)
{
float byte_pos_x = s.PosHexStart + s.HexCellWidth * n;
if (OptMidColsCount > 0)
byte_pos_x += (float)(n / OptMidColsCount) * s.SpacingBetweenMidCols;
ImGui::SameLine(byte_pos_x);
// Draw highlight or custom background color
const bool is_highlight_from_user_range = (addr >= HighlightMin && addr < HighlightMax);
const bool is_highlight_from_user_func = (HighlightFn && HighlightFn(mem_data, addr, UserData));
const bool is_highlight_from_preview = (addr >= DataPreviewAddr && addr < DataPreviewAddr + preview_data_type_size);
ImU32 bg_color = 0;
bool is_next_byte_highlighted = false;
if (is_highlight_from_user_range || is_highlight_from_user_func || is_highlight_from_preview)
{
is_next_byte_highlighted = (addr + 1 < mem_size) && ((HighlightMax != (size_t)-1 && addr + 1 < HighlightMax) || (HighlightFn && HighlightFn(mem_data, addr + 1, UserData)) || (addr + 1 < DataPreviewAddr + preview_data_type_size));
bg_color = HighlightColor;
}
else if (BgColorFn != nullptr)
{
is_next_byte_highlighted = (addr + 1 < mem_size) && ((BgColorFn(mem_data, addr + 1, UserData) & IM_COL32_A_MASK) != 0);
bg_color = BgColorFn(mem_data, addr, UserData);
}
if (bg_color != 0)
{
float bg_width = s.GlyphWidth * 2;
if (is_next_byte_highlighted || (n + 1 == Cols))
{
bg_width = s.HexCellWidth;
if (OptMidColsCount > 0 && n > 0 && (n + 1) < Cols && ((n + 1) % OptMidColsCount) == 0)
bg_width += s.SpacingBetweenMidCols;
}
ImVec2 pos = ImGui::GetCursorScreenPos();
draw_list->AddRectFilled(pos, ImVec2(pos.x + bg_width, pos.y + s.LineHeight), bg_color);
}
if (DataEditingAddr == addr)
{
// Display text input on current byte
bool data_write = false;
ImGui::PushID((void*)addr);
if (DataEditingTakeFocus)
{
ImGui::SetKeyboardFocusHere(0);
ImSnprintf(AddrInputBuf, 32, format_data, s.AddrDigitsCount, base_display_addr + addr);
ImSnprintf(DataInputBuf, 32, format_byte, ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr]);
}
struct InputTextUserData
{
// FIXME: We should have a way to retrieve the text edit cursor position more easily in the API, this is rather tedious. This is such a ugly mess we may be better off not using InputText() at all here.
static int Callback(ImGuiInputTextCallbackData* data)
{
InputTextUserData* user_data = (InputTextUserData*)data->UserData;
if (!data->HasSelection())
user_data->CursorPos = data->CursorPos;
#if IMGUI_VERSION_NUM < 19102
if (data->Flags & ImGuiInputTextFlags_ReadOnly)
return 0;
#endif
if (data->SelectionStart == 0 && data->SelectionEnd == data->BufTextLen)
{
// When not editing a byte, always refresh its InputText content pulled from underlying memory data
// (this is a bit tricky, since InputText technically "owns" the master copy of the buffer we edit it in there)
data->DeleteChars(0, data->BufTextLen);
data->InsertChars(0, user_data->CurrentBufOverwrite);
data->SelectionStart = 0;
data->SelectionEnd = 2;
data->CursorPos = 0;
}
return 0;
}
char CurrentBufOverwrite[3]; // Input
int CursorPos; // Output
};
InputTextUserData input_text_user_data;
input_text_user_data.CursorPos = -1;
ImSnprintf(input_text_user_data.CurrentBufOverwrite, 3, format_byte, ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr]);
ImGuiInputTextFlags flags = ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoHorizontalScroll | ImGuiInputTextFlags_CallbackAlways;
if (ReadOnly)
flags |= ImGuiInputTextFlags_ReadOnly;
flags |= ImGuiInputTextFlags_AlwaysOverwrite; // was ImGuiInputTextFlags_AlwaysInsertMode
ImGui::SetNextItemWidth(s.GlyphWidth * 2);
if (ImGui::InputText("##data", DataInputBuf, IM_ARRAYSIZE(DataInputBuf), flags, InputTextUserData::Callback, &input_text_user_data))
data_write = data_next = true;
else if (!DataEditingTakeFocus && !ImGui::IsItemActive())
DataEditingAddr = data_editing_addr_next = (size_t)-1;
DataEditingTakeFocus = false;
if (input_text_user_data.CursorPos >= 2)
data_write = data_next = true;
if (data_editing_addr_next != (size_t)-1)
data_write = data_next = false;
unsigned int data_input_value = 0;
if (!ReadOnly && data_write && sscanf(DataInputBuf, "%X", &data_input_value) == 1)
{
if (WriteFn)
WriteFn(mem_data, addr, (ImU8)data_input_value, UserData);
else
mem_data[addr] = (ImU8)data_input_value;
}
if (ImGui::IsItemHovered())
{
MouseHovered = true;
MouseHoveredAddr = addr;
}
ImGui::PopID();
}
else
{
// NB: The trailing space is not visible but ensure there's no gap that the mouse cannot click on.
ImU8 b = ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr];
if (OptShowHexII)
{
if ((b >= 32 && b < 128))
ImGui::Text(".%c ", b);
else if (b == 0xFF && OptGreyOutZeroes)
ImGui::TextDisabled("## ");
else if (b == 0x00)
ImGui::Text(" ");
else
ImGui::Text(format_byte_space, b);
}
else
{
if (b == 0 && OptGreyOutZeroes)
ImGui::TextDisabled("00 ");
else
ImGui::Text(format_byte_space, b);
}
if (ImGui::IsItemHovered())
{
MouseHovered = true;
MouseHoveredAddr = addr;
if (ImGui::IsMouseClicked(0))
{
DataEditingTakeFocus = true;
data_editing_addr_next = addr;
}
}
}
}
if (OptShowAscii)
{
// Draw ASCII values
ImGui::SameLine(s.PosAsciiStart);
ImVec2 pos = ImGui::GetCursorScreenPos();
addr = (size_t)line_i * Cols;
const float mouse_off_x = ImGui::GetIO().MousePos.x - pos.x;
const size_t mouse_addr = (mouse_off_x >= 0.0f && mouse_off_x < s.PosAsciiEnd - s.PosAsciiStart) ? addr + (size_t)(mouse_off_x / s.GlyphWidth) : (size_t)-1;
ImGui::PushID(line_i);
if (ImGui::InvisibleButton("ascii", ImVec2(s.PosAsciiEnd - s.PosAsciiStart, s.LineHeight)))
{
DataEditingAddr = DataPreviewAddr = mouse_addr;
DataEditingTakeFocus = true;
}
if (ImGui::IsItemHovered())
{
MouseHovered = true;
MouseHoveredAddr = mouse_addr;
}
ImGui::PopID();
for (int n = 0; n < Cols && addr < mem_size; n++, addr++)
{
if (addr == DataEditingAddr)
{
draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_FrameBg));
draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_TextSelectedBg));
}
else if (BgColorFn)
{
draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), BgColorFn(mem_data, addr, UserData));
}
unsigned char c = ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr];
char display_c = (c < 32 || c >= 128) ? '.' : c;
draw_list->AddText(pos, (display_c == c) ? color_text : color_disabled, &display_c, &display_c + 1);
pos.x += s.GlyphWidth;
}
}
}
ImGui::PopStyleVar(2);
const float child_width = ImGui::GetWindowSize().x;
ImGui::EndChild();
// Notify the main window of our ideal child content size (FIXME: we are missing an API to get the contents size from the child)
ImGui::SetCursorPosX(s.WindowWidth);
ImGui::Dummy(ImVec2(0.0f, 0.0f));
if (data_next && DataEditingAddr + 1 < mem_size)
{
DataEditingAddr = DataPreviewAddr = DataEditingAddr + 1;
DataEditingTakeFocus = true;
}
else if (data_editing_addr_next != (size_t)-1)
{
DataEditingAddr = DataPreviewAddr = data_editing_addr_next;
DataEditingTakeFocus = true;
}
const bool lock_show_data_preview = OptShowDataPreview;
if (FontOptions) ImGui::PushFont(FontOptions);
if (OptShowOptions)
{
ImGui::Separator();
DrawOptionsLine(s, mem_data, mem_size, base_display_addr);
}
if (lock_show_data_preview)
{
ImGui::Separator();
DrawPreviewLine(s, mem_data, mem_size, base_display_addr);
}
if (FontOptions) ImGui::PopFont();
const ImVec2 contents_pos_end(contents_pos_start.x + child_width, ImGui::GetCursorScreenPos().y);
//ImGui::GetForegroundDrawList()->AddRect(contents_pos_start, contents_pos_end, IM_COL32(255, 0, 0, 255));
if (OptShowOptions)
if (ImGui::IsMouseHoveringRect(contents_pos_start, contents_pos_end))
if (ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows) && ImGui::IsMouseReleased(ImGuiMouseButton_Right))
ImGui::OpenPopup("OptionsPopup");
if (ImGui::BeginPopup("OptionsPopup"))
{
if (FontOptions) ImGui::PushFont(FontOptions);
ImGui::SetNextItemWidth(s.GlyphWidth * 7 + style.FramePadding.x * 2.0f);
if (ImGui::DragInt("##cols", &Cols, 0.2f, 4, 32, "%d cols")) { ContentsWidthChanged = true; if (Cols < 1) Cols = 1; }
ImGui::PushStyleVarY(ImGuiStyleVar_FramePadding, 2.0f);
ImGui::Checkbox("Show Data Preview", &OptShowDataPreview);
ImGui::Checkbox("Show HexII", &OptShowHexII);
if (ImGui::Checkbox("Show Ascii", &OptShowAscii)) { ContentsWidthChanged = true; }
ImGui::Checkbox("Grey out zeroes", &OptGreyOutZeroes);
ImGui::Checkbox("Uppercase Hex", &OptUpperCaseHex);
ImGui::PopStyleVar();
ImGui::PopFont();
ImGui::EndPopup();
}
}
void DrawOptionsLine(const Sizes& s, void* mem_data, size_t mem_size, size_t base_display_addr)
{
IM_UNUSED(mem_data);
ImGuiStyle& style = ImGui::GetStyle();
const char* format_range = OptUpperCaseHex ? "%0*" _PRISizeT "X..%0*" _PRISizeT "X" : "%0*" _PRISizeT "x..%0*" _PRISizeT "x";
// Options menu
if (ImGui::Button("Options"))
ImGui::OpenPopup("OptionsPopup");
ImGui::SameLine();
ImGui::Text("Range "); ImGui::SameLine(0.0, 0.0);
if (FontOptions) ImGui::PopFont();
ImGui::Text(format_range, s.AddrDigitsCount, base_display_addr, s.AddrDigitsCount, base_display_addr + mem_size - 1);
ImGui::SameLine();
ImGui::SetNextItemWidth((s.AddrDigitsCount + 1) * s.GlyphWidth + style.FramePadding.x * 2.0f);
if (ImGui::InputText("##addr", AddrInputBuf, IM_ARRAYSIZE(AddrInputBuf), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue))
{
size_t goto_addr;
if (sscanf(AddrInputBuf, "%" _PRISizeT "X", &goto_addr) == 1)
{
GotoAddr = goto_addr - base_display_addr;
HighlightMin = HighlightMax = (size_t)-1;
}
}
if (FontOptions) ImGui::PushFont(FontOptions);
if (GotoAddr != (size_t)-1)
{
if (GotoAddr < mem_size)
{
ImGui::BeginChild("##scrolling");
ImGui::SetScrollFromPosY(ImGui::GetCursorStartPos().y + (GotoAddr / Cols) * ImGui::GetTextLineHeight());
ImGui::EndChild();
DataEditingAddr = DataPreviewAddr = GotoAddr;
DataEditingTakeFocus = true;
}
GotoAddr = (size_t)-1;
}
//if (MouseHovered)
//{
// ImGui::SameLine();
// ImGui::Text("Hovered: %p", MouseHoveredAddr);
//}
}
void DrawPreviewLine(const Sizes& s, void* mem_data_void, size_t mem_size, size_t base_display_addr)
{
IM_UNUSED(base_display_addr);
ImU8* mem_data = (ImU8*)mem_data_void;
ImGuiStyle& style = ImGui::GetStyle();
ImGui::AlignTextToFramePadding();
ImGui::Text("Preview as:");
ImGui::SameLine();
ImGui::SetNextItemWidth((s.GlyphWidth * 10.0f) + style.FramePadding.x * 2.0f + style.ItemInnerSpacing.x);
static const ImGuiDataType supported_data_types[] = { ImGuiDataType_S8, ImGuiDataType_U8, ImGuiDataType_S16, ImGuiDataType_U16, ImGuiDataType_S32, ImGuiDataType_U32, ImGuiDataType_S64, ImGuiDataType_U64, ImGuiDataType_Float, ImGuiDataType_Double };
if (ImGui::BeginCombo("##combo_type", DataTypeGetDesc(PreviewDataType), ImGuiComboFlags_HeightLargest))
{
for (int n = 0; n < IM_ARRAYSIZE(supported_data_types); n++)
{
ImGuiDataType data_type = supported_data_types[n];
if (ImGui::Selectable(DataTypeGetDesc(data_type), PreviewDataType == data_type))
PreviewDataType = data_type;
}
ImGui::EndCombo();
}
ImGui::SameLine();
ImGui::SetNextItemWidth((s.GlyphWidth * 6.0f) + style.FramePadding.x * 2.0f + style.ItemInnerSpacing.x);
ImGui::Combo("##combo_endianness", &PreviewEndianness, "LE\0BE\0\0");
char buf[128] = "";
float x = s.GlyphWidth * 6.0f;
bool has_value = DataPreviewAddr != (size_t)-1;
if (FontOptions) ImGui::PopFont();
if (has_value)
DrawPreviewData(DataPreviewAddr, mem_data, mem_size, PreviewDataType, DataFormat_Dec, buf, (size_t)IM_ARRAYSIZE(buf));
ImGui::Text("Dec"); ImGui::SameLine(x); ImGui::TextUnformatted(has_value ? buf : "N/A");
if (has_value)
DrawPreviewData(DataPreviewAddr, mem_data, mem_size, PreviewDataType, DataFormat_Hex, buf, (size_t)IM_ARRAYSIZE(buf));
ImGui::Text("Hex"); ImGui::SameLine(x); ImGui::TextUnformatted(has_value ? buf : "N/A");
if (has_value)
DrawPreviewData(DataPreviewAddr, mem_data, mem_size, PreviewDataType, DataFormat_Bin, buf, (size_t)IM_ARRAYSIZE(buf));
buf[IM_ARRAYSIZE(buf) - 1] = 0;
ImGui::Text("Bin"); ImGui::SameLine(x); ImGui::TextUnformatted(has_value ? buf : "N/A");
if (FontOptions) ImGui::PushFont(FontOptions);
}
// Utilities for Data Preview (since we don't access imgui_internal.h)
// FIXME: This technically depends on ImGuiDataType order.
const char* DataTypeGetDesc(ImGuiDataType data_type) const
{
const char* descs[] = { "Int8", "Uint8", "Int16", "Uint16", "Int32", "Uint32", "Int64", "Uint64", "Float", "Double" };
IM_ASSERT(data_type >= 0 && data_type < IM_ARRAYSIZE(descs));
return descs[data_type];
}
size_t DataTypeGetSize(ImGuiDataType data_type) const
{
const size_t sizes[] = { 1, 1, 2, 2, 4, 4, 8, 8, sizeof(float), sizeof(double) };
IM_ASSERT(data_type >= 0 && data_type < IM_ARRAYSIZE(sizes));
return sizes[data_type];
}
const char* DataFormatGetDesc(DataFormat data_format) const
{
const char* descs[] = { "Bin", "Dec", "Hex" };
IM_ASSERT(data_format >= 0 && data_format < DataFormat_COUNT);
return descs[data_format];
}
bool IsBigEndian() const
{
uint16_t x = 1;
char c[2];
memcpy(c, &x, 2);
return c[0] != 0;
}
static void* EndiannessCopyBigEndian(void* _dst, void* _src, size_t s, int is_little_endian)
{
if (is_little_endian)
{
uint8_t* dst = (uint8_t*)_dst;
uint8_t* src = (uint8_t*)_src + s - 1;
for (int i = 0, n = (int)s; i < n; ++i)
memcpy(dst++, src--, 1);
return _dst;
}
else
{
return memcpy(_dst, _src, s);
}
}
static void* EndiannessCopyLittleEndian(void* _dst, void* _src, size_t s, int is_little_endian)
{
if (is_little_endian)
{
return memcpy(_dst, _src, s);
}
else
{
uint8_t* dst = (uint8_t*)_dst;
uint8_t* src = (uint8_t*)_src + s - 1;
for (int i = 0, n = (int)s; i < n; ++i)
memcpy(dst++, src--, 1);
return _dst;
}
}
void* EndiannessCopy(void* dst, void* src, size_t size) const
{
static void* (*fp)(void*, void*, size_t, int) = nullptr;
if (fp == nullptr)
fp = IsBigEndian() ? EndiannessCopyBigEndian : EndiannessCopyLittleEndian;
return fp(dst, src, size, PreviewEndianness);
}
const char* FormatBinary(const uint8_t* buf, int width) const
{
IM_ASSERT(width <= 64);
size_t out_n = 0;
static char out_buf[64 + 8 + 1];
int n = width / 8;
for (int j = n - 1; j >= 0; --j)
{
for (int i = 0; i < 8; ++i)
out_buf[out_n++] = (buf[j] & (1 << (7 - i))) ? '1' : '0';
out_buf[out_n++] = ' ';
}
IM_ASSERT(out_n < IM_ARRAYSIZE(out_buf));
out_buf[out_n] = 0;
return out_buf;
}
// [Internal]
void DrawPreviewData(size_t addr, const ImU8* mem_data, size_t mem_size, ImGuiDataType data_type, DataFormat data_format, char* out_buf, size_t out_buf_size) const
{
uint8_t buf[8];
size_t elem_size = DataTypeGetSize(data_type);
size_t size = addr + elem_size > mem_size ? mem_size - addr : elem_size;
if (ReadFn)
for (int i = 0, n = (int)size; i < n; ++i)
buf[i] = ReadFn(mem_data, addr + i, UserData);
else
memcpy(buf, mem_data + addr, size);
if (data_format == DataFormat_Bin)
{
uint8_t binbuf[8];
EndiannessCopy(binbuf, buf, size);
ImSnprintf(out_buf, out_buf_size, "%s", FormatBinary(binbuf, (int)size * 8));
return;
}
out_buf[0] = 0;
switch (data_type)
{
case ImGuiDataType_S8:
{
int8_t data = 0;
EndiannessCopy(&data, buf, size);
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hhd", data); return; }
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%02x", data & 0xFF); return; }
break;
}
case ImGuiDataType_U8:
{
uint8_t data = 0;
EndiannessCopy(&data, buf, size);
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hhu", data); return; }
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%02x", data & 0XFF); return; }
break;
}
case ImGuiDataType_S16:
{
int16_t data = 0;
EndiannessCopy(&data, buf, size);
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hd", data); return; }
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%04x", data & 0xFFFF); return; }
break;
}
case ImGuiDataType_U16:
{
uint16_t data = 0;
EndiannessCopy(&data, buf, size);
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hu", data); return; }
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%04x", data & 0xFFFF); return; }
break;
}
case ImGuiDataType_S32:
{
int32_t data = 0;
EndiannessCopy(&data, buf, size);
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%d", data); return; }
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%08x", data); return; }
break;
}
case ImGuiDataType_U32:
{
uint32_t data = 0;
EndiannessCopy(&data, buf, size);
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%u", data); return; }
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%08x", data); return; }
break;
}
case ImGuiDataType_S64:
{
int64_t data = 0;
EndiannessCopy(&data, buf, size);
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%lld", (long long)data); return; }
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%016llx", (long long)data); return; }
break;
}
case ImGuiDataType_U64:
{
uint64_t data = 0;
EndiannessCopy(&data, buf, size);
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%llu", (long long)data); return; }
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%016llx", (long long)data); return; }
break;
}
case ImGuiDataType_Float:
{
float data = 0.0f;
EndiannessCopy(&data, buf, size);
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%f", data); return; }
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "%a", data); return; }
break;
}
case ImGuiDataType_Double:
{
double data = 0.0;
EndiannessCopy(&data, buf, size);
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%f", data); return; }
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "%a", data); return; }
break;
}
default:
case ImGuiDataType_COUNT:
break;
} // Switch
IM_ASSERT(0); // Shouldn't reach
}
};
#undef _PRISizeT
#undef ImSnprintf
#ifdef _MSC_VER
#pragma warning (pop)
#endif
================================================
FILE: frontend/ui/menubar.cpp
================================================
#include
#include "iris.hpp"
#include "res/IconsMaterialSymbols.h"
#include "portable-file-dialogs.h"
#include "ps2_elf.h"
#include "ps2_iso9660.h"
namespace iris {
const char* aspect_mode_names[] = {
"Native",
"Stretch",
"Stretch (Keep aspect ratio)",
"Force 4:3 (NTSC)",
"Force 16:9 (Widescreen)",
"Force 5:4 (PAL)",
"Auto"
};
const char* renderer_names[] = {
"Null",
"Software",
"Hardware (Vulkan)"
};
const char* fullscreen_names[] = {
"Windowed",
"Fullscreen"
};
const char* rotation_names[] = {
"0 degrees",
"90 degrees",
"180 degrees",
"270 degrees"
};
int fullscreen_flags[] = {
0,
SDL_WINDOW_FULLSCREEN
};
void show_main_menubar(iris::instance* iris) {
using namespace ImGui;
PushFont(iris->font_icons);
PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0, 7.0));
if (BeginMainMenuBar()) {
ImVec2 size = GetWindowSize();
if (BeginMenu("Iris")) {
if (MenuItem(ICON_MS_DRIVE_FILE_MOVE " Open...")) {
audio::mute(iris);
auto f = pfd::open_file("Select a file to load", "", {
"All File Types (*.iso; *.bin; *.cue; *.chd; *.cso; *.zso; *.elf)", "*.iso *.bin *.cue *.chd *.cso *.zso *.elf",
"Disc Images (*.iso; *.bin; *.cue; *.chd; *.cso; *.zso)", "*.iso *.bin *.cue *.chd *.cso *.zso",
"CD Images (*.bin; *.cue; *.chd)", "*.bin *.cue *.chd",
"DVD Images (*.iso; *.chd; *.cso; *.zso)", "*.iso *.chd *.cso *.zso",
"ISO Files (*.iso)", "*.iso",
"CUE Files (*.cue)", "*.cue",
"BIN Files (*.bin)", "*.bin",
"CHD Files (*.chd)", "*.chd",
"CSO/ZSO Files (*.cso; *.zso)", "*.cso *.zso",
"ELF Executables (*.elf)", "*.elf",
"All Files (*.*)", "*"
});
while (!f.ready());
audio::unmute(iris);
if (f.result().size()) {
std::string path = f.result().at(0);
if (path.size()) {
if (open_file(iris, path)) {
push_info(iris, "Failed to open file: " + path);
} else {
add_recent(iris, path, RECENT_TYPE_PS2);
}
}
}
}
if (BeginMenu(ICON_MS_HISTORY " Open Recent", iris->recents.size())) {
for (const auto& recent : iris->recents) {
if (MenuItem(recent.path.c_str())) {
if (recent.type == RECENT_TYPE_PS2) {
if (open_file(iris, recent.path)) {
push_info(iris, "Failed to open file: " + recent.path);
} else {
add_recent(iris, recent.path, recent.type);
}
} else {
if (!emu::load_arcade(iris, recent.path)) {
push_info(iris, "Failed to boot arcade: " + recent.path);
} else {
add_recent(iris, recent.path, RECENT_TYPE_ARCADE);
}
}
}
}
Separator();
if (MenuItem(ICON_MS_DELETE_HISTORY " Clear all recents")) {
iris->recents.clear();
}
// To-do: Use try_open_file
// if (MenuItem("Clear invalid recents")) {
// iris->recents.clear();
// }
// To-do
// if (MenuItem("Stop recents history")) {
// }
ImGui::EndMenu();
}
if (MenuItem(ICON_MS_JOYSTICK " Open Arcade...")) {
audio::mute(iris);
auto f = pfd::select_folder("Select arcade game folder", "", pfd::opt::none);
while (!f.ready());
audio::unmute(iris);
if (f.result().size()) {
std::string path = f.result();
if (path.size()) {
if (!emu::load_arcade(iris, path)) {
push_info(iris, "Failed to boot arcade: " + path);
} else {
add_recent(iris, path, RECENT_TYPE_ARCADE);
}
}
}
}
// if (MenuItem(ICON_MS_DRIVE_FILE_MOVE " Load disc...")) {
// const char* patterns[3] = { "*.iso", "*.bin", "*.cue" };
// const char* file = tinyfd_openFileDialog(
// "Select CD/DVD image",
// "",
// 3,
// patterns,
// "Disc images",
// 0
// );
// if (file) {
// struct iso9660_state* iso = iso9660_open(file);
// if (!iso) {
// printf("iris: Couldn't open disc image \"%s\"\n", file);
// exit(1);
// return;
// }
// char* boot_file = iso9660_get_boot_path(iso);
// if (!boot_file)
// return;
// // Temporarily disable window updates
// struct gs_callback cb = *ps2_gs_get_callback(iris->ps2->gs, GS_EVENT_VBLANK);
// ps2_gs_remove_callback(iris->ps2->gs, GS_EVENT_VBLANK);
// ps2_boot_file(iris->ps2, boot_file);
// // Re-enable window updates
// ps2_gs_init_callback(iris->ps2->gs, GS_EVENT_VBLANK, cb.func, cb.udata);
// ps2_cdvd_open(iris->ps2->cdvd, file);
// iso9660_close(iso);
// iris->loaded = file;
// }
// }
// if (MenuItem(ICON_MS_DRAFT " Load executable...")) {
// const char* patterns[3] = { "*.elf" };
// const char* file = tinyfd_openFileDialog(
// "Select ELF executable",
// "",
// 1,
// patterns,
// "ELF executables",
// 0
// );
// if (file) {
// std::string str(file);
// str = "host:" + str;
// // Temporarily disable window updates
// struct gs_callback cb = *ps2_gs_get_callback(iris->ps2->gs, GS_EVENT_VBLANK);
// ps2_gs_remove_callback(iris->ps2->gs, GS_EVENT_VBLANK);
// ps2_boot_file(iris->ps2, str.c_str());
// // ps2_elf_load(iris->ps2, file);
// // Re-enable window updates
// ps2_gs_init_callback(iris->ps2->gs, GS_EVENT_VBLANK, cb.func, cb.udata);
// iris->loaded = file;
// }
// }
Separator();
if (MenuItem(iris->pause ? ICON_MS_PLAY_ARROW " Run" : ICON_MS_PAUSE " Pause", "Space")) {
iris->pause = !iris->pause;
}
// To-do: Show confirm dialog maybe?
if (MenuItem(ICON_MS_REFRESH " Reset")) {
ps2_reset(iris->ps2);
}
if (MenuItem(ICON_MS_FOLDER " Change disc...")) {
audio::mute(iris);
auto f = pfd::open_file("Select CD/DVD image", "", {
"Disc Images (*.iso; *.bin; *.cue; *.chd; *.cso; *.zso)", "*.iso *.bin *.cue *.chd *.cso *.zso",
"CD Images (*.bin; *.cue; *.chd)", "*.bin *.cue *.chd",
"DVD Images (*.iso; *.chd; *.cso; *.zso)", "*.iso *.chd *.cso *.zso",
"ISO Files (*.iso)", "*.iso",
"CUE Files (*.cue)", "*.cue",
"BIN Files (*.bin)", "*.bin",
"CHD Files (*.chd)", "*.chd",
"CSO/ZSO Files (*.cso; *.zso)", "*.cso *.zso",
"All Files (*.*)", "*"
});
while (!f.ready());
audio::unmute(iris);
if (f.result().size()) {
// 2-second delay to allow the disc to spin up
if (!ps2_cdvd_open(iris->ps2->cdvd, f.result().at(0).c_str(), 38860800*2)) {
iris->loaded = f.result().at(0);
}
}
}
if (MenuItem(ICON_MS_EJECT " Eject disc")) {
iris->loaded = "";
ps2_cdvd_close(iris->ps2->cdvd);
}
ImGui::EndMenu();
}
if (BeginMenu("Settings")) {
if (BeginMenu(ICON_MS_MONITOR " Display")) {
if (BeginMenu(ICON_MS_BRUSH " Renderer")) {
for (int i = 0; i < 3; i++) {
BeginDisabled(i == RENDERER_BACKEND_SOFTWARE);
if (MenuItem(renderer_names[i], nullptr, i == iris->renderer_backend)) {
render::switch_backend(iris, i);
}
EndDisabled();
}
ImGui::EndMenu();
}
if (BeginMenu(ICON_MS_CROP " Scale")) {
for (int i = 2; i <= 6; i++) {
char buf[16]; snprintf(buf, 16, "%.1fx", (float)i * 0.5f);
if (MenuItem(buf, nullptr, ((float)i * 0.5f) == iris->scale)) {
iris->scale = (float)i * 0.5f;
// renderer_set_scale(iris->ctx, iris->scale);
}
}
ImGui::EndMenu();
}
if (BeginMenu(ICON_MS_ASPECT_RATIO " Aspect mode")) {
for (int i = 0; i < 7; i++) {
if (MenuItem(aspect_mode_names[i], nullptr, iris->aspect_mode == i)) {
iris->aspect_mode = i;
// renderer_set_aspect_mode(iris->ctx, iris->aspect_mode);
}
}
ImGui::EndMenu();
}
if (BeginMenu(ICON_MS_FILTER " Scaling filter")) {
const char* filter_names[] = {
"Nearest",
"Bilinear",
"Cubic"
};
for (int i = 0; i < 3; i++) {
BeginDisabled(i == 2 && !iris->cubic_supported);
if (MenuItem(filter_names[i], nullptr, iris->filter == i)) {
iris->filter = i;
}
EndDisabled();
}
ImGui::EndMenu();
}
if (BeginMenu(ICON_MS_SCREEN_ROTATION " Rotation")) {
const int normalized_angle = ((iris->angle % 360) + 360) % 360;
const int rotation_index = normalized_angle / 90;
for (int i = 0; i < 4; i++) {
if (MenuItem(rotation_names[i], nullptr, rotation_index == i)) {
iris->angle = i * 90;
}
}
ImGui::EndMenu();
}
if (BeginMenu(ICON_MS_ASPECT_RATIO " Window size")) {
const char* sizes[] = {
"640x480",
"800x600",
"960x720",
"1024x768",
"1280x720",
"1280x960"
};
int widths[] = {
640, 800, 960, 1024, 1280, 1280
};
int heights[] = {
480, 600, 720, 768, 720, 960
};
for (int i = 0; i < 6; i++) {
bool selected = iris->window_width == widths[i] && iris->window_height == heights[i];
if (MenuItem(sizes[i], nullptr, selected)) {
iris->window_width = widths[i];
iris->window_height = heights[i];
SDL_SetWindowSize(iris->window, iris->window_width, iris->window_height + get_menubar_height(iris));
}
}
ImGui::EndMenu();
}
if (MenuItem(ICON_MS_SPEED_2X " Integer scaling", nullptr, &iris->integer_scaling)) {
// renderer_set_integer_scaling(iris->ctx, iris->integer_scaling);
}
MenuItem(ICON_MS_FLIP " Flip horizontally", nullptr, &iris->flip_x);
MenuItem(ICON_MS_FLIP " Flip vertically", nullptr, &iris->flip_y);
if (MenuItem(ICON_MS_FULLSCREEN " Fullscreen", "F11", &iris->fullscreen)) {
SDL_SetWindowFullscreen(iris->window, iris->fullscreen);
}
if (MenuItem(ICON_MS_SYNC " VSync", nullptr, &iris->vsync)) {
imgui::set_vsync(iris, iris->vsync);
iris->swapchain_rebuild = true;
}
if (MenuItem(ICON_MS_IMAGE " Enable shaders", nullptr, &iris->enable_shaders)) {
// renderer_set_shaders_enabled(iris->ctx, iris->enable_shaders);
}
ImGui::EndMenu();
}
if (BeginMenu(ICON_MS_MUSIC_NOTE " Audio")) {
PushStyleVarY(ImGuiStyleVar_FramePadding, 0.0f);
AlignTextToFramePadding();
const char* icon = ICON_MS_VOLUME_UP;
if (iris->volume == 0.0f) {
icon = ICON_MS_VOLUME_MUTE;
} else if (iris->volume <= 0.5f) {
icon = ICON_MS_VOLUME_DOWN;
}
Text(icon); SameLine();
SetNextItemWidth(100.0f);
SliderFloat("Volume", &iris->volume, 0.0f, 1.0f, "%.1f");
PopStyleVar();
MenuItem(ICON_MS_VOLUME_OFF " Mute", nullptr, &iris->mute);
MenuItem(ICON_MS_MUSIC_OFF " Mute ADMA", nullptr, &iris->mute_adma);
ImGui::EndMenu();
}
if (MenuItem(ICON_MS_DOCK_TO_BOTTOM " Show status bar", nullptr, &iris->show_status_bar)) {
SDL_SetWindowSize(iris->window, iris->window_width, iris->window_height + get_menubar_height(iris));
}
if (MenuItem(ICON_MS_OPEN_IN_NEW " Open data folder")) {
SDL_OpenURL(iris->pref_path.c_str());
}
Separator();
if (MenuItem(ICON_MS_MANUFACTURING " Settings...")) {
iris->show_settings = true;
}
ImGui::EndMenu();
}
if (BeginMenu("Tools")) {
if (MenuItem(ICON_MS_BUILD " ImGui Demo", NULL, &iris->show_imgui_demo));
if (MenuItem(ICON_MS_SEARCH " Memory search", NULL, &iris->show_memory_search));
if (MenuItem(ICON_MS_PHOTO_CAMERA " Take screenshot...", "F9")) {
audio::mute(iris);
std::string filename = input::get_default_screenshot_filename(iris);
auto f = pfd::save_file("Save screenshot", filename, {
"PNG (*.png)", "*.png",
"JPG (*.jpg)", "*.jpg",
"BMP (*.bmp)", "*.bmp",
"TGA (*.tga)", "*.tga",
"All Files (*.*)", "*"
});
while (!f.ready());
audio::unmute(iris);
if (f.result().size()) {
input::save_screenshot(iris, f.result());
}
}
if (MenuItem(ICON_MS_SD_CARD " Memory Card tool")) {
iris->show_memory_card_tool = true;
}
ImGui::EndMenu();
}
if (BeginMenu("Debug")) {
SeparatorText("EE");
// if (BeginMenu(ICON_MS_BUG_REPORT " EE")) {
if (MenuItem(ICON_MS_SETTINGS " Control##ee", NULL, &iris->show_ee_control));
if (MenuItem(ICON_MS_EDIT_NOTE " State##ee", NULL, &iris->show_ee_state));
if (MenuItem(ICON_MS_TERMINAL " Logs##ee", NULL, &iris->show_ee_logs));
if (MenuItem(ICON_MS_BOLT " Interrupts##ee", NULL, &iris->show_ee_interrupts));
BeginDisabled(iris->symbols.empty());
if (MenuItem(ICON_MS_CODE " Symbols##ee", NULL, &iris->show_symbols));
EndDisabled();
if (MenuItem(ICON_MS_ACCOUNT_TREE " Threads##ee", NULL, &iris->show_threads));
// ImGui::EndMenu();
// }
SeparatorText("IOP");
// if (BeginMenu(ICON_MS_BUG_REPORT " IOP")) {
if (MenuItem(ICON_MS_SETTINGS " Control##iop", NULL, &iris->show_iop_control));
if (MenuItem(ICON_MS_EDIT_NOTE " State##iop", NULL, &iris->show_iop_state));
if (MenuItem(ICON_MS_TERMINAL " Logs##iop", NULL, &iris->show_iop_logs));
if (MenuItem(ICON_MS_BOLT " Interrupts##iop", NULL, &iris->show_iop_interrupts));
if (MenuItem(ICON_MS_EXTENSION " Modules##iop", NULL, &iris->show_iop_modules));
// ImGui::EndMenu();
// }
Separator();
if (MenuItem(ICON_MS_BUG_REPORT " Breakpoints", NULL, &iris->show_breakpoints));
if (MenuItem(ICON_MS_BRUSH " GS debugger", NULL, &iris->show_gs_debugger));
if (MenuItem(ICON_MS_MUSIC_NOTE " SPU2 debugger", NULL, &iris->show_spu2_debugger));
if (MenuItem(ICON_MS_MEMORY " Memory viewer", NULL, &iris->show_memory_viewer));
if (MenuItem(ICON_MS_VIEW_IN_AR " VU disassembler", NULL, &iris->show_vu_disassembler));
if (MenuItem(ICON_MS_GAMEPAD " DualShock debugger", NULL, &iris->show_pad_debugger));
if (MenuItem(ICON_MS_BUG_REPORT " Performance overlay", NULL, &iris->show_overlay));
if (MenuItem(ICON_MS_TERMINAL " SYSMEM logs", NULL, &iris->show_sysmem_logs));
Separator();
if (BeginMenu(ICON_MS_MORE_TIME " Timescale")) {
for (int i = 0; i < 9; i++) {
char buf[16]; snprintf(buf, 16, "%dx", 1 << i);
if (MenuItem(buf, nullptr, iris->timescale == (1 << i))) {
iris->timescale = (1 << i);
ps2_set_timescale(iris->ps2, iris->timescale);
}
}
ImGui::EndMenu();
}
if (MenuItem(ICON_MS_SKIP_NEXT " Skip FMVs", NULL, &iris->skip_fmv)) {
printf("Skip FMVs: %d\n", iris->skip_fmv);
ee_set_fmv_skip(iris->ps2->ee, iris->skip_fmv);
}
if (MenuItem(ICON_MS_CLOSE " Close all")) {
iris->show_ee_control = false;
iris->show_ee_state = false;
iris->show_ee_logs = false;
iris->show_ee_interrupts = false;
iris->show_ee_dmac = false;
iris->show_iop_control = false;
iris->show_iop_state = false;
iris->show_iop_logs = false;
iris->show_iop_interrupts = false;
iris->show_iop_modules = false;
iris->show_iop_dma = false;
iris->show_gs_debugger = false;
iris->show_spu2_debugger = false;
iris->show_memory_viewer = false;
iris->show_memory_search = false;
iris->show_vu_disassembler = false;
iris->show_status_bar = false;
iris->show_breakpoints = false;
iris->show_threads = false;
iris->show_sysmem_logs = false;
iris->show_imgui_demo = false;
iris->show_overlay = false;
}
ImGui::EndMenu();
}
if (BeginMenu("Help")) {
if (MenuItem(ICON_MS_INFO " About")) {
iris->show_about_window = true;
}
if (MenuItem(ICON_MS_EXCLAMATION " Report an issue")) {
SDL_OpenURL("https://github.com/allkern/iris/issues/new");
}
ImGui::EndMenu();
}
EndMainMenuBar();
}
PopStyleVar();
PopFont();
}
}
================================================
FILE: frontend/ui/modules.cpp
================================================
#include "iris.hpp"
#include "iop/hle/loadcore.h"
#include "res/IconsMaterialSymbols.h"
namespace iris {
static const char* sizing_combo_items[] = {
ICON_MS_FIT_WIDTH " Fixed fit",
ICON_MS_FULLSCREEN " Fixed same",
ICON_MS_FULLSCREEN " Stretch prop",
ICON_MS_FULLSCREEN " Stretch same"
};
static ImGuiTableFlags table_sizing_flags[] = {
ImGuiTableFlags_SizingFixedFit,
ImGuiTableFlags_SizingFixedSame,
ImGuiTableFlags_SizingStretchProp,
ImGuiTableFlags_SizingStretchSame
};
static int table_sizing_combo = 0;
static int table_sizing = ImGuiTableFlags_SizingStretchProp;
static inline void show_modules_table(iris::instance* iris) {
using namespace ImGui;
struct iop_state* iop = iris->ps2->iop;
if (BeginTable("##iopmodules", 4, ImGuiTableFlags_RowBg | table_sizing)) {
PushFont(iris->font_small_code);
TableSetupColumn("Name");
TableSetupColumn("Text Start");
TableSetupColumn("Data Start");
TableSetupColumn("BSS Start");
TableHeadersRow();
PopFont();
for (int i = 0; i < iop->module_count; i++) {
struct iop_module *mod = &iop->module_list[i];
TableNextRow();
TableSetColumnIndex(0);
Text("%s", mod->name);
// text section
TableSetColumnIndex(1);
PushFont(iris->font_code);
uint32_t addr = mod->text_addr;
Text("0x%08x", addr);
// data section
TableSetColumnIndex(2);
addr += mod->text_size;
Text("0x%08x", addr);
// bss section
TableSetColumnIndex(3);
addr += mod->data_size;
Text("0x%08x", addr);
PopFont();
}
EndTable();
}
}
static inline struct iop_module* find_iop_module(iris::instance* iris, uint32_t addr) {
struct iop_state* iop = iris->ps2->iop;
for (int i = 0; i < iop->module_count; i++) {
struct iop_module* mod = &iop->module_list[i];
if ((addr >= mod->text_addr) && (addr < (mod->text_addr + mod->text_size)))
return mod;
}
return NULL;
}
void show_iop_modules(iris::instance* iris) {
using namespace ImGui;
struct iop_state* iop = iris->ps2->iop;
if (imgui::BeginEx("IOP Modules", &iris->show_iop_modules, ImGuiWindowFlags_MenuBar)) {
if (BeginMenuBar()) {
if (BeginMenu("Settings")) {
if (BeginMenu(ICON_MS_CROP " Sizing")) {
for (int i = 0; i < 4; i++) {
if (Selectable(sizing_combo_items[i], i == table_sizing_combo)) {
table_sizing = table_sizing_flags[i];
table_sizing_combo = i;
}
}
ImGui::EndMenu();
}
ImGui::EndMenu();
}
EndMenuBar();
}
if (Button(ICON_MS_REFRESH)) {
refresh_module_list(iris->ps2->iop);
}
Separator();
if (BeginChild("##tablechild", ImVec2(0, GetContentRegionAvail().y * 0.9))) {
show_modules_table(iris);
EndChild();
}
Separator();
const struct iop_module* mod = find_iop_module(iris, iop->pc);
if (!mod) {
Text("Current module: \n");
} else {
Text("Current module: %s (%08x-%08x)\n", mod->name, mod->text_addr, mod->text_addr + mod->text_size);
}
}
End();
}
}
================================================
FILE: frontend/ui/overlay.cpp
================================================
#include
#include
#include "iris.hpp"
#include "res/IconsMaterialSymbols.h"
#include "implot.h"
#define MAX_SAMPLES 100
namespace iris {
std::vector fps_history = { 0 };
std::vector fps_history_avg = { 0 };
ImVec2 pos = ImVec2(10, 10);
const ImVec2 padding = ImVec2(5, 5);
const ImVec2 size = ImVec2(250, 100);
const float opacity = 0.75f;
float max = 0.0;
void update_overlay(iris::instance* iris) {
// if (fps_history.size() == MAX_SAMPLES) {
// if (fps_history.front() >= max) {
// max = 0.0;
// for (int i = 1; i < MAX_SAMPLES; i++) {
// if (fps_history[i] > max) {
// max = fps_history[i];
// }
// }
// }
// fps_history.pop_front();
// }
float sample = 1.0 / ImGui::GetIO().DeltaTime;
if (!iris->pause) {
if (fps_history.size() == MAX_SAMPLES)
fps_history.erase(fps_history.begin());
fps_history.push_back(sample);
if (fps_history_avg.size() == MAX_SAMPLES)
fps_history_avg.erase(fps_history_avg.begin());
fps_history_avg.push_back(std::roundf(ImGui::GetIO().Framerate));
}
}
void show_overlay(iris::instance* iris) {
using namespace ImGui;
using namespace ImPlot;
SetNextWindowBgAlpha(0.5f);
ImVec2 pos = ImVec2(10.0f, 10.0f + iris->menubar_height);
if (GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
pos.x = GetMainViewport()->Pos.x + GetMainViewport()->Size.x + 10.0f;
pos.y = GetMainViewport()->Pos.y;
}
SetNextWindowPos(pos, ImGuiCond_Always);
SetNextWindowViewport(0);
if (Begin("Overlay", nullptr,
ImGuiWindowFlags_NoDecoration |
ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoSavedSettings |
ImGuiWindowFlags_NoFocusOnAppearing |
ImGuiWindowFlags_NoNav |
ImGuiWindowFlags_NoDocking)) {
update_overlay(iris);
ImPlotFlags flags =
ImPlotFlags_NoTitle |
ImPlotFlags_NoLegend |
ImPlotFlags_NoMouseText |
ImPlotFlags_NoBoxSelect |
ImPlotFlags_NoFrame |
ImPlotFlags_NoMenus |
ImPlotFlags_CanvasOnly |
ImPlotFlags_NoInputs;
ImPlotAxisFlags axis_flags =
ImPlotAxisFlags_NoTickLabels |
ImPlotAxisFlags_NoGridLines;
if (BeginPlot("##overlay_plot", ImVec2(0, 0), flags)) {
SetupAxes(nullptr, nullptr, axis_flags, axis_flags);
SetupAxesLimits(0, (double)MAX_SAMPLES - 1, 0, 60, ImGuiCond_Always);
PlotLine("FPS", fps_history.data(), (int)fps_history.size());
EndPlot();
}
renderer_stats* stats; // = renderer_get_debug_stats(iris->ctx);
PushFont(iris->font_black);
Text("%d fps", (int)std::roundf(1.0 / ImGui::GetIO().DeltaTime));
PopFont();
// Text("Primitives: %d", stats->primitives);
// Text("Texture uploads: %d", stats->texture_uploads);
// Text("Texture blits: %d", stats->texture_blits);
} End();
}
}
================================================
FILE: frontend/ui/pad.cpp
================================================
#include
#include
#include
#include "iris.hpp"
#include "res/IconsMaterialSymbols.h"
namespace iris {
void show_pad_debugger(iris::instance* iris) {
using namespace ImGui;
if (imgui::BeginEx("DualShock 2", &iris->show_pad_debugger)) {
if (BeginTabBar("##padtabbar")) {
if (BeginTabItem("Slot 1")) {
struct ds_state* ds = (struct ds_state*)iris->ps2->sio2->port[0].udata;
if (!ds) {
Text("No controller connected");
} else {
Text("Buttons: %04x", ds->buttons);
Text("AxisRH: %04x", ds->ax_right_x);
Text("AxisLH: %04x", ds->ax_left_x);
Text("AxisRV: %04x", ds->ax_right_y);
Text("AxisLV: %04x", ds->ax_left_y);
Text("Config Mode: %d", ds->config_mode);
Text("Active Index: %d", ds->act_index);
Text("Mode Index: %d", ds->mode_index);
Text("Mode: %d", ds->mode);
Text("Vibration 0: %d", ds->vibration[0]);
Text("Vibration 1: %d", ds->vibration[1]);
Text("Mask 0: %d", ds->mask[0]);
Text("Mask 1: %d", ds->mask[1]);
Text("Lock: %d", ds->lock);
}
EndTabItem();
}
if (BeginTabItem("Slot 2")) {
struct ds_state* ds = (struct ds_state*)iris->ps2->sio2->port[1].udata;
if (!ds) {
Text("No controller connected");
} else {
Text("Buttons: %04x", ds->buttons);
Text("AxisRH: %04x", ds->ax_right_x);
Text("AxisLH: %04x", ds->ax_left_x);
Text("AxisRV: %04x", ds->ax_right_y);
Text("AxisLV: %04x", ds->ax_left_y);
Text("Config Mode: %d", ds->config_mode);
Text("Active Index: %d", ds->act_index);
Text("Mode Index: %d", ds->mode_index);
Text("Mode: %d", ds->mode);
Text("Vibration 0: %d", ds->vibration[0]);
Text("Vibration 1: %d", ds->vibration[1]);
Text("Mask 0: %d", ds->mask[0]);
Text("Mask 1: %d", ds->mask[1]);
Text("Lock: %d", ds->lock);
}
EndTabItem();
}
EndTabBar();
}
} End();
}
}
================================================
FILE: frontend/ui/settings.cpp
================================================
#include
#include
#include
#include
#include "iris.hpp"
#include "res/IconsMaterialSymbols.h"
#include "portable-file-dialogs.h"
namespace iris {
bool hovered = false;
std::string tooltip = "";
int selected_settings = 0;
int saved = 0;
mapping* get_input_mapping(iris::instance* iris, int slot) {
if (iris->input_map[slot] == -1)
return nullptr;
return &iris->input_maps[iris->input_map[slot]];
}
const char* get_input_name(input_action action) {
switch (action) {
case IRIS_DS_BT_SELECT: return "Select";
case IRIS_DS_BT_L3: return "L3";
case IRIS_DS_BT_R3: return "R3";
case IRIS_DS_BT_START: return "Start";
case IRIS_DS_BT_UP: return "D-pad Up";
case IRIS_DS_BT_RIGHT: return "D-pad Right";
case IRIS_DS_BT_DOWN: return "D-pad Down";
case IRIS_DS_BT_LEFT: return "D-pad Left";
case IRIS_DS_BT_L2: return "L2";
case IRIS_DS_BT_R2: return "R2";
case IRIS_DS_BT_L1: return "L1";
case IRIS_DS_BT_R1: return "R1";
case IRIS_DS_BT_TRIANGLE: return "Triangle";
case IRIS_DS_BT_CIRCLE: return "Circle";
case IRIS_DS_BT_CROSS: return "Cross";
case IRIS_DS_BT_SQUARE: return "Square";
case IRIS_DS_BT_ANALOG: return "Analog";
case IRIS_DS_AX_RIGHTV_POS: return "Right Stick Vertical+";
case IRIS_DS_AX_RIGHTV_NEG: return "Right Stick Vertical-";
case IRIS_DS_AX_RIGHTH_POS: return "Right Stick Horizontal+";
case IRIS_DS_AX_RIGHTH_NEG: return "Right Stick Horizontal-";
case IRIS_DS_AX_LEFTV_POS: return "Left Stick Vertical+";
case IRIS_DS_AX_LEFTV_NEG: return "Left Stick Vertical-";
case IRIS_DS_AX_LEFTH_POS: return "Left Stick Horizontal+";
case IRIS_DS_AX_LEFTH_NEG: return "Left Stick Horizontal-";
case IRIS_S14X_SW_DOWN: return "System 147/148 Down";
case IRIS_S14X_SW_UP: return "System 147/148 Up";
case IRIS_S14X_SW_ENTER: return "System 147/148 Enter";
case IRIS_S14X_SW_TEST: return "System 147/148 Test";
case IRIS_S14X_SW_SERVICE: return "System 147/148 Service";
case IRIS_S14X_SW_P1_START: return "System 147/148 P1 Start";
case IRIS_S14X_SW_P2_START: return "System 147/148 P2 Start";
case IRIS_S14X_SW_P3_START: return "System 147/148 P3 Start";
case IRIS_S14X_SW_P4_START: return "System 147/148 P4 Start";
}
return "";
}
std::string get_event_name(const input_event& event) {
std::string name;
switch (event.type) {
case IRIS_EVENT_KEYBOARD: {
SDL_Keycode keycode = static_cast(event.id);
name = SDL_GetKeyName(keycode & 0xf0000fff);
// Append modifier names
if ((keycode >> 12) & SDL_KMOD_LSHIFT) name = "Left Shift + " + name;
if ((keycode >> 12) & SDL_KMOD_RSHIFT) name = "Right Shift + " + name;
if ((keycode >> 12) & SDL_KMOD_LCTRL) name = "Left Ctrl + " + name;
if ((keycode >> 12) & SDL_KMOD_RCTRL) name = "Right Ctrl + " + name;
if ((keycode >> 12) & SDL_KMOD_LALT) name = "Left Alt + " + name;
if ((keycode >> 12) & SDL_KMOD_RALT) name = "Right Alt + " + name;
} break;
case IRIS_EVENT_GAMEPAD_BUTTON: {
SDL_GamepadButton button = static_cast(event.id);
name = SDL_GetGamepadStringForButton(button);
} break;
case IRIS_EVENT_GAMEPAD_AXIS_POS: {
SDL_GamepadAxis axis = static_cast(event.id);
name = SDL_GetGamepadStringForAxis(axis) + std::string("+");
} break;
case IRIS_EVENT_GAMEPAD_AXIS_NEG: {
SDL_GamepadAxis axis = static_cast(event.id);
name = SDL_GetGamepadStringForAxis(axis) + std::string("-");
} break;
default: {
name = "unknown";
} break;
}
// Capitalize first letter
if (!name.empty()) {
name[0] = std::toupper(name[0]);
}
return name;
}
const char* settings_renderer_names[] = {
"Null",
"Software",
"Software (Threaded)"
};
const char* settings_aspect_mode_names[] = {
"Native",
"Stretch",
"Stretch (Keep aspect ratio)",
"Force 4:3 (NTSC)",
"Force 16:9 (Widescreen)",
"Force 5:4 (PAL)",
"Auto"
};
const char* settings_fullscreen_names[] = {
"Windowed",
"Fullscreen (Desktop)",
};
const char* settings_rotation_names[] = {
"0 degrees",
"90 degrees",
"180 degrees",
"270 degrees"
};
int settings_fullscreen_flags[] = {
0,
SDL_WINDOW_FULLSCREEN
};
const char* settings_buttons[] = {
" " ICON_MS_DEPLOYED_CODE " System",
" " ICON_MS_FOLDER " Paths",
" " ICON_MS_MONITOR " Graphics",
" " ICON_MS_BRUSH " Shaders",
" " ICON_MS_STADIA_CONTROLLER " Input",
" " ICON_MS_SD_CARD " Memory cards",
" " ICON_MS_MORE_HORIZ " Misc.",
nullptr
};
const char* system_names[] = {
"Auto",
"Retail (Fat)",
"Retail (Slim)",
"PSX DESR",
"TEST unit (DTL-H)",
"TOOL unit (DTL-T)",
"Konami Python",
"Konami Python 2",
"Namco System 147",
"Namco System 148",
"Namco System 246",
"Namco System 256"
};
const char* mechacon_model_names[] = {
"SPC970",
"Dragon"
};
void show_system_settings(iris::instance* iris) {
using namespace ImGui;
Text("Model");
if (BeginCombo("##combo", system_names[iris->system])) {
for (int i = 0; i < IM_ARRAYSIZE(system_names); i++) {
if (Selectable(system_names[i], i == iris->system)) {
iris->system = i;
ps2_set_system(iris->ps2, i);
}
}
EndCombo();
}
if (BeginTable("##specs-table", 2, ImGuiTableFlags_SizingFixedSame)) {
TableNextRow();
if (iris->system == 0) {
TableSetColumnIndex(0);
TextDisabled("Detected system");
TableSetColumnIndex(1);
Text("%s", system_names[iris->ps2->detected_system]);
TableNextRow();
}
TableSetColumnIndex(0);
TextDisabled("Main RAM");
TableSetColumnIndex(1);
Text("%d MB", iris->ps2->ee_ram->size / (1024 * 1024));
TableNextRow();
TableSetColumnIndex(0);
TextDisabled("IOP RAM");
TableSetColumnIndex(1);
Text("%d MB", iris->ps2->iop_ram->size / (1024 * 1024));
TableNextRow();
TableSetColumnIndex(0);
TextDisabled("MechaCon Model");
TableSetColumnIndex(1);
Text("%s", mechacon_model_names[iris->ps2->cdvd->mechacon_model]);
EndTable();
}
Text("\nTimescale");
char buf[16];
sprintf(buf, "%dx", iris->timescale);
if (BeginCombo("##timescale", buf)) {
for (int i = 0; i < 9; i++) {
char buf[16]; snprintf(buf, 16, "%dx", 1 << i);
if (Selectable(buf, iris->timescale == (1 << i))) {
iris->timescale = (1 << i);
ps2_set_timescale(iris->ps2, iris->timescale);
}
}
EndCombo();
}
if (BeginTable("##effective-clock", 2, ImGuiTableFlags_SizingFixedSame)) {
TableNextRow();
TableSetColumnIndex(0);
TextDisabled("Effective frequency");
TableSetColumnIndex(1);
Text("%.3f MHz", 294.912f / iris->timescale);
EndTable();
}
SeparatorText("Network");
// To-do: Improve MAC address input by using a single text input
// that fills in the colons automatically
Text("MAC Address");
PushFont(iris->font_code);
float w = CalcTextSize("FFFFFFFFFFFF").x;
SetNextItemWidth(w * 2.0);
if (InputScalarN("##macaddress", ImGuiDataType_U8, iris->mac_address, 6, nullptr, nullptr, "%02X", ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase)) {
ps2_set_mac_address(iris->ps2, iris->mac_address);
} SameLine();
PopFont();
if (Button(ICON_MS_REFRESH "##macaddress")) {
ps2_set_mac_address(iris->ps2, iris->mac_address);
}
SeparatorText("Misc.");
PushStyleVarY(ImGuiStyleVar_FramePadding, 2.0F);
Checkbox("Start games automatically", &iris->autostart);
Checkbox("Skip FMVs", &iris->skip_fmv);
PopStyleVar();
}
const char* ssaa_names[] = {
"Disabled",
"2x",
"4x",
"8x",
"16x"
};
void show_hardware_renderer_settings(iris::instance* iris) {
using namespace ImGui;
Text("SSAA");
if (BeginCombo("##ssaa", ssaa_names[iris->hardware_backend_config.super_sampling])) {
for (int i = 0; i < IM_ARRAYSIZE(ssaa_names); i++) {
if (Selectable(ssaa_names[i], iris->hardware_backend_config.super_sampling == i)) {
iris->hardware_backend_config.super_sampling = i;
if (i != 0) {
iris->hardware_backend_config.force_progressive = true;
}
render::refresh(iris);
}
}
EndCombo();
}
PushStyleVarY(ImGuiStyleVar_FramePadding, 2.0F);
// BeginDisabled(iris->hardware_backend_config.super_sampling != 0);
if (Checkbox(" Force progressive scan", &iris->hardware_backend_config.force_progressive)) {
render::refresh(iris);
}
// EndDisabled();
if (Checkbox(" Overscan", &iris->hardware_backend_config.overscan)) {
render::refresh(iris);
}
PopStyleVar();
SeparatorText("Advanced");
PushStyleVarY(ImGuiStyleVar_FramePadding, 2.0F);
if (Checkbox(" CRTC Offsets", &iris->hardware_backend_config.crtc_offsets)) {
render::refresh(iris);
}
if (Checkbox(" Disable Mipmaps", &iris->hardware_backend_config.disable_mipmaps)) {
render::refresh(iris);
}
if (Checkbox(" Unsynced Readbacks", &iris->hardware_backend_config.unsynced_readbacks)) {
render::refresh(iris);
}
if (Checkbox(" Backbuffer Promotion", &iris->hardware_backend_config.backbuffer_promotion)) {
render::refresh(iris);
}
if (Checkbox(" Allow Blend Demote", &iris->hardware_backend_config.allow_blend_demote)) {
render::refresh(iris);
}
PopStyleVar();
}
void show_graphics_settings(iris::instance* iris) {
using namespace ImGui;
static const char* settings_renderer_names[] = {
"Null",
"Software",
"Hardware"
};
Text("Renderer");
if (BeginCombo("##renderer", settings_renderer_names[iris->renderer_backend], ImGuiComboFlags_HeightSmall)) {
for (int i = 0; i < 3; i++) {
BeginDisabled(i == RENDERER_BACKEND_SOFTWARE);
if (Selectable(settings_renderer_names[i], i == iris->renderer_backend)) {
render::switch_backend(iris, i);
}
EndDisabled();
}
EndCombo();
}
Text("Aspect mode");
if (BeginCombo("##aspectmode", settings_aspect_mode_names[iris->aspect_mode])) {
for (int i = 0; i < 7; i++) {
if (Selectable(settings_aspect_mode_names[i], iris->aspect_mode == i)) {
iris->aspect_mode = i;
}
}
EndCombo();
}
BeginDisabled(
iris->aspect_mode == RENDER_ASPECT_AUTO ||
iris->aspect_mode == RENDER_ASPECT_STRETCH ||
iris->aspect_mode == RENDER_ASPECT_STRETCH_KEEP
);
Text("Scale");
char buf[16]; snprintf(buf, 16, "%.1fx", (float)iris->scale);
if (BeginCombo("##scale", buf, ImGuiComboFlags_HeightSmall)) {
for (int i = 2; i <= 6; i++) {
snprintf(buf, 16, "%.1fx", (float)i * 0.5f);
if (Selectable(buf, ((float)i * 0.5f) == iris->scale)) {
iris->scale = (float)i * 0.5f;
}
}
EndCombo();
}
EndDisabled();
Text("Scaling");
const char* filter_names[] = {
"Nearest",
"Bilinear",
"Cubic"
};
if (BeginCombo("##scalingfilter", filter_names[iris->filter])) {
for (int i = 0; i < 3; i++) {
BeginDisabled(i == 2 && !iris->cubic_supported);
if (Selectable(filter_names[i], iris->filter == i)) {
iris->filter = i;
}
EndDisabled();
}
EndCombo();
}
const int normalized_angle = ((iris->angle % 360) + 360) % 360;
const int rotation_index = normalized_angle / 90;
Text("Rotation");
if (BeginCombo("##rotation", settings_rotation_names[rotation_index])) {
for (int i = 0; i < 4; i++) {
if (Selectable(settings_rotation_names[i], rotation_index == i)) {
iris->angle = i * 90;
}
}
EndCombo();
}
Text("Window mode");
if (BeginCombo("##windowmode", settings_fullscreen_names[iris->fullscreen])) {
for (int i = 0; i < 2; i++) {
if (Selectable(settings_fullscreen_names[i], iris->fullscreen == i)) {
iris->fullscreen = i;
SDL_SetWindowFullscreen(iris->window, settings_fullscreen_flags[i]);
}
}
EndCombo();
}
SeparatorText("Misc.");
PushStyleVarY(ImGuiStyleVar_FramePadding, 2.0F);
if (Checkbox(" VSync", &iris->vsync)) {
imgui::set_vsync(iris, iris->vsync);
iris->swapchain_rebuild = true;
}
Checkbox(" Integer scaling", &iris->integer_scaling);
Checkbox(" Flip horizontally", &iris->flip_x);
Checkbox(" Flip vertically", &iris->flip_y);
PopStyleVar();
if (iris->renderer_backend == RENDERER_BACKEND_HARDWARE) {
SeparatorText("Renderer settings");
show_hardware_renderer_settings(iris);
}
SeparatorText("Vulkan settings");
Text("GPU");
static bool changed = false;
const char* hint;
const auto& selected_device = iris->vulkan_gpus[iris->vulkan_selected_device_index];
if (iris->vulkan_physical_device < 0) {
hint = "Auto";
} else {
hint = iris->vulkan_gpus[iris->vulkan_physical_device].name.c_str();
}
if (changed) {
SameLine();
TextColored(ImVec4(211.0/255.0, 167.0/255.0, 30.0/255.0, 1.0), ICON_MS_WARNING " Restart the emulator to apply these changes");
}
PushStyleVarY(ImGuiStyleVar_ItemSpacing, 5.0F);
if (BeginCombo("##gpu", hint)) {
if (Selectable("Auto", iris->vulkan_physical_device < 0)) {
iris->vulkan_physical_device = -1;
}
for (int i = 0; i < iris->vulkan_gpus.size(); i++) {
const auto& device = iris->vulkan_gpus[i];
std::string name = device.name;
if (device.device == selected_device.device) {
name += " (Current)";
}
if (Selectable(name.c_str(), device.device == selected_device.device)) {
changed = iris->vulkan_physical_device != i;
iris->vulkan_physical_device = i;
}
}
EndCombo();
}
PushStyleVarY(ImGuiStyleVar_FramePadding, 2.0F);
if (Checkbox(" Enable validation layers", &iris->vulkan_enable_validation_layers)) {
changed = true;
}
PopStyleVar(2);
}
void show_controller_slot(iris::instance* iris, int slot) {
using namespace ImGui;
char label[9] = "Slot #";
label[5] = '1' + slot;
ImVec4 col = GetStyleColorVec4(iris->ds[slot] ? ImGuiCol_Text : ImGuiCol_TextDisabled);
col.w = 1.0;
if (BeginChild(label, ImVec2(GetContentRegionAvail().x / 2.0 - 10.0, 50 * iris->ui_scale))) {
Text("Controller");
std::string controller_name = "None";
if (iris->ds[slot]) {
controller_name = "DualShock 2";
}
float avail_width = GetContentRegionAvail().x;
SetNextItemWidth(avail_width);
if (BeginCombo("##controller", controller_name.c_str())) {
if (Selectable("None")) {
if (iris->ds[slot]) {
ps2_sio2_detach_device(iris->ps2->sio2, slot);
iris->ds[slot] = nullptr;
}
}
if (Selectable("DualShock 2")) {
if (!iris->ds[slot]) {
iris->ds[slot] = ds_attach(iris->ps2->sio2, slot);
}
}
EndCombo();
}
} EndChild(); SameLine(0.0, 10.0);
if (BeginChild((std::string(label) + "##icon").c_str(), ImVec2(0, 50 * iris->ui_scale))) {
BeginDisabled(!iris->ds[slot]);
float avail_width = GetContentRegionAvail().x;
Text("Input device");
std::string name = "None";
if (!iris->input_devices[slot]) {
name = "None";
} else if (iris->input_devices[slot]->get_type() == 0) {
name = "Keyboard";
} else if (iris->input_devices[slot]->get_type() == 1) {
gamepad_device* gp = static_cast(iris->input_devices[slot]);
name = SDL_GetGamepadNameForID(gp->get_id());
}
SetNextItemWidth(avail_width);
if (BeginCombo("##devicetype", name.c_str())) {
if (Selectable("None")) {
if (iris->input_devices[slot]) {
delete iris->input_devices[slot];
iris->input_devices[slot] = nullptr;
}
}
if (Selectable("Keyboard")) {
if (iris->input_devices[slot]) {
delete iris->input_devices[slot];
iris->input_devices[slot] = nullptr;
}
iris->input_devices[slot] = new keyboard_device();
iris->input_devices[slot]->set_slot(slot);
if (iris->input_map[slot] <= 1) {
iris->input_map[slot] = 0;
}
}
for (auto gamepad : iris->gamepads) {
if (Selectable(SDL_GetGamepadNameForID(gamepad.first))) {
if (iris->input_devices[slot]) {
delete iris->input_devices[slot];
iris->input_devices[slot] = nullptr;
}
iris->input_devices[slot] = new gamepad_device(gamepad.first);
iris->input_devices[slot]->set_slot(slot);
if (iris->input_map[slot] <= 1) {
iris->input_map[slot] = 1;
}
}
}
EndCombo();
}
EndDisabled();
} EndChild();
InvisibleButton("##slot0", ImVec2(10, 10));
texture* tex = &iris->dualshock2_icon;
float width = 250.0f;
float height = (tex->height * width) / tex->width;
SetCursorPosX((GetContentRegionAvail().x / 2.0) - (width / 2.0));
Image(
(ImTextureID)(intptr_t)tex->descriptor_set,
ImVec2(width, height),
ImVec2(0, 0), ImVec2(1, 1),
col,
ImVec4(0.0, 0.0, 0.0, 0.0)
);
InvisibleButton("##pad1", ImVec2(10, 10));
Text("Mapping");
SetNextItemWidth(GetContentRegionAvail().x / 2.0 - 10.0);
mapping* mapping = get_input_mapping(iris, slot);
if (BeginCombo("##mapping", mapping ? mapping->name.c_str() : "None")) {
if (Selectable("None", mapping == nullptr)) {
iris->input_map[slot] = -1;
}
int i = 0;
for (auto& map : iris->input_maps) {
if (Selectable(map.name.c_str(), mapping == &map)) {
iris->input_map[slot] = i;
}
i++;
}
EndCombo();
}
}
bool event_is_mod_key(const input_event& event) {
if (event.type != IRIS_EVENT_KEYBOARD) {
return false;
}
SDL_Keycode keycode = static_cast(event.id);
return (keycode & 0xf0000fff) == SDLK_LSHIFT ||
(keycode & 0xf0000fff) == SDLK_RSHIFT ||
(keycode & 0xf0000fff) == SDLK_LCTRL ||
(keycode & 0xf0000fff) == SDLK_RCTRL ||
(keycode & 0xf0000fff) == SDLK_LALT ||
(keycode & 0xf0000fff) == SDLK_RALT;
}
int selected_mapping = 0;
bool waiting_for_input = false;
uint64_t mapping_editing = 0;
void show_mappings_editor(iris::instance* iris) {
using namespace ImGui;
static char buf[1024] = { 0 };
const char* hint = iris->gcdb_path.size() ? iris->gcdb_path.c_str() : "Not configured (using default)";
Text("Game controller DB");
SetNextItemWidth(300);
if (InputTextWithHint("##gcdbinput", hint, buf, 1024, ImGuiInputTextFlags_EscapeClearsAll | ImGuiInputTextFlags_EnterReturnsTrue)) {
iris->gcdb_path = std::string(buf);
// To-do: Check return value
input::load_db_from_file(iris, iris->gcdb_path.c_str());
}
SameLine();
if (Button(ICON_MS_FOLDER "##gcdbbtn")) {
audio::mute(iris);
auto f = pfd::open_file("Select Game controller DB file", "", {
"Game controller DB (*.txt)", "*.txt",
"All Files (*.*)", "*"
});
while (!f.ready());
audio::unmute(iris);
if (f.result().size()) {
strncpy(buf, f.result().at(0).c_str(), 1024);
iris->gcdb_path = std::string(buf);
// To-do: Check return value
input::load_db_from_file(iris, iris->gcdb_path.c_str());
}
} SameLine();
if (Button(ICON_MS_CLEAR "##gcdbclear")) {
iris->gcdb_path = "";
memset(buf, 0, 1024);
input::load_db_default(iris);
}
Text("Mapping");
if (BeginCombo("##mapping", iris->input_maps[selected_mapping].name.c_str())) {
int i = 0;
for (auto& map : iris->input_maps) {
if (Selectable(map.name.c_str(), selected_mapping == i)) {
selected_mapping = i;
}
i++;
}
EndCombo();
} SameLine();
if (selected_mapping <= 1) {
if (Button(ICON_MS_REFRESH " Default")) {
input::init_default_mapping(iris, selected_mapping);
}
}
SetNextItemWidth(GetContentRegionAvail().x);
if (BeginTable("##mappingeditor", 2, ImGuiTableFlags_SizingStretchProp)) {
TableSetupColumn("Input");
TableSetupColumn("Mapping");
std::vector> elems(
iris->input_maps[selected_mapping].map.forward_map().begin(),
iris->input_maps[selected_mapping].map.forward_map().end());
std::sort(elems.begin(), elems.end(), [](const std::pair& a, const std::pair& b) {
return a.second < b.second;
});
for (auto& entry : elems) {
TableNextRow();
std::string key_name = get_input_name(static_cast(entry.second));
TableSetColumnIndex(0);
AlignTextToFramePadding();
Text("%s", key_name.c_str());
TableSetColumnIndex(1);
input_event event;
event.u64 = entry.first;
std::string value_name = get_event_name(event) + "##" + key_name;
if (waiting_for_input && (mapping_editing == entry.first)) {
PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled));
if (Button("Press a key or button...", ImVec2(GetContentRegionAvail().x, 0))) {
waiting_for_input = false;
}
PopStyleColor();
if (iris->last_input_event_read == false && iris->last_input_event_value > 0.5f && !event_is_mod_key(iris->last_input_event)) {
iris->last_input_event_read = true;
waiting_for_input = false;
mapping_editing = 0;
auto event = iris->last_input_event;
auto action = entry.second;
// printf("Mapping input event %s (%llu) to action %s (%llu)\n",
// get_event_name(iris->last_input_event).c_str(),
// iris->last_input_event.u64,
// get_input_name(action),
// static_cast(entry.second)
// );
auto* value_ptr = iris->input_maps[selected_mapping].map.get_value(event.u64);
if (value_ptr != nullptr) {
// Remove previous mapping for this input event
auto value = *value_ptr;
auto key = *iris->input_maps[selected_mapping].map.get_key(action);
// printf("Removing previous mapping of event %s (%llu) to action %s (%llu)\n",
// get_event_name(event).c_str(),
// event.u64,
// get_input_name(value),
// static_cast(value)
// );
iris->input_maps[selected_mapping].map.erase_by_key(event.u64);
iris->input_maps[selected_mapping].map.erase_by_value(action);
iris->input_maps[selected_mapping].map.insert(event.u64, action);
iris->input_maps[selected_mapping].map.insert(key, value);
} else {
iris->input_maps[selected_mapping].map.erase_by_value(action);
iris->input_maps[selected_mapping].map.insert(event.u64, action);
}
}
} else {
if (Button(value_name.c_str(), ImVec2(GetContentRegionAvail().x, 0))) {
iris->last_input_event_read = true;
waiting_for_input = true;
mapping_editing = entry.first;
}
}
// if (IsMouseDoubleClicked(ImGuiMouseButton_Left) && IsItemHovered()) {
// iris->last_input_event_read = true;
// waiting_for_input = true;
// mapping_editing = entry.first;
// }
}
EndTable();
}
}
void show_input_settings(iris::instance* iris) {
using namespace ImGui;
if (BeginTabBar("##inputtabs")) {
if (BeginTabItem("Slot 1")) {
show_controller_slot(iris, 0);
EndTabItem();
}
if (BeginTabItem("Slot 2")) {
show_controller_slot(iris, 1);
EndTabItem();
}
if (BeginTabItem("Mappings")) {
show_mappings_editor(iris);
EndTabItem();
}
EndTabBar();
}
}
void show_paths_settings(iris::instance* iris) {
using namespace ImGui;
static char buf[512];
static char dvd_buf[512];
static char rom2_buf[512];
static char nvram_buf[512];
static char flash_buf[512];
Text("BIOS (rom0)");
if (IsItemHovered()) {
hovered = true;
tooltip = ICON_MS_INFO " Select a BIOS file, this is required for the emulator to function properly";
}
const char* bios_hint = iris->bios_path.size() ? iris->bios_path.c_str() : "e.g. scph10000.bin";
const char* rom1_hint = iris->rom1_path.size() ? iris->rom1_path.c_str() : "Not configured";
const char* rom2_hint = iris->rom2_path.size() ? iris->rom2_path.c_str() : "Not configured";
const char* nvram_hint = iris->nvram_path.size() ? iris->nvram_path.c_str() : "Not configured";
const char* flash_hint = iris->flash_path.size() ? iris->flash_path.c_str() : "Not configured";
SetNextItemWidth(300);
InputTextWithHint("##rom0", bios_hint, buf, 512, ImGuiInputTextFlags_EscapeClearsAll);
SameLine();
if (Button(ICON_MS_FOLDER "##rom0")) {
audio::mute(iris);
auto f = pfd::open_file("Select BIOS file", "", {
"BIOS dumps (*.bin; *.rom0)", "*.bin *.rom0",
"All Files (*.*)", "*"
});
while (!f.ready());
audio::unmute(iris);
if (f.result().size()) {
strncpy(buf, f.result().at(0).c_str(), 512);
ps2_load_bios(iris->ps2, buf);
}
}
if (BeginTable("##rom-info", 2, ImGuiTableFlags_SizingFixedFit)) {
TableNextRow();
TableSetColumnIndex(0);
TextDisabled("Model" " ");
TableSetColumnIndex(1);
Text("%s", iris->ps2->rom0_info.model);
TableNextRow();
TableSetColumnIndex(0);
TextDisabled("Version" " ");
TableSetColumnIndex(1);
Text("%s", iris->ps2->rom0_info.version);
TableNextRow();
TableSetColumnIndex(0);
TextDisabled("Region" " ");
TableSetColumnIndex(1);
Text("%s", iris->ps2->rom0_info.region);
TableNextRow();
TableSetColumnIndex(0);
TextDisabled("MD5 hash" " ");
TableSetColumnIndex(1);
Text("%s", iris->ps2->rom0_info.md5hash); SameLine();
if (SmallButton(ICON_MS_CONTENT_COPY)) {
SDL_SetClipboardText(iris->ps2->rom0_info.md5hash);
}
EndTable();
}
Separator();
Text("DVD Player (rom1)");
SetNextItemWidth(300);
InputTextWithHint("##rom1", rom1_hint, dvd_buf, 512, ImGuiInputTextFlags_EscapeClearsAll);
SameLine();
if (Button(ICON_MS_FOLDER "##rom1")) {
audio::mute(iris);
auto f = pfd::open_file("Select DVD BIOS file", "", {
"DVD BIOS dumps (*.bin; *.rom1)", "*.bin *.rom1",
"All Files (*.*)", "*"
});
while (!f.ready());
audio::unmute(iris);
if (f.result().size()) {
strncpy(dvd_buf, f.result().at(0).c_str(), 512);
ps2_load_rom1(iris->ps2, dvd_buf);
}
} SameLine();
if (Button(ICON_MS_CLEAR "##rom1")) {
iris->rom1_path = "";
memset(dvd_buf, 0, 512);
}
if (iris->rom1_path.size()) {
if (BeginTable("##rom1-info", 2, ImGuiTableFlags_SizingFixedFit)) {
TableNextRow();
TableSetColumnIndex(0);
TextDisabled("Version" " ");
TableSetColumnIndex(1);
Text("%s", iris->ps2->rom1_info.version);
TableNextRow();
TableSetColumnIndex(0);
TextDisabled("MD5 hash" " ");
TableSetColumnIndex(1);
Text("%s", iris->ps2->rom1_info.md5hash); SameLine();
if (SmallButton(ICON_MS_CONTENT_COPY)) {
SDL_SetClipboardText(iris->ps2->rom1_info.md5hash);
}
EndTable();
}
Separator();
}
Text("Chinese extensions (rom2)");
SetNextItemWidth(300);
InputTextWithHint("##rom2", rom2_hint, rom2_buf, 512, ImGuiInputTextFlags_EscapeClearsAll);
SameLine();
if (Button(ICON_MS_FOLDER "##rom2")) {
audio::mute(iris);
auto f = pfd::open_file("Select ROM2 file", "", {
"ROM2 dumps (*.bin; *.rom2)", "*.bin *.rom2",
"All Files (*.*)", "*"
});
while (!f.ready());
audio::unmute(iris);
if (f.result().size()) {
strncpy(rom2_buf, f.result().at(0).c_str(), 512);
}
} SameLine();
if (Button(ICON_MS_CLEAR "##rom2")) {
iris->rom2_path = "";
memset(rom2_buf, 0, 512);
}
Text("EEPROM memory (nvram)");
SetNextItemWidth(300);
InputTextWithHint("##nvram", nvram_hint, nvram_buf, 512, ImGuiInputTextFlags_EscapeClearsAll);
SameLine();
if (Button(ICON_MS_FOLDER "##nvram")) {
audio::mute(iris);
auto f = pfd::open_file("Select NVRAM file", "", {
"NVRAM dumps (*.nvm; *.bin)", "*.nvm *.bin",
"All Files (*.*)", "*"
});
while (!f.ready());
audio::unmute(iris);
if (f.result().size()) {
strncpy(nvram_buf, f.result().at(0).c_str(), 512);
}
} SameLine();
if (Button(ICON_MS_CLEAR "##nvram")) {
iris->nvram_path = "";
memset(nvram_buf, 0, 512);
}
Text("Flash memory (xfrom)");
SetNextItemWidth(300);
InputTextWithHint("##flash", flash_hint, flash_buf, 512, ImGuiInputTextFlags_EscapeClearsAll);
SameLine();
if (Button(ICON_MS_FOLDER "##flash")) {
audio::mute(iris);
auto f = pfd::open_file("Select Flash/XFROM dump file", "", {
"XFROM dumps (*.bin)", "*.bin",
"All Files (*.*)", "*"
});
while (!f.ready());
audio::unmute(iris);
if (f.result().size()) {
strncpy(flash_buf, f.result().at(0).c_str(), 512);
}
} SameLine();
if (Button(ICON_MS_CLEAR "##xfrom")) {
iris->flash_path = "";
memset(flash_buf, 0, 512);
}
if (Button(ICON_MS_SAVE " Save")) {
std::string bios_path = buf;
std::string rom1_path = dvd_buf;
std::string rom2_path = rom2_buf;
std::string flash_path = flash_buf;
std::string nvram_path = nvram_buf;
if (bios_path.size()) iris->bios_path = bios_path;
if (rom1_path.size()) iris->rom1_path = rom1_path;
if (rom2_path.size()) iris->rom2_path = rom2_path;
if (flash_path.size()) iris->flash_path = flash_path;
if (nvram_path.size()) iris->nvram_path = nvram_path;
saved = 1;
} SameLine();
if (saved) {
TextColored(ImVec4(211.0/255.0, 167.0/255.0, 30.0/255.0, 1.0), ICON_MS_WARNING " Restart the emulator to apply these changes");
}
}
static char slot0_buf[1024];
static char slot1_buf[1024];
void show_memory_card(iris::instance* iris, int slot) {
using namespace ImGui;
char label[9] = "##mcard0";
label[7] = '0' + slot;
if (BeginChild(label, ImVec2(GetContentRegionAvail().x / (slot ? 1.0 : 2.0) - 10.0, 0))) {
std::string& path = slot ? iris->mcd1_path : iris->mcd0_path;
ImVec4 col = GetStyleColorVec4(iris->mcd_slot_type[slot] ? ImGuiCol_Text : ImGuiCol_TextDisabled);
col.w = 1.0;
InvisibleButton("##pad0", ImVec2(10, 10));
texture* tex = &iris->ps2_memory_card_icon;
if (iris->mcd_slot_type[slot] == 2) {
tex = &iris->ps1_memory_card_icon;
} else if (iris->mcd_slot_type[slot] == 3) {
tex = &iris->pocketstation_icon;
}
SetCursorPosX((GetContentRegionAvail().x / 2.0) - (tex->width / 2.0));
Image(
(ImTextureID)(intptr_t)tex->descriptor_set,
ImVec2(tex->width, tex->height),
ImVec2(0, 0), ImVec2(1, 1),
col,
ImVec4(0.0, 0.0, 0.0, 0.0)
);
InvisibleButton("##pad1", ImVec2(10, 10));
if (path.size() && !iris->mcd_slot_type[slot]) {
TextColored(ImVec4(211.0/255.0, 167.0/255.0, 30.0/255.0, 1.0), ICON_MS_WARNING " Check file");
if (IsItemHovered(ImGuiHoveredFlags_DelayNormal)) {
if (BeginTooltip()) {
Text("Please check files ");
EndTooltip();
}
}
}
PushFont(iris->font_heading);
Text("Slot %d", slot+1);
PopFont();
char* buf = slot ? slot1_buf : slot0_buf;
const char* hint = path.size() ? path.c_str() : "Not configured";
char it_label[7] = "##mcd0";
char bt_label[10] = ICON_MS_FOLDER "##mcd0";
char ed_label[10];
snprintf(ed_label, 10, "%s##mcd0", iris->mcd_slot_type[slot] ? ICON_MS_ARROW_DOWNWARD : ICON_MS_ARROW_UPWARD);
it_label[5] = '0' + slot;
bt_label[8] = '0' + slot;
ed_label[8] = '0' + slot;
InputTextWithHint(it_label, hint, buf, 512, ImGuiInputTextFlags_EscapeClearsAll);
SameLine();
if (Button(bt_label)) {
audio::mute(iris);
auto f = pfd::open_file("Select Memory Card file for Slot 1", iris->pref_path, {
"Memory Card files (*.ps2; *.mcd; *.bin; *.psm; *.pocket)", "*.ps2 *.mcd *.bin *.psm *.pocket",
"All Files (*.*)", "*"
});
while (!f.ready());
audio::unmute(iris);
if (f.result().size()) {
strncpy(buf, f.result().at(0).c_str(), 512);
path = f.result().at(0);
emu::attach_memory_card(iris, slot, path.c_str());
}
}
SameLine();
BeginDisabled((!iris->mcd_slot_type[slot]) && (!path.size()));
if (Button(ed_label)) {
if (iris->mcd_slot_type[slot]) {
emu::detach_memory_card(iris, slot);
} else {
emu::attach_memory_card(iris, slot, path.c_str());
}
}
EndDisabled();
} EndChild();
}
void show_memory_card_settings(iris::instance* iris) {
using namespace ImGui;
if (Button(ICON_MS_EDIT " Create memory cards...")) {
// Launch memory card utility
iris->show_memory_card_tool = true;
}
Separator();
show_memory_card(iris, 0); SameLine(0.0, 10.0);
show_memory_card(iris, 1);
}
static const char* const theme_names[] = {
"Granite",
"ImGui Dark",
"ImGui Light",
"ImGui Classic",
"Cherry",
"Source"
};
static const char* const codeview_color_scheme_names[] = {
"Solarized Dark",
"Solarized Light",
"One Dark Pro",
"Catppuccin Latte",
"Catppuccin Frappé",
"Catppuccin Macchiato",
"Catppuccin Mocha"
};
#ifdef _WIN32
static const char* titlebar_style_names[] = {
"Default",
"Seamless"
};
#endif
void show_misc_settings(iris::instance* iris) {
using namespace ImGui;
SeparatorText("Style");
Text("Theme");
if (BeginCombo("##theme", theme_names[iris->theme])) {
for (int i = 0; i < IM_ARRAYSIZE(theme_names); i++) {
if (Selectable(theme_names[i], iris->theme == i)) {
iris->theme = i;
imgui::set_theme(iris, i);
platform::apply_settings(iris);
}
}
EndCombo();
}
Text("Background color");
ColorEdit3("##bgcolor", (float*)&iris->clear_value.color);
Text("UI scale");
DragFloat("##uiscale", &iris->ui_scale, 0.05f, 0.5f, 1.5f, "%.1f");
GetStyle().FontScaleMain = iris->ui_scale;
PushStyleVarY(ImGuiStyleVar_FramePadding, 2.0F);
Checkbox("Enable viewports", &iris->imgui_enable_viewports); SameLine();
PopStyleVar();
TextDisabled(ICON_MS_WARNING " Experimental feature, requires restart");
#ifdef _WIN32
Text("Titlebar style (Windows only)");
if (BeginCombo("##titlebar_style", titlebar_style_names[iris->windows_titlebar_style])) {
for (int i = 0; i < 2; i++) {
if (Selectable(titlebar_style_names[i], iris->windows_titlebar_style == i)) {
iris->windows_titlebar_style = i;
platform::apply_settings(iris);
}
}
EndCombo();
}
PushStyleVarY(ImGuiStyleVar_FramePadding, 2.0F);
BeginDisabled(iris->windows_titlebar_style != IRIS_TITLEBAR_DEFAULT);
if (Checkbox(" Immersive dark mode", &iris->windows_dark_mode)) {
platform::apply_settings(iris);
}
EndDisabled();
if (Checkbox(" Show window borders", &iris->windows_enable_borders)) {
platform::apply_settings(iris);
}
PopStyleVar();
#endif
SeparatorText("Codeview");
#define SCHEME(str, id) \
if (Selectable(str, iris->codeview_color_scheme == id)) { \
iris->codeview_color_scheme = id; \
imgui::set_codeview_scheme(iris, id); \
}
Text("Color scheme");
if (BeginCombo("##codeview_color_scheme", codeview_color_scheme_names[iris->codeview_color_scheme])) {
PushFont(iris->font_small);
TextDisabled("Dark");
PopFont();
SCHEME("Solarized Dark", IRIS_CODEVIEW_COLOR_SCHEME_SOLARIZED_DARK);
SCHEME("One Dark Pro", IRIS_CODEVIEW_COLOR_SCHEME_ONE_DARK_PRO);
SCHEME("Catppuccin Mocha", IRIS_CODEVIEW_COLOR_SCHEME_CATPPUCCIN_MOCHA);
SCHEME("Catppuccin Macchiato", IRIS_CODEVIEW_COLOR_SCHEME_CATPPUCCIN_MACCHIATO);
SCHEME("Catppuccin Frappé", IRIS_CODEVIEW_COLOR_SCHEME_CATPPUCCIN_FRAPPE);
PushFont(iris->font_small);
TextDisabled("Light");
PopFont();
SCHEME("Solarized Light", IRIS_CODEVIEW_COLOR_SCHEME_SOLARIZED_LIGHT);
SCHEME("Catppuccin Latte", IRIS_CODEVIEW_COLOR_SCHEME_CATPPUCCIN_LATTE);
EndCombo();
}
#undef SCHEME
static bool use_theme_background = !iris->codeview_use_theme_background;
PushStyleVarY(ImGuiStyleVar_FramePadding, 2.0F);
if (Checkbox("Use scheme background", &use_theme_background)) {
iris->codeview_use_theme_background = !use_theme_background;
}
PopStyleVar();
Text("Font scale");
DragFloat("##codeview_font_scale", &iris->codeview_font_scale, 0.05f, 0.75f, 1.5f, "%.1f");
SeparatorText("Screenshots");
const char* format_names[] = {
"PNG",
"BMP",
"JPG",
"TGA"
};
const char* jpg_quality_names[] = {
"Minimum", // 1
"Low", // 25
"Medium", // 50
"High", // 90
"Maximum", // 100
"Custom..."
};
const char* mode_names[] = {
"Internal",
"Display"
};
Text("Format");
if (BeginCombo("##screenshotformat", format_names[iris->screenshot_format])) {
for (int i = 0; i < 4; i++) {
if (Selectable(format_names[i], iris->screenshot_format == i)) {
iris->screenshot_format = i;
}
}
EndCombo();
}
Text("Resolution mode");
if (BeginCombo("##screenshotmode", mode_names[iris->screenshot_mode])) {
for (int i = 0; i < 2; i++) {
if (Selectable(mode_names[i], iris->screenshot_mode == i)) {
iris->screenshot_mode = i;
}
}
EndCombo();
}
if (iris->screenshot_format == IRIS_SCREENSHOT_FORMAT_JPG) {
Text("JPG Quality");
if (BeginCombo("##jpgquality", jpg_quality_names[iris->screenshot_jpg_quality_mode])) {
for (int i = 0; i < 6; i++) {
if (Selectable(jpg_quality_names[i], iris->screenshot_jpg_quality_mode == i)) {
iris->screenshot_jpg_quality_mode = i;
}
}
EndCombo();
}
if (iris->screenshot_jpg_quality_mode == IRIS_SCREENSHOT_JPG_QUALITY_CUSTOM) {
SliderInt("Quality##jpgqualitycustom", &iris->screenshot_jpg_quality, 1, 100, "%d", ImGuiSliderFlags_AlwaysClamp);
}
}
PushStyleVarY(ImGuiStyleVar_FramePadding, 2.0F);
Checkbox(" Include shader processing", &iris->screenshot_shader_processing);
PopStyleVar();
}
const char* builtin_shader_names[] = {
"iris-ntsc-encoder",
"iris-ntsc-decoder",
"iris-ntsc-curvature",
"iris-ntsc-scanlines",
"iris-ntsc-noise",
};
const char* presets[] = {
"NTSC codec",
0
};
void show_shader_settings(iris::instance* iris) {
using namespace ImGui;
static const char* selected_shader = "";
PushStyleVarY(ImGuiStyleVar_FramePadding, 2.0f);
Checkbox(" Enable shaders", &iris->enable_shaders);
PopStyleVar();
Separator();
Text("Add shader");
if (BeginCombo("##combo", selected_shader)) {
for (int i = 0; i < IM_ARRAYSIZE(builtin_shader_names); i++) {
if (Selectable(builtin_shader_names[i], selected_shader == builtin_shader_names[i])) {
selected_shader = builtin_shader_names[i];
}
}
EndCombo();
} SameLine();
if (Button(ICON_MS_ADD)) {
if (selected_shader && selected_shader[0]) {
std::string shader(selected_shader);
shaders::push(iris, selected_shader);
}
} SameLine();
if (Button(ICON_MS_REMOVE_SELECTION)) {
shaders::clear(iris);
}
// Text("Preset");
// if (BeginCombo("##presets", selected_shader)) {
// for (int i = 0; i < 3; i++) {
// if (Selectable(presets[i], selected_shader == builtin_shader_names[i])) {
// selected_shader = builtin_shader_names[i];
// }
// }
// EndCombo();
// }
if (BeginTable("##shaders", 1, ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_RowBg)) {
for (int i = 0; i < shaders::count(iris); i++) {
TableNextRow();
char bypass[16];
char del[16];
char id[1024];
sprintf(bypass, "%s##%d", shaders::at(iris, i)->bypass ? ICON_MS_CHECK_BOX_OUTLINE_BLANK : ICON_MS_CHECK_BOX, i);
sprintf(del, ICON_MS_DELETE "##%d", i);
sprintf(id, "%s##%d", shaders::at(iris, i)->get_id().c_str(), i);
TableSetColumnIndex(0);
if (SmallButton(del)) {
iris->shader_passes.erase(iris->shader_passes.begin() + i);
break;
} SameLine();
if (SmallButton(bypass)) {
shaders::at(iris, i)->bypass = !shaders::at(iris, i)->bypass;
} SameLine();
Selectable(id, false, ImGuiSelectableFlags_SpanAllColumns);
if (BeginDragDropSource()) {
SetDragDropPayload("SHADER_DND_PAYLOAD", &i, sizeof(int));
EndDragDropSource();
}
if (BeginDragDropTarget()) {
if (const ImGuiPayload* payload = AcceptDragDropPayload("SHADER_DND_PAYLOAD")) {
int src = *(int*)payload->Data;
shaders::swap(iris, src, i);
}
EndDragDropTarget();
}
}
EndTable();
}
}
void show_settings(iris::instance* iris) {
using namespace ImGui;
hovered = false;
static ImGuiWindowFlags flags =
ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoDocking;
SetNextWindowSize(ImVec2(675, 500), ImGuiCond_FirstUseEver);
PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(675, 500));
if (GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable && !GetIO().ConfigViewportsNoDecoration)
flags |= ImGuiWindowFlags_NoTitleBar;
if (Begin("Settings", &iris->show_settings, flags)) {
PushStyleVarX(ImGuiStyleVar_ButtonTextAlign, 0.0);
PushStyleVarY(ImGuiStyleVar_ItemSpacing, 6.0);
if (BeginChild("##sidebar", ImVec2(175, GetContentRegionAvail().y), ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_Borders)) {
for (int i = 0; settings_buttons[i]; i++) {
if (selected_settings == i) PushStyleColor(ImGuiCol_Button, GetStyle().Colors[ImGuiCol_ButtonHovered]);
bool pressed = Button(settings_buttons[i], ImVec2(175, 35));
if (selected_settings == i) PopStyleColor();
if (pressed) {
selected_settings = i;
}
}
} EndChild(); SameLine(0.0, 10.0);
PopStyleVar(2);
if (BeginChild("##content", ImVec2(0, GetContentRegionAvail().y), ImGuiChildFlags_AutoResizeY)) {
switch (selected_settings) {
case 0: show_system_settings(iris); break;
case 1: show_paths_settings(iris); break;
case 2: show_graphics_settings(iris); break;
case 3: show_shader_settings(iris); break;
case 4: show_input_settings(iris); break;
case 5: show_memory_card_settings(iris); break;
case 6: show_misc_settings(iris); break;
}
} EndChild();
// Separator();
// if (hovered) {
// TextWrapped(tooltip.c_str());
// } else {
// Text("Hover over an item to get more information");
// }
} End();
PopStyleVar();
}
}
================================================
FILE: frontend/ui/spu2.cpp
================================================
#include
#include
#include
#include "iris.hpp"
#include "res/IconsMaterialSymbols.h"
namespace iris {
int core0_selected = -1;
int core1_selected = -1;
float selected_adsr[120];
int adsr_index;
const char* get_adsr_stage_name(int s) {
switch (s) {
case 0: return "Attack";
case 1: return "Decay";
case 2: return "Sustain";
case 3: return "Release";
case 4: return "End";
}
return "None";
}
void show_spu2_core(iris::instance* iris, int c) {
using namespace ImGui;
const struct spu2_core* core = &iris->ps2->spu2->c[c];
bool* mute = c ? iris->core1_mute : iris->core0_mute;
int* solo = c ? &iris->core1_solo : &iris->core0_solo;
int* selected = c ? &core1_selected : &core0_selected;
Text("IRQ address: %08x", core->irqa);
char id[] = "##spu2coreX";
id[10] = '0' + c;
if (BeginTable(id, 7, ImGuiTableFlags_RowBg)) {
PushFont(iris->font_small_code);
TableSetupColumn("Voice");
TableSetupColumn("ENVX");
TableSetupColumn("NAX");
TableSetupColumn("SSA");
TableSetupColumn("LSAX");
TableSetupColumn("Stage");
TableSetupColumn("Cycles");
TableHeadersRow();
PopFont();
for (int i = 0; i < 24; i++) {
TableNextRow();
TableSetColumnIndex(0);
char voice[9]; sprintf(voice, "Voice %d", i);
if (Selectable(voice, i == *selected)) {
if (i == *selected) {
*selected = -1;
} else {
*selected = i;
}
}
TableSetColumnIndex(1);
PushFont(iris->font_code);
Text("%04x", core->v[i].envx);
TableSetColumnIndex(2);
Text("%08x", core->v[i].nax);
TableSetColumnIndex(3);
Text("%08x", core->v[i].ssa);
TableSetColumnIndex(4);
Text("%08x", core->v[i].lsax);
TableSetColumnIndex(5);
PopFont();
Text("%s", get_adsr_stage_name(core->v[i].adsr_phase));
TableSetColumnIndex(6);
Text("%d", core->v[i].adsr_cycles);
}
EndTable();
}
}
void show_spu2_tab(iris::instance* iris, int c) {
using namespace ImGui;
const struct spu2_core* core = &iris->ps2->spu2->c[c];
show_spu2_core(iris, c);
Separator();
int* selected = c ? &core1_selected : &core0_selected;
if (*selected == -1) {
Text("No voice selected");
return;
}
if (adsr_index == 119) {
for (int i = 0; i < 119; i++)
selected_adsr[i] = selected_adsr[i+1];
selected_adsr[119] = core->v[*selected].envx / 32767.0f;
} else {
selected_adsr[adsr_index++] = core->v[*selected].envx / 32767.0f;
}
PlotLines("ADSR", selected_adsr, IM_ARRAYSIZE(selected_adsr), 0, NULL, 0.0f, 1.0f, { 250.0, 80.0 });
}
void show_spu2_debugger(iris::instance* iris) {
using namespace ImGui;
const struct ps2_spu2* spu2 = iris->ps2->spu2;
if (imgui::BeginEx("SPU2", &iris->show_spu2_debugger)) {
if (BeginTabBar("##spu2tabbar")) {
if (BeginTabItem("CORE0")) {
show_spu2_tab(iris, 0);
EndTabItem();
}
if (BeginTabItem("CORE1")) {
show_spu2_tab(iris, 1);
EndTabItem();
}
EndTabBar();
}
} End();
}
}
================================================
FILE: frontend/ui/state.cpp
================================================
#include
#include
#include
#include "iris.hpp"
#include "res/IconsMaterialSymbols.h"
#include "ee/ee_dis.h"
#include "ee/ee_def.hpp"
#include "ee/vu_def.hpp"
#include "iop/iop_dis.h"
#define IM_RGB(r, g, b) ImVec4(((float)r / 255.0f), ((float)g / 255.0f), ((float)b / 255.0f), 1.0)
namespace iris {
static const char* ee_cop0_r[] = {
"Index",
"Random",
"EntryLo0",
"EntryLo1",
"Context",
"PageMask",
"Wired",
"Unused7",
"BadVAddr",
"Count",
"EntryHi",
"Compare",
"Status",
"Cause",
"EPC",
"PRId",
"Config",
"Unused17",
"Unused18",
"Unused19",
"Unused20",
"Unused21",
"Unused22",
"BadPAddr",
"Debug",
"Perf",
"Unused26",
"Unused27",
"TagLo",
"TagHi",
"ErrorEPC",
"Unused31"
};
static const char* mips_cc_r[] = {
"r0", "at", "v0", "v1", "a0", "a1", "a2", "a3",
"t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
"s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
"t8", "t9", "k0", "k1", "gp", "sp", "fp", "ra"
};
static const char* vu0i_regs[] = {
"vi00",
"vi01",
"vi02",
"vi03",
"vi04",
"vi05",
"vi06",
"vi07",
"vi08",
"vi09",
"vi10",
"vi11",
"vi12",
"vi13",
"vi14",
"vi15",
"Status",
"MAC",
"Clip",
"Reserved",
"R",
"I",
"Q",
"Reserved",
"Reserved",
"Reserved",
"TPC",
"CMSAR0",
"FBRST",
"VPUSTAT",
"Reserved",
"CMSAR1"
};
uint128_t ee_prev[32];
uint128_t ee_frames[32];
uint32_t ee_cop0_prev[32];
uint32_t ee_cop0_frames[32];
uint32_t vu0i_prev[32];
uint32_t vu0i_frames[32];
uint32_t ee_fpu_prev[32];
uint32_t ee_fpu_frames[32];
struct vu_reg128 vu0f_prev[32];
uint128_t vu0f_frames[32];
uint32_t iop_prev[32];
uint32_t iop_frames[32];
bool vu0f_float;
static ImGuiTableFlags ee_table_sizing = ImGuiTableFlags_SizingStretchSame;
static ImGuiTableFlags iop_table_sizing = ImGuiTableFlags_SizingStretchProp;
static inline void show_ee_main_registers(iris::instance* iris) {
using namespace ImGui;
struct ee_state* ee = iris->ps2->ee;
for (int i = 0; i < 32; i++) {
for (int j = 0; j < 4; j++) {
if (ee_prev[i].u32[j] != ee->r[i].u32[j])
ee_frames[i].u32[j] = 60;
}
ee_prev[i] = ee->r[i];
}
PushFont(iris->font_code);
if (BeginTable("ee#registers", 5, ImGuiTableFlags_RowBg | ee_table_sizing)) {
PushFont(iris->font_small_code);
TableSetupColumn("Reg");
TableSetupColumn("96-127");
TableSetupColumn("64-95");
TableSetupColumn("32-63");
TableSetupColumn("0-31");
TableHeadersRow();
PopFont();
for (int i = 0; i < 32; i++) {
TableNextRow();
TableSetColumnIndex(0);
Text("%s", mips_cc_r[i]);
for (int j = 0; j < 4; j++) {
float a = (float)ee_frames[i].u32[j] / 60.0f;
TableSetColumnIndex(1+(3-j));
char label[5]; sprintf(label, "##%02x", (i << 2) | j);
if (Selectable(label, false, ImGuiSelectableFlags_AllowOverlap)) {
OpenPopup("Change register value");
} SameLine(0.0, 0.0);
if (BeginPopupContextItem(NULL, ImGuiPopupFlags_MouseButtonLeft)) {
static char new_value[9];
PushFont(iris->font_small_code);
TextDisabled("Edit "); SameLine(0.0, 0.0);
Text("%s", mips_cc_r[i]); SameLine(0.0, 0.0);
TextDisabled(" (bits %d-%d)", j*32, ((j+1)*32)-1);
PopFont();
PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0, 2.0));
PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0, 8.0));
PushFont(iris->font_body);
PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.35, 0.35, 0.35, 0.35));
AlignTextToFramePadding();
Text(ICON_MS_EDIT); SameLine();
SetNextItemWidth(100);
PushFont(iris->font_code);
if (InputText("##", new_value, 9, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue)) {
if (new_value[0])
ee->r[i].u32[j] = strtoul(new_value, NULL, 16);
CloseCurrentPopup();
}
PopFont();
if (Button("Change")) {
if (new_value[0])
ee->r[i].u32[j] = strtoul(new_value, NULL, 16);
CloseCurrentPopup();
} SameLine();
if (Button("Cancel"))
CloseCurrentPopup();
PopStyleColor();
PopStyleVar(2);
PopFont();
EndPopup();
}
TextColored(ImVec4(0.6+a, 0.6, 0.6, 1.0), "%08x", ee->r[i].u32[j]);
}
}
}
EndTable();
PopFont();
}
static inline void show_ee_cop0_registers(iris::instance* iris) {
using namespace ImGui;
struct ee_state* ee = iris->ps2->ee;
for (int i = 0; i < 32; i++) {
if (ee_cop0_prev[i] != ee->cop0_r[i])
ee_cop0_frames[i] = 60;
ee_cop0_prev[i] = ee->cop0_r[i];
}
PushFont(iris->font_code);
if (BeginTable("ee#cop0registers", 5, ImGuiTableFlags_RowBg | ee_table_sizing)) {
PushFont(iris->font_small_code);
TableSetupColumn("Reg");
TableSetupColumn("96-127");
TableSetupColumn("64-95");
TableSetupColumn("32-63");
TableSetupColumn("0-31");
TableHeadersRow();
PopFont();
for (int i = 0; i < 32; i++) {
float a = (float)ee_cop0_frames[i] / 60.0f;
TableNextRow();
TableSetColumnIndex(0);
Text("%s", ee_cop0_r[i]);
TableSetColumnIndex(4);
char label[8]; sprintf(label, "##e%d", (i << 2));
if (Selectable(label, false, ImGuiSelectableFlags_AllowOverlap)) {
OpenPopup("Change register value");
} SameLine(0.0, 0.0);
if (BeginPopupContextItem(NULL, ImGuiPopupFlags_MouseButtonLeft)) {
static char new_value[9];
PushFont(iris->font_small_code);
TextDisabled("Edit "); SameLine(0.0, 0.0);
Text("%s", ee_cop0_r[i]);
PopFont();
PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0, 2.0));
PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0, 8.0));
PushFont(iris->font_body);
PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.35, 0.35, 0.35, 0.35));
AlignTextToFramePadding();
Text(ICON_MS_EDIT); SameLine();
SetNextItemWidth(100);
PushFont(iris->font_code);
if (InputText("##", new_value, 9, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue)) {
if (new_value[0])
ee->cop0_r[i] = strtoul(new_value, NULL, 16);
CloseCurrentPopup();
}
PopFont();
if (Button("Change")) {
if (new_value[0])
ee->cop0_r[i] = strtoul(new_value, NULL, 16);
CloseCurrentPopup();
} SameLine();
if (Button("Cancel"))
CloseCurrentPopup();
PopStyleColor();
PopStyleVar(2);
PopFont();
EndPopup();
}
TextColored(ImVec4(0.6+a, 0.6, 0.6, 1.0), "%08x", ee->cop0_r[i]);
}
}
EndTable();
PopFont();
}
static inline void show_ee_fpu_registers(iris::instance* iris) {
using namespace ImGui;
struct ee_state* ee = iris->ps2->ee;
for (int i = 0; i < 32; i++) {
if (ee_fpu_prev[i] != ee->f[i].u32)
ee_fpu_frames[i] = 60;
ee_fpu_prev[i] = ee->f[i].u32;
}
PushFont(iris->font_code);
if (BeginTable("ee#fpuregisters", 3, ImGuiTableFlags_RowBg | ee_table_sizing)) {
PushFont(iris->font_small_code);
TableSetupColumn("Reg");
TableSetupColumn("u32");
TableSetupColumn("float");
TableHeadersRow();
PopFont();
for (int i = 0; i < 32; i++) {
float a = (float)ee_fpu_frames[i] / 60.0f;
TableNextRow();
TableSetColumnIndex(0);
Text("f%-2d", i);
TableSetColumnIndex(1);
char label1[8]; sprintf(label1, "##u%d", i);
if (Selectable(label1, false, ImGuiSelectableFlags_AllowOverlap)) {
OpenPopup("Change register value");
} SameLine(0.0, 0.0);
if (BeginPopupContextItem(NULL, ImGuiPopupFlags_MouseButtonLeft)) {
static char new_value[9];
PushFont(iris->font_small_code);
TextDisabled("Edit "); SameLine(0.0, 0.0);
Text("f%d ", i); SameLine(0.0, 0.0);
TextDisabled("(as u32)");
PopFont();
PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0, 2.0));
PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0, 8.0));
PushFont(iris->font_body);
PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.35, 0.35, 0.35, 0.35));
AlignTextToFramePadding();
Text(ICON_MS_EDIT); SameLine();
SetNextItemWidth(100);
PushFont(iris->font_code);
if (InputText("##", new_value, 9, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue)) {
if (new_value[0])
ee->f[i].u32 = strtoul(new_value, NULL, 16);
CloseCurrentPopup();
}
PopFont();
if (Button("Change")) {
if (new_value[0])
ee->f[i].u32 = strtoul(new_value, NULL, 16);
CloseCurrentPopup();
} SameLine();
if (Button("Cancel"))
CloseCurrentPopup();
PopStyleColor();
PopStyleVar(2);
PopFont();
EndPopup();
}
TextColored(ImVec4(0.6+a, 0.6, 0.6, 1.0), "%08x", ee->f[i].u32);
TableSetColumnIndex(2);
char label2[8]; sprintf(label2, "##f%d", i);
if (Selectable(label2, false, ImGuiSelectableFlags_AllowOverlap)) {
OpenPopup("Change register value");
} SameLine(0.0, 0.0);
if (BeginPopupContextItem(NULL, ImGuiPopupFlags_MouseButtonLeft)) {
static char new_value[9];
PushFont(iris->font_small_code);
TextDisabled("Edit "); SameLine(0.0, 0.0);
Text("f%d ", i); SameLine(0.0, 0.0);
TextDisabled("(as float)");
PopFont();
PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0, 2.0));
PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0, 8.0));
PushFont(iris->font_body);
PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.35, 0.35, 0.35, 0.35));
AlignTextToFramePadding();
Text(ICON_MS_EDIT); SameLine();
SetNextItemWidth(100);
PushFont(iris->font_code);
if (InputText("##", new_value, 9, ImGuiInputTextFlags_CharsScientific | ImGuiInputTextFlags_EnterReturnsTrue)) {
if (new_value[0])
ee->f[i].f = strtof(new_value, NULL);
CloseCurrentPopup();
}
PopFont();
if (Button("Change")) {
if (new_value[0])
ee->f[i].f = strtof(new_value, NULL);
CloseCurrentPopup();
} SameLine();
if (Button("Cancel"))
CloseCurrentPopup();
PopStyleColor();
PopStyleVar(2);
PopFont();
EndPopup();
}
TextColored(ImVec4(0.6+a, 0.6, 0.6, 1.0), "%.7f", ee->f[i].f);
}
}
EndTable();
PopFont();
}
static inline void show_vu0_float(iris::instance* iris) {
using namespace ImGui;
struct vu_state* vu0 = iris->ps2->ee->vu0;
for (int i = 0; i < 32; i++) {
for (int j = 0; j < 4; j++) {
if (vu0f_prev[i].u32[j] != vu0->vf[i].u32[j])
vu0f_frames[i].u32[j] = 60;
}
vu0f_prev[i] = vu0->vf[i];
}
PushFont(iris->font_code);
if (BeginTable("ee#registers", 5, ImGuiTableFlags_RowBg | ee_table_sizing)) {
PushFont(iris->font_small_code);
TableSetupColumn("Reg");
TableSetupColumn("W");
TableSetupColumn("Z");
TableSetupColumn("Y");
TableSetupColumn("X");
TableHeadersRow();
PopFont();
for (int i = 0; i < 32; i++) {
TableNextRow();
TableSetColumnIndex(0);
Text("vf%02d", i);
for (int j = 0; j < 4; j++) {
float a = (float)vu0f_frames[i].u32[j] / 60.0f;
TableSetColumnIndex(1+(3-j));
char label[5]; sprintf(label, "##%02x", (i << 2) | j);
if (Selectable(label, false, ImGuiSelectableFlags_AllowOverlap)) {
OpenPopup("Change register value");
} SameLine(0.0, 0.0);
if (BeginPopupContextItem(NULL, ImGuiPopupFlags_MouseButtonLeft)) {
static char new_value[9];
PushFont(iris->font_small_code);
TextDisabled("Edit "); SameLine(0.0, 0.0);
Text("vf%02d", i); SameLine(0.0, 0.0);
TextDisabled(" (%c field)", "XYZW"[j]);
PopFont();
PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0, 2.0));
PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0, 8.0));
PushFont(iris->font_body);
PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.35, 0.35, 0.35, 0.35));
AlignTextToFramePadding();
Text(ICON_MS_EDIT); SameLine();
SetNextItemWidth(100);
PushFont(iris->font_code);
if (vu0f_float) {
if (InputText("##", new_value, 9, ImGuiInputTextFlags_CharsScientific | ImGuiInputTextFlags_EnterReturnsTrue)) {
if (new_value[0])
vu0->vf[i].f[j] = strtof(new_value, NULL);
CloseCurrentPopup();
}
} else {
if (InputText("##", new_value, 9, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue)) {
if (new_value[0])
vu0->vf[i].u32[j] = strtoul(new_value, NULL, 16);
CloseCurrentPopup();
}
}
PopFont();
if (Button("Change")) {
if (vu0f_float) {
if (new_value[0])
vu0->vf[i].f[j] = strtof(new_value, NULL);
} else {
if (new_value[0])
vu0->vf[i].u32[j] = strtoul(new_value, NULL, 16);
}
CloseCurrentPopup();
} SameLine();
if (Button("Cancel"))
CloseCurrentPopup();
PopStyleColor();
PopStyleVar(2);
PopFont();
EndPopup();
}
if (vu0f_float) {
TextColored(ImVec4(0.6+a, 0.6, 0.6, 1.0), "%.7f", vu0->vf[i].f[j]);
} else {
TextColored(ImVec4(0.6+a, 0.6, 0.6, 1.0), "%08x", vu0->vf[i].u32[j]);
}
}
}
EndTable();
}
PopFont();
}
static inline void show_vu0_integer(iris::instance* iris) {
using namespace ImGui;
struct vu_state* vu0 = iris->ps2->ee->vu0;
for (int i = 0; i < 32; i++) {
if (vu0i_prev[i] != (i < 16 ? vu0->vi[i] : vu0->cr[i-16]))
vu0i_frames[i] = 60;
vu0i_prev[i] = i < 16 ? vu0->vi[i] : vu0->cr[i-16];
}
PushFont(iris->font_code);
if (BeginTable("ee#cop0registers", 5, ImGuiTableFlags_RowBg | ee_table_sizing)) {
PushFont(iris->font_small_code);
TableSetupColumn("Reg");
TableSetupColumn("96-127");
TableSetupColumn("64-95");
TableSetupColumn("32-63");
TableSetupColumn("0-31");
TableHeadersRow();
PopFont();
for (int i = 0; i < 32; i++) {
float a = (float)vu0i_frames[i] / 60.0f;
TableNextRow();
TableSetColumnIndex(0);
Text("%s", vu0i_regs[i]);
TableSetColumnIndex(4);
char label[8]; sprintf(label, "##e%d", (i << 2));
if (Selectable(label, false, ImGuiSelectableFlags_AllowOverlap)) {
OpenPopup("Change register value");
} SameLine(0.0, 0.0);
if (BeginPopupContextItem(NULL, ImGuiPopupFlags_MouseButtonLeft)) {
static char new_value[9];
PushFont(iris->font_small_code);
TextDisabled("Edit "); SameLine(0.0, 0.0);
Text("%s", vu0i_regs[i]);
PopFont();
PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0, 2.0));
PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0, 8.0));
PushFont(iris->font_body);
PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.35, 0.35, 0.35, 0.35));
AlignTextToFramePadding();
Text(ICON_MS_EDIT); SameLine();
SetNextItemWidth(100);
PushFont(iris->font_code);
if (InputText("##", new_value, 9, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue)) {
if (new_value[0]) {
if (i < 16) {
vu0->vi[i] = strtoul(new_value, NULL, 16);
} else {
vu0->cr[i-16] = strtoul(new_value, NULL, 16);
}
}
CloseCurrentPopup();
}
PopFont();
if (Button("Change")) {
if (i < 16) {
vu0->vi[i] = strtoul(new_value, NULL, 16);
} else {
vu0->cr[i-16] = strtoul(new_value, NULL, 16);
}
CloseCurrentPopup();
} SameLine();
if (Button("Cancel"))
CloseCurrentPopup();
PopStyleColor();
PopStyleVar(2);
PopFont();
EndPopup();
}
TextColored(ImVec4(0.6+a, 0.6, 0.6, 1.0), "%08x", i < 16 ? vu0->vi[i] : vu0->cr[i-16]);
}
}
EndTable();
PopFont();
}
static inline void show_iop_main_registers(iris::instance* iris) {
using namespace ImGui;
PushFont(iris->font_code);
struct iop_state* iop = iris->ps2->iop;
for (int i = 0; i < 32; i++) {
if (iop_prev[i] != iop->r[i])
iop_frames[i] = 60;
iop_prev[i] = iop->r[i];
}
if (BeginTable("table3", 4, ImGuiTableFlags_RowBg | iop_table_sizing)) {
for (int i = 0; i < 32;) {
TableNextRow();
for (int x = 0; (x < 4) && (i < 32); x++) {
float a = (float)iop_frames[i] / 60.0f;
char id[5]; sprintf(id, "##%02u", i);
TableSetColumnIndex(x);
if (Selectable(id, false, ImGuiSelectableFlags_AllowOverlap)) {
OpenPopup("Change register value");
} SameLine(0.0, 0.0);
if (BeginPopupContextItem(NULL, ImGuiPopupFlags_MouseButtonLeft)) {
static char new_value[9];
PushFont(iris->font_small_code);
TextDisabled("Edit %s", mips_cc_r[i]);
PopFont();
PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0, 2.0));
PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0, 8.0));
PushFont(iris->font_body);
PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.35, 0.35, 0.35, 0.35));
AlignTextToFramePadding();
Text(ICON_MS_EDIT); SameLine();
SetNextItemWidth(100);
PushFont(iris->font_code);
if (InputText("##", new_value, 9, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue)) {
if (new_value[0])
iop->r[i] = strtoul(new_value, NULL, 16);
CloseCurrentPopup();
}
PopFont();
if (Button("Change")) {
if (new_value[0])
iop->r[i] = strtoul(new_value, NULL, 16);
CloseCurrentPopup();
} SameLine();
if (Button("Cancel"))
CloseCurrentPopup();
PopStyleColor();
PopStyleVar(2);
PopFont();
EndPopup();
}
SameLine(0.0, 0.0);
Text("%2s", mips_cc_r[i]); SameLine();
TextColored(ImVec4(0.6 + a, 0.6, 0.6, 1.0), "%08x", iop->r[i]);
++i;
}
}
EndTable();
}
PopFont();
}
static inline void show_work_in_progress(iris::instance* iris) {
using namespace ImGui;
Text("Work-in-progress!");
}
static const char* sizing_combo_items[] = {
ICON_MS_FIT_WIDTH " Fixed fit",
ICON_MS_FULLSCREEN " Fixed same",
ICON_MS_FULLSCREEN " Stretch prop",
ICON_MS_FULLSCREEN " Stretch same"
};
static ImGuiTableFlags table_sizing_flags[] = {
ImGuiTableFlags_SizingFixedFit,
ImGuiTableFlags_SizingFixedSame,
ImGuiTableFlags_SizingStretchProp,
ImGuiTableFlags_SizingStretchSame
};
static int ee_sizing_combo = 3;
static int iop_sizing_combo = 0;
static const char* ee_reg_group_names[] = {
"Main",
"COP0",
"FPU",
"VU0f",
"VU0i"
};
static int ee_reg_group = 0;
void show_ee_state(iris::instance* iris) {
using namespace ImGui;
if (imgui::BeginEx("EE state", &iris->show_ee_state, ImGuiWindowFlags_MenuBar)) {
if (BeginMenuBar()) {
if (BeginMenu("Settings")) {
if (BeginMenu(ICON_MS_CROP " Sizing")) {
for (int i = 0; i < 4; i++) {
if (Selectable(sizing_combo_items[i], i == ee_sizing_combo)) {
ee_table_sizing = table_sizing_flags[i];
ee_sizing_combo = i;
}
}
ImGui::EndMenu();
}
if (MenuItem("Display VU0f as floats", nullptr, &vu0f_float));
ImGui::EndMenu();
}
EndMenuBar();
}
if (BeginTable("table4", 5, ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_BordersInnerV)) {
TableNextRow();
for (int i = 0; i < 5; i++) {
TableSetColumnIndex(i);
if (Selectable(ee_reg_group_names[i], ee_reg_group == i)) {
ee_reg_group = i;
}
}
EndTable();
}
if (BeginChild("ee#child")) {
switch (ee_reg_group) {
case 0: {
show_ee_main_registers(iris);
} break;
case 1: {
show_ee_cop0_registers(iris);
} break;
case 2: {
show_ee_fpu_registers(iris);
} break;
case 3: {
show_vu0_float(iris);
} break;
case 4: {
show_vu0_integer(iris);
} break;
}
EndChild();
}
} End();
for (int i = 0; i < 32; i++) {
if (ee_fpu_frames[i])
ee_fpu_frames[i]--;
if (ee_cop0_frames[i])
ee_cop0_frames[i]--;
if (vu0i_frames[i])
vu0i_frames[i]--;
for (int j = 0; j < 4; j++)
if (ee_frames[i].u32[j])
ee_frames[i].u32[j]--;
for (int j = 0; j < 4; j++)
if (vu0f_frames[i].u32[j])
vu0f_frames[i].u32[j]--;
}
}
void show_iop_state(iris::instance* iris) {
using namespace ImGui;
if (imgui::BeginEx("IOP state", &iris->show_iop_state, ImGuiWindowFlags_MenuBar)) {
if (BeginMenuBar()) {
if (BeginMenu("Settings")) {
if (BeginMenu(ICON_MS_CROP " Sizing")) {
for (int i = 0; i < 4; i++) {
if (Selectable(sizing_combo_items[i], i == iop_sizing_combo)) {
iop_table_sizing = table_sizing_flags[i];
iop_sizing_combo = i;
}
}
ImGui::EndMenu();
}
ImGui::EndMenu();
}
EndMenuBar();
}
if (BeginChild("iop#child")) {
show_iop_main_registers(iris);
EndChild();
}
} End();
for (int i = 0; i < 32; i++)
if (iop_frames[i])
iop_frames[i]--;
}
}
================================================
FILE: frontend/ui/statusbar.cpp
================================================
#include "imgui_internal.h"
#include "iris.hpp"
#include "res/IconsMaterialSymbols.h"
namespace ImGui {
bool BeginMainStatusBar()
{
ImGuiContext& g = *GetCurrentContext();
ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)GetMainViewport();
// Notify of viewport change so GetFrameHeight() can be accurate in case of DPI change
SetCurrentViewport(NULL, viewport);
// For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set.
// FIXME: This could be generalized as an opt-in way to clamp window->DC.CursorStartPos to avoid SafeArea?
// FIXME: Consider removing support for safe area down the line... it's messy. Nowadays consoles have support for TV calibration in OS settings.
g.NextWindowData.MenuBarOffsetMinVal = ImVec2(g.Style.DisplaySafeAreaPadding.x, ImMax(g.Style.DisplaySafeAreaPadding.y - g.Style.FramePadding.y, 0.0f));
ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar;
float height = GetFrameHeight();
bool is_open = BeginViewportSideBar("##MainStatusBar", viewport, ImGuiDir_Down, height, window_flags);
g.NextWindowData.MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f);
if (is_open)
BeginMenuBar();
else
End();
return is_open;
}
void EndMainStatusBar()
{
EndMenuBar();
// When the user has left the menu layer (typically: closed menus through activation of an item), we restore focus to the previous window
// FIXME: With this strategy we won't be able to restore a NULL focus.
ImGuiContext& g = *GImGui;
if (g.CurrentWindow == g.NavWindow && g.NavLayer == ImGuiNavLayer_Main && !g.NavAnyRequest)
FocusTopMostWindowUnderOne(g.NavWindow, NULL, NULL, ImGuiFocusRequestFlags_UnlessBelowModal | ImGuiFocusRequestFlags_RestoreFocusedChild);
End();
}
}
namespace iris {
int get_format_bpp(VkFormat fmt) {
switch (fmt) {
case GS_PSMCT32: return 32;
case GS_PSMCT24: return 24;
case GS_PSMCT16:
case GS_PSMCT16S: return 16;
}
return 0;
}
void show_status_bar(iris::instance* iris) {
using namespace ImGui;
if (BeginMainStatusBar()) {
static const char* const modes[] = {
"Progressive",
"Interlaced (Field)",
"Progressive",
"Interlaced (Frame)"
};
static const char* const renderers[] = {
"Null",
"Software",
"Hardware (Vulkan)"
};
int dispfb = 0;
char buf[128];
sprintf(buf, "%s", emu::get_current_system_name(iris));
float width = CalcTextSize(buf).x;
ImVec4 col = GetStyleColorVec4(ImGuiCol_Text);
col.w = 0.2;
PushStyleColor(ImGuiCol_Separator, col);
PushStyleVarX(ImGuiStyleVar_ItemSpacing, 4.0f);
SetCursorPosX(5.0);
if (!iris->image.image) {
Text("%s", renderers[iris->renderer_backend]);
SeparatorEx(ImGuiSeparatorFlags_Vertical);
Text("No image");
SeparatorEx(ImGuiSeparatorFlags_Vertical);
Text("%.1f fps", GetIO().Framerate);
SetCursorPosX(GetWindowWidth() - width - 5);
Text(buf);
} else {
// | %dx%d | %dx%d | %s | %dbpp | %.1f fps",
Text("%s", renderers[iris->renderer_backend]);
SeparatorEx(ImGuiSeparatorFlags_Vertical);
Text("%dx%d", iris->render_width, iris->render_height);
SeparatorEx(ImGuiSeparatorFlags_Vertical);
Text("%dx%d", iris->image.width, iris->image.height);
SeparatorEx(ImGuiSeparatorFlags_Vertical);
Text("%s", modes[iris->ps2->gs->smode2 & 3]);
SeparatorEx(ImGuiSeparatorFlags_Vertical);
Text("%.1f fps", GetIO().Framerate);
SetCursorPosX(GetWindowWidth() - width - 5);
Text(buf);
}
PopStyleColor();
PopStyleVar();
// if (vp_w) {
// Text(ICON_MS_MONITOR " %s | %dx%d | %dx%d | %s | %dbpp | %.1f fps",
// "None", // renderer_get_name(iris->ctx),
// disp_w, disp_h,
// vp_w, vp_h,
// mode == 3 ? "Interlaced" : "Progressive",
// get_format_bpp(disp_fmt),
// GetIO().Framerate
// );
// // iris->avg_frames++;
// // iris->avg_fps += iris->fps;
// } else {
// Text(ICON_MS_MONITOR " %s | No image",
// "None" // renderer_get_name(iris->ctx)
// );
// }
EndMainStatusBar();
}
}
}
================================================
FILE: frontend/ui/symbols.cpp
================================================
#include
#include
#include
#include
#include
#include "iris.hpp"
#include "res/IconsMaterialSymbols.h"
namespace iris {
static const char* symbols_sizing_combo_items[] = {
ICON_MS_FIT_WIDTH " Fixed fit",
ICON_MS_FULLSCREEN " Fixed same",
ICON_MS_FULLSCREEN " Stretch prop",
ICON_MS_FULLSCREEN " Stretch same"
};
static ImGuiTableFlags symbols_table_sizing_flags[] = {
ImGuiTableFlags_SizingFixedFit,
ImGuiTableFlags_SizingFixedSame,
ImGuiTableFlags_SizingStretchProp,
ImGuiTableFlags_SizingStretchSame
};
static int symbols_table_sizing_combo = 0;
static int symbols_table_sizing = ImGuiTableFlags_SizingStretchProp;
std::vector symbols_list;
bool regex = false;
bool case_sensitive = false;
bool autosearch = true;
void filter_symbols(iris::instance* iris, const char* filter, bool regex, bool case_sensitive) {
symbols_list.clear();
if (filter[0] == '\0') {
symbols_list = iris->symbols;
return;
}
std::string filter_str(filter);
for (const iris::elf_symbol& sym : iris->symbols) {
if (regex) {
std::regex r(filter_str, std::regex::ECMAScript | (case_sensitive ? std::regex_constants::syntax_option_type(0) : std::regex::icase));
if (std::regex_match(sym.name, r)) {
symbols_list.push_back(sym);
}
} else {
std::string sym_name_str(sym.name);
if (!case_sensitive) {
std::transform(sym_name_str.begin(), sym_name_str.end(), sym_name_str.begin(), tolower);
std::transform(filter_str.begin(), filter_str.end(), filter_str.begin(), tolower);
}
auto it = sym_name_str.find(filter_str);
if (it != std::string::npos) {
symbols_list.push_back(sym);
}
}
}
}
int edit_callback(ImGuiInputTextCallbackData* data) {
iris::instance* iris = (iris::instance*)data->UserData;
filter_symbols(iris, data->Buf, regex, case_sensitive);
return 0;
}
void show_symbols(iris::instance* iris) {
using namespace ImGui;
if (imgui::BeginEx("Symbols", &iris->show_symbols, ImGuiWindowFlags_MenuBar)) {
if (BeginMenuBar()) {
if (BeginMenu("Settings")) {
if (BeginMenu(ICON_MS_CROP " Sizing")) {
for (int i = 0; i < 4; i++) {
if (Selectable(symbols_sizing_combo_items[i], i == symbols_table_sizing_combo)) {
symbols_table_sizing = symbols_table_sizing_flags[i];
symbols_table_sizing_combo = i;
}
}
ImGui::EndMenu();
}
MenuItem(ICON_MS_SEARCH_CHECK " Auto search", NULL, &autosearch);
ImGui::EndMenu();
}
EndMenuBar();
}
static char buf[512];
SetNextItemWidth(200.0f);
ImGuiInputFlags flags = ImGuiInputTextFlags_EnterReturnsTrue;
if (autosearch) {
flags |= ImGuiInputTextFlags_CallbackEdit;
}
if (InputTextWithHint("##search", "Search symbols...", buf, 512, flags, edit_callback, (void*)iris)) {
filter_symbols(iris, buf, regex, case_sensitive);
} SameLine();
if (Button(ICON_MS_SEARCH)) {
filter_symbols(iris, buf, regex, case_sensitive);
} SameLine();
if (BeginPopupContextItem("symbols_settings")) {
if (MenuItem(ICON_MS_REGULAR_EXPRESSION " Regex mode", NULL, ®ex)) {
filter_symbols(iris, buf, regex, case_sensitive);
}
if (MenuItem(ICON_MS_MATCH_CASE " Case-sensitive", NULL, &case_sensitive)) {
filter_symbols(iris, buf, regex, case_sensitive);
}
EndPopup();
}
if (Button(ICON_MS_SETTINGS, ImVec2(50, 0))) {
OpenPopup("symbols_settings");
}
int text_height = GetTextLineHeightWithSpacing();
ImVec2 outer_size = ImVec2(0, GetContentRegionAvail().y - text_height);
ImGuiTableFlags table_flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_Sortable | ImGuiTableFlags_Hideable | ImGuiTableFlags_ScrollY;
table_flags |= symbols_table_sizing;
if (BeginTable("##symbolstb", 3, table_flags, outer_size)) {
if (ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs()) {
if (sort_specs->SpecsDirty) {
if (buf[0] == '\0') {
symbols_list = iris->symbols;
}
// Sort by symbol
if (sort_specs->Specs->ColumnIndex == 0) {
std::sort(symbols_list.begin(), symbols_list.end(), [=](const iris::elf_symbol& a, const iris::elf_symbol& b) {
return sort_specs->Specs->SortDirection == ImGuiSortDirection_Ascending ? std::string(a.name) < std::string(b.name) : std::string(a.name) > std::string(b.name);
});
}
// Sort by address
if (sort_specs->Specs->ColumnIndex == 1) {
std::sort(symbols_list.begin(), symbols_list.end(), [=](const iris::elf_symbol& a, const iris::elf_symbol& b) {
return sort_specs->Specs->SortDirection == ImGuiSortDirection_Ascending ? a.addr < b.addr : a.addr > b.addr;
});
}
// Sort by size
if (sort_specs->Specs->ColumnIndex == 2) {
std::sort(symbols_list.begin(), symbols_list.end(), [=](const iris::elf_symbol& a, const iris::elf_symbol& b) {
return sort_specs->Specs->SortDirection == ImGuiSortDirection_Ascending ? a.size < b.size : a.size > b.size;
});
}
sort_specs->SpecsDirty = false;
}
}
TableSetupScrollFreeze(0, 1);
TableSetupColumn("Symbol");
TableSetupColumn("Address");
TableSetupColumn("Size");
PushFont(iris->font_small_code);
TableHeadersRow();
PopFont();
PushFont(iris->font_code);
int index = 0;
for (const iris::elf_symbol& symbol : symbols_list) {
TableNextRow();
TableSetColumnIndex(0);
Text("%s", symbol.name);
TableSetColumnIndex(1);
Text("%08x", symbol.addr);
TableSetColumnIndex(2);
char label[64];
sprintf(label, "%zu##%d", symbol.size, index++);
if (Selectable(label, false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowDoubleClick)) {
if (IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
iris->show_ee_control = true;
iris->ee_control_follow_pc = false;
iris->ee_control_address = symbol.addr;
}
}
if (BeginPopupContextItem()) {
PushFont(iris->font_small_code);
TextDisabled("%s", symbol.name);
PopFont();
PushFont(iris->font_body);
if (Selectable(ICON_MS_ARROW_FORWARD " Go to this address")) {
iris->show_ee_control = true;
iris->ee_control_follow_pc = false;
iris->ee_control_address = symbol.addr;
}
if (Selectable(ICON_MS_ADD_CIRCLE " Set a breakpoint here")) {
breakpoint b;
b.addr = symbol.addr;
b.cond_r = false;
b.cond_w = false;
b.cond_x = true;
b.cpu = BKPT_CPU_EE;
b.size = 4;
b.enabled = true;
iris->breakpoints.push_back(b);
}
PopFont();
EndPopup();
}
}
PopFont();
EndTable();
}
Text("%zu ", symbols_list.size()); SameLine(0.0, 0.0);
TextDisabled("symbols found "); SameLine(0.0, 0.0); Text("| "); SameLine(0.0, 0.0);
TextDisabled("%s ", regex ? "Regex mode" : "Normal mode"); SameLine(0.0, 0.0); Text("| "); SameLine(0.0, 0.0);
TextDisabled("%s ", case_sensitive ? "Case-sensitive" : "Case-insensitive");
} End();
}
}
================================================
FILE: frontend/ui/threads.cpp
================================================
#include
#include
#include
#include
#include
#include "iris.hpp"
#include "ee/ee_def.hpp"
#include "res/IconsMaterialSymbols.h"
namespace iris {
static inline const char* get_status_string(int status) {
switch (status) {
case THS_RUN: return "RUN";
case THS_READY: return "READY";
case THS_WAIT: return "WAIT";
case THS_SUSPEND: return "SUSPEND";
case THS_WAITSUSPEND: return "WAIT/SUSPEND";
case THS_DORMANT: return "DORMANT";
}
return "";
}
static const char* get_entry_symbol(iris::instance* iris, uint32_t addr) {
// Look up the address in the symbol table
if (addr == 0x81fc0) return "EE Idle Thread";
for (const iris::elf_symbol& sym : iris->symbols) {
if ((sym.addr >= addr) && (sym.addr < (addr + sym.size))) {
return sym.name;
}
}
return nullptr;
}
void show_thread_list(iris::instance* iris) {
using namespace ImGui;
struct ee_state* ee = iris->ps2->ee;
ImGuiTableFlags table_flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_Sortable | ImGuiTableFlags_Hideable | ImGuiTableFlags_ScrollY;
if (BeginTable("##threadlist", 5, table_flags)) {
TableSetupScrollFreeze(0, 1);
TableSetupColumn("ID");
TableSetupColumn("Priority");
TableSetupColumn("Entry");
TableSetupColumn("PC");
TableSetupColumn("Status");
PushFont(iris->font_small_code);
TableHeadersRow();
PopFont();
struct ee_thread* thr = (struct ee_thread*)&iris->ps2->ee_ram->buf[ee->thread_list_base & 0x1fffffff];
int id = 0;
while (thr->status) {
TableNextRow();
TableSetColumnIndex(0);
Text("%d", id++);
TableSetColumnIndex(1);
Text("%d", thr->current_priority);
TableSetColumnIndex(2);
const char* symbol = get_entry_symbol(iris, thr->func);
if (symbol) {
Text("%s", symbol);
} else {
Text("0x%08X", thr->func);
}
uint32_t argv = *(uint32_t*)&iris->ps2->ee_ram->buf[(thr->argv + 4) & 0x1fffffff];
// if (thr->argc) {
// printf("argv=%08x *argv=%08x\n", thr->argv, argv);
// }
TableSetColumnIndex(3);
Text("%s", thr->argc ? (char*)&iris->ps2->ee_ram->buf[argv & 0x1fffffff] : "NULL");
TableSetColumnIndex(4);
Text("%s", get_status_string(thr->status));
thr++;
}
EndTable();
}
}
void show_threads(iris::instance* iris) {
using namespace ImGui;
if (imgui::BeginEx("Threads", &iris->show_threads)) {
if (!iris->ps2->ee->thread_list_base) {
ImVec2 size = CalcTextSize(ICON_MS_WARNING " Thread list hasn't been initialized yet");
ImVec2 pos = ImVec2(GetContentRegionAvail().x / 2 - size.x / 2, GetContentRegionAvail().y / 2 - size.y / 2);
ImVec4 col = GetStyle().Colors[ImGuiCol_Text];
SetCursorPos(pos);
TextDisabled(ICON_MS_WARNING " Thread list hasn't been initialized yet");
End();
return;
}
show_thread_list(iris);
} End();
}
}
================================================
FILE: frontend/ui/vu_disassembly.cpp
================================================
#include
#include
#include
#include
#include "iris.hpp"
#include "portable-file-dialogs.h"
#include "res/IconsMaterialSymbols.h"
#include "ee/vu_dis.h"
#include "ee/vu_def.hpp"
#include
#include
#include
// Trim from the start (in place)
inline void ltrim_impl(std::string &s) {
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) {
return !std::isspace(ch);
}));
}
// Trim from the end (in place)
inline void rtrim_impl(std::string &s) {
s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) {
return !std::isspace(ch);
}).base(), s.end());
}
// Trim from both ends (in place)
inline void trim_impl(std::string &s) {
rtrim_impl(s);
ltrim_impl(s);
}
// Trim from the start (copying)
inline std::string ltrim(std::string s) {
ltrim_impl(s);
return s;
}
// Trim from the end (copying)
inline std::string rtrim(std::string s) {
rtrim_impl(s);
return s;
}
// Trim from both ends (copying)
inline std::string trim(std::string s) {
trim_impl(s);
return s;
}
#define IM_RGB(r, g, b) ImVec4(((float)r / 255.0f), ((float)g / 255.0f), ((float)b / 255.0f), 1.0)
namespace iris {
struct vu_dis_state g_vu_dis_state = { 0 };
uint32_t addr = 0;
bool stop_at_e_bit = false;
bool disassemble_all = false;
bool add_padding = true;
bool compact_view = false;
bool show_address_opcode = true;
void print_highlighted_vu1(iris::instance* iris, const char* buf) {
using namespace ImGui;
std::vector tokens;
std::string text;
while (*buf) {
text.clear();
if (isalpha(*buf)) {
while (isalpha(*buf) || isdigit(*buf) || (*buf == '.'))
text.push_back(*buf++);
} else if (isxdigit(*buf) || (*buf == '-')) {
while (isxdigit(*buf) || (*buf == 'x') || (*buf == '-'))
text.push_back(*buf++);
} else if (*buf == '$') {
while (*buf == '$' || isdigit(*buf) || isalpha(*buf) || *buf == '_')
text.push_back(*buf++);
} else if (*buf == ',') {
while (*buf == ',')
text.push_back(*buf++);
} else if (*buf == '(') {
while (*buf == '(')
text.push_back(*buf++);
} else if (*buf == ')') {
while (*buf == ')')
text.push_back(*buf++);
} else if (*buf == '<') {
while (*buf != '>')
text.push_back(*buf++);
text.push_back(*buf++);
} else if (*buf == '_') {
text.push_back(*buf++);
} else if (*buf == '.') {
text.push_back(*buf++);
} else if (*buf == '+') {
text.push_back(*buf++);
} else if (*buf == '-') {
text.push_back(*buf++);
} else {
printf("unhandled char %c (%d) \"%s\"\n", *buf, *buf, buf);
exit(1);
}
while (isspace(*buf))
text.push_back(*buf++);
tokens.push_back(text);
}
for (const std::string& t : tokens) {
ImVec4 col = ImVec4(
iris->codeview_color_register.Value.x,
iris->codeview_color_register.Value.y,
iris->codeview_color_register.Value.z,
iris->codeview_color_register.Value.w
);
if (t[0] == 'v' && (t[1] == 'f' || t[1] == 'i')) {
TextColored(col, "%s", t.c_str());
} else if (rtrim(t) == "acc") {
TextColored(col, "%s", t.c_str());
} else if (rtrim(t) == "q") {
TextColored(col, "%s", t.c_str());
} else if (rtrim(t) == "p") {
TextColored(col, "%s", t.c_str());
} else if (rtrim(t) == "i") {
TextColored(col, "%s", t.c_str());
} else if (rtrim(t) == "r") {
TextColored(col, "%s", t.c_str());
} else if (isalpha(t[0])) {
col = ImVec4(
iris->codeview_color_mnemonic.Value.x,
iris->codeview_color_mnemonic.Value.y,
iris->codeview_color_mnemonic.Value.z,
iris->codeview_color_mnemonic.Value.w
);
TextColored(col, "%s", t.c_str());
} else if (isdigit(t[0]) || t[0] == '-') {
col = ImVec4(
iris->codeview_color_number.Value.x,
iris->codeview_color_number.Value.y,
iris->codeview_color_number.Value.z,
iris->codeview_color_number.Value.w
);
TextColored(col, "%s", t.c_str());
} else if (t[0] == '<') {
col = ImVec4(
iris->codeview_color_other.Value.x,
iris->codeview_color_other.Value.y,
iris->codeview_color_other.Value.z,
iris->codeview_color_other.Value.w
);
TextColored(col, "%s", t.c_str());
} else {
Text("%s", t.c_str());
}
SameLine(0.0f, 0.0f);
}
NewLine();
}
static void show_vu_disassembly_view(iris::instance* iris, uint64_t* mem, size_t size) {
using namespace ImGui;
float font_scale = GetStyle().FontScaleMain;
GetStyle().FontScaleMain = iris->codeview_font_scale;
PushFont(iris->font_code);
if (!iris->codeview_use_theme_background) {
PushStyleColor(ImGuiCol_TableRowBg, ImVec4(
iris->codeview_color_background.Value.x,
iris->codeview_color_background.Value.y,
iris->codeview_color_background.Value.z,
iris->codeview_color_background.Value.w
));
PushStyleColor(ImGuiCol_TableRowBgAlt, ImVec4(
iris->codeview_color_background.Value.x,
iris->codeview_color_background.Value.y,
iris->codeview_color_background.Value.z,
iris->codeview_color_background.Value.w
));
PushStyleColor(ImGuiCol_Text, ImVec4(
iris->codeview_color_text.Value.x,
iris->codeview_color_text.Value.y,
iris->codeview_color_text.Value.z,
iris->codeview_color_text.Value.w
));
PushStyleColor(ImGuiCol_TextDisabled, ImVec4(
iris->codeview_color_comment.Value.x,
iris->codeview_color_comment.Value.y,
iris->codeview_color_comment.Value.z,
iris->codeview_color_comment.Value.w
));
}
if (BeginTable("table1", compact_view ? 2 : 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Hideable)) {
PushFont(iris->font_small_code);
TableSetupColumn(" Address/Opcode");
TableSetupColumn(compact_view ? "Upper/Lower" : "Upper");
if (!compact_view) {
TableSetupColumn("Lower");
}
TableHeadersRow();
PopFont();
int e_bit = 0;
for (int row = disassemble_all ? 0 : addr; row < size; row++) {
g_vu_dis_state.addr = row * 8;
TableNextRow();
TableSetColumnIndex(0);
uint64_t u = mem[row] >> 32;
uint64_t l = mem[row] & 0xffffffff;
if (!compact_view) {
TextDisabled("%04x: %08x %08x", row, u, l); SameLine();
} else {
TextDisabled("%04x: %08x", row, u); SameLine();
}
TableSetColumnIndex(1);
char upper[512], lower[512];
g_vu_dis_state.addr = row;
vu_disassemble_upper(upper, u, &g_vu_dis_state);
vu_disassemble_lower(lower, l, &g_vu_dis_state);
if (add_padding && !compact_view) {
#ifdef _WIN32
sprintf_s(upper, "%-40s", upper);
sprintf_s(lower, "%-40s", lower);
#else
sprintf(upper, "%-40s", upper);
sprintf(lower, "%-40s", lower);
#endif
}
print_highlighted_vu1(iris, upper);
if (!compact_view) {
TableSetColumnIndex(2);
}
print_highlighted_vu1(iris, lower);
if (e_bit && stop_at_e_bit && !disassemble_all) break;
e_bit = (u & 0x40000000) ? 1 : 0;
}
EndTable();
}
PopFont();
if (!iris->codeview_use_theme_background) {
PopStyleColor(4);
}
GetStyle().FontScaleMain = font_scale;
}
void save_disassembly(FILE* file, uint64_t* mem, size_t size) {
int e_bit = 0;
for (int row = disassemble_all ? 0 : addr; row < size; row++) {
g_vu_dis_state.addr = row * 8;
uint64_t u = mem[row] >> 32;
uint64_t l = mem[row] & 0xffffffff;
char upper[512], lower[512];
g_vu_dis_state.addr = row;
vu_disassemble_upper(upper, u, &g_vu_dis_state);
vu_disassemble_lower(lower, l, &g_vu_dis_state);
if (add_padding && !compact_view) {
#ifdef _WIN32
sprintf_s(upper, "%-40s", upper);
sprintf_s(lower, "%-40s", lower);
#else
sprintf(upper, "%-40s", upper);
sprintf(lower, "%-40s", lower);
#endif
}
if (compact_view) {
fprintf(file, "%04x: %08x %s\n", row, u, upper);
fprintf(file, " %08x %s\n", l, lower);
} else {
fprintf(file, "%04x: %08x %08x %s %s\n", row, u, l, upper, lower);
}
if (e_bit && stop_at_e_bit && !disassemble_all) break;
e_bit = (u & 0x40000000) ? 1 : 0;
}
}
void show_vu_disassembler(iris::instance* iris) {
using namespace ImGui;
PushFont(iris->font_icons);
if (imgui::BeginEx("VU disassembler", &iris->show_vu_disassembler, ImGuiWindowFlags_MenuBar)) {
if (BeginMenuBar()) {
if (BeginMenu("File")) {
if (MenuItem(ICON_MS_FILE_SAVE " Save disassembly as...", NULL)) {
}
ImGui::EndMenu();
}
if (BeginMenu("Settings")) {
MenuItem(ICON_MS_FORMAT_LETTER_SPACING_WIDER " Add padding", NULL, &add_padding);
MenuItem(ICON_MS_COLLAPSE_ALL " Compact view", NULL, &compact_view);
ImGui::EndMenu();
}
EndMenuBar();
}
if (BeginTabBar("##vudistabbar", ImGuiTabBarFlags_Reorderable)) {
if (BeginTabItem("VU0")) {
BeginDisabled(disassemble_all);
AlignTextToFramePadding();
Text("Address"); SameLine();
SetNextItemWidth(100.0f);
PushFont(iris->font_code);
if (InputInt("##address", (int*)&addr, 0, 0, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_EscapeClearsAll));
PopFont();
SameLine();
Checkbox("Stop at E bit", &stop_at_e_bit);
EndDisabled();
SameLine();
Checkbox("Disassemble all", &disassemble_all); SameLine();
if (Button(ICON_MS_SAVE)) {
pfd::save_file file("Save VU0 disassembly", "vu0.s", { "Text files", "*.txt" });
if (!file.result().empty()) {
FILE* f = fopen(file.result().c_str(), "w");
if (f) {
uint64_t* ptr = vu_get_micro_mem_ptr(iris->ps2->vu0, 0);
save_disassembly(f, ptr, 512);
fclose(f);
} else {
pfd::message("Error", "Failed to open file for writing.", pfd::choice::ok, pfd::icon::error);
}
}
} SameLine();
BeginDisabled(!stop_at_e_bit);
if (Button(ICON_MS_ARROW_RIGHT_ALT " Next program")) {
int prev = addr;
addr = 0;
for (int i = prev; i < 512; i++) {
uint32_t upper = *vu_get_micro_mem_ptr(iris->ps2->vu0, i) >> 32;
if (upper & 0x40000000) {
addr = i + 2;
break;
}
}
}
EndDisabled();
SeparatorText("Disassembly");
if (BeginChild("vu0##disassembly")) {
uint64_t* ptr = vu_get_micro_mem_ptr(iris->ps2->vu0, 0);
show_vu_disassembly_view(iris, ptr, 512);
} EndChild();
EndTabItem();
}
if (BeginTabItem("VU1")) {
BeginDisabled(disassemble_all);
AlignTextToFramePadding();
Text("Address"); SameLine();
SetNextItemWidth(100.0f);
PushFont(iris->font_code);
if (InputInt("##address", (int*)&addr, 0, 0, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_EscapeClearsAll));
PopFont();
SameLine();
Checkbox("Stop at E bit", &stop_at_e_bit);
EndDisabled();
SameLine();
Checkbox("Disassemble all", &disassemble_all); SameLine();
if (Button(ICON_MS_SAVE)) {
pfd::save_file file("Save VU1 disassembly", "vu1.s", { "Text files", "*.txt" });
if (!file.result().empty()) {
FILE* f = fopen(file.result().c_str(), "w");
if (f) {
save_disassembly(f, iris->ps2->vu1->micro_mem, 2048);
fclose(f);
} else {
pfd::message("Error", "Failed to open file for writing.", pfd::choice::ok, pfd::icon::error);
}
}
} SameLine();
BeginDisabled(!stop_at_e_bit);
if (Button(ICON_MS_ARROW_RIGHT_ALT " Next program")) {
int prev = addr;
addr = 0;
for (int i = prev; i < 2048; i++) {
uint32_t upper = iris->ps2->vu1->micro_mem[i] >> 32;
if (upper & 0x40000000) {
addr = i + 2;
break;
}
}
}
EndDisabled();
SeparatorText("Disassembly");
if (BeginChild("vu1##disassembly")) {
show_vu_disassembly_view(iris, iris->ps2->vu1->micro_mem, 2048);
} EndChild();
EndTabItem();
}
EndTabBar();
}
} End();
PopFont();
}
}
================================================
FILE: frontend/vulkan.cpp
================================================
#include
#include "config.hpp"
#include "iris.hpp"
#include
#include
namespace iris::vulkan {
std::vector get_instance_extensions() {
std::vector extensions;
uint32_t count;
vkEnumerateInstanceExtensionProperties(nullptr, &count, nullptr);
extensions.resize(count);
if (vkEnumerateInstanceExtensionProperties(nullptr, &count, extensions.data()) != VK_SUCCESS) {
fprintf(stderr, "vulkan: Failed to enumerate instance extensions\n");
return {};
}
return extensions;
}
std::vector get_instance_layers() {
std::vector layers;
uint32_t count;
vkEnumerateInstanceLayerProperties(&count, nullptr);
layers.resize(count);
if (vkEnumerateInstanceLayerProperties(&count, layers.data()) != VK_SUCCESS) {
fprintf(stderr, "vulkan: Failed to enumerate instance layers\n");
return {};
}
return layers;
}
bool is_instance_extension_supported(iris::instance* iris, const char* name) {
return std::find_if(
iris->instance_extensions.begin(),
iris->instance_extensions.end(),
[name](const VkExtensionProperties& ext) {
return strncmp(ext.extensionName, name, VK_MAX_EXTENSION_NAME_SIZE) == 0;
}
) != iris->instance_extensions.end();
}
bool is_instance_layer_supported(iris::instance* iris, const char* name) {
return std::find_if(
iris->instance_layers.begin(),
iris->instance_layers.end(),
[name](const VkLayerProperties& layer) {
return strncmp(layer.layerName, name, VK_MAX_EXTENSION_NAME_SIZE) == 0;
}
) != iris->instance_layers.end();
}
bool is_device_extension_supported(iris::instance* iris, const char* name) {
return std::find_if(
iris->device_extensions.begin(),
iris->device_extensions.end(),
[name](const VkExtensionProperties& ext) {
return strncmp(ext.extensionName, name, VK_MAX_EXTENSION_NAME_SIZE) == 0;
}
) != iris->device_extensions.end();
}
bool is_device_layer_supported(iris::instance* iris, const char* name) {
return std::find_if(
iris->device_layers.begin(),
iris->device_layers.end(),
[name](const VkLayerProperties& layer) {
return strncmp(layer.layerName, name, VK_MAX_EXTENSION_NAME_SIZE) == 0;
}
) != iris->device_layers.end();
}
std::vector get_device_extensions(iris::instance* iris) {
std::vector extensions;
uint32_t count;
vkEnumerateDeviceExtensionProperties(iris->physical_device, nullptr, &count, nullptr);
extensions.resize(count);
if (vkEnumerateDeviceExtensionProperties(iris->physical_device, nullptr, &count, extensions.data()) != VK_SUCCESS) {
fprintf(stderr, "vulkan: Failed to enumerate device extensions\n");
return {};
}
return extensions;
}
std::vector get_device_layers(iris::instance* iris) {
std::vector layers;
uint32_t count;
vkEnumerateDeviceLayerProperties(iris->physical_device, &count, nullptr);
layers.resize(count);
if (vkEnumerateDeviceLayerProperties(iris->physical_device, &count, layers.data()) != VK_SUCCESS) {
fprintf(stderr, "vulkan: Failed to enumerate device layers\n");
return {};
}
return layers;
}
struct instance_create_info {
std::vector enabled_extensions;
std::vector enabled_layers;
VkInstanceCreateFlags flags = 0;
};
VkInstance create_instance(iris::instance* iris, const instance_create_info& info) {
VkInstance instance = VK_NULL_HANDLE;
for (const char* ext : info.enabled_extensions) {
if (!is_instance_extension_supported(iris, ext)) {
fprintf(stderr, "vulkan: Requested instance extension not supported: %s\n", ext);
continue;
}
iris->enabled_instance_extensions.push_back(ext);
}
for (const char* layer : info.enabled_layers) {
if (!is_instance_layer_supported(iris, layer)) {
fprintf(stderr, "vulkan: Requested instance layer not supported: %s\n", layer);
continue;
}
iris->enabled_instance_layers.push_back(layer);
}
iris->app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
iris->app_info.pApplicationName = IRIS_TITLE;
iris->app_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
iris->app_info.pEngineName = "Vulkan";
iris->app_info.engineVersion = VK_MAKE_VERSION(1, 1, 0);
iris->app_info.apiVersion = IRIS_VULKAN_API_VERSION;
iris->app_info.pNext = VK_NULL_HANDLE;
iris->instance_create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
iris->instance_create_info.pApplicationInfo = &iris->app_info;
iris->instance_create_info.enabledExtensionCount = iris->enabled_instance_extensions.size();
iris->instance_create_info.ppEnabledExtensionNames = iris->enabled_instance_extensions.data();
iris->instance_create_info.enabledLayerCount = iris->enabled_instance_layers.size();
iris->instance_create_info.ppEnabledLayerNames = iris->enabled_instance_layers.data();
iris->instance_create_info.flags = info.flags;
if (vkCreateInstance(&iris->instance_create_info, nullptr, &instance) != VK_SUCCESS) {
return VK_NULL_HANDLE;
}
return instance;
}
static inline uint32_t find_memory_type(iris::instance* iris, uint32_t filter, VkMemoryPropertyFlags properties) {
VkPhysicalDeviceMemoryProperties mp;
vkGetPhysicalDeviceMemoryProperties(iris->physical_device, &mp);
for (uint32_t i = 0; i < mp.memoryTypeCount; i++) {
if ((filter & (1 << i)) && (mp.memoryTypes[i].propertyFlags & properties) == properties) {
return i;
}
}
return 0;
}
struct device_create_info {
std::vector enabled_extensions;
std::vector enabled_layers;
VkPhysicalDeviceFeatures enabled_features = {};
void* data;
};
VkDevice create_device(iris::instance* iris, const device_create_info& info) {
VkDevice device = VK_NULL_HANDLE;
for (const char* ext : info.enabled_extensions) {
if (!is_device_extension_supported(iris, ext)) {
fprintf(stderr, "vulkan: Requested device extension not supported: %s\n", ext);
continue;
}
iris->enabled_device_extensions.push_back(ext);
}
iris->cubic_supported = is_device_extension_supported(iris, VK_EXT_FILTER_CUBIC_EXTENSION_NAME);
for (const char* layer : info.enabled_layers) {
if (!is_device_layer_supported(iris, layer)) {
fprintf(stderr, "vulkan: Requested device layer not supported: %s\n", layer);
continue;
}
iris->enabled_device_layers.push_back(layer);
}
iris->device_features = {};
iris->device_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
iris->device_features.pNext = info.data;
VkPhysicalDeviceFeatures2 supported_features = {};
supported_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
supported_features.pNext = nullptr;
vkGetPhysicalDeviceFeatures2(iris->physical_device, &supported_features);
#define SET_FEATURE(f) \
iris->device_features.features.f = (supported_features.features.f && info.enabled_features.f)
SET_FEATURE(robustBufferAccess);
SET_FEATURE(fullDrawIndexUint32);
SET_FEATURE(imageCubeArray);
SET_FEATURE(independentBlend);
SET_FEATURE(geometryShader);
SET_FEATURE(tessellationShader);
SET_FEATURE(sampleRateShading);
SET_FEATURE(dualSrcBlend);
SET_FEATURE(logicOp);
SET_FEATURE(multiDrawIndirect);
SET_FEATURE(drawIndirectFirstInstance);
SET_FEATURE(depthClamp);
SET_FEATURE(depthBiasClamp);
SET_FEATURE(fillModeNonSolid);
SET_FEATURE(depthBounds);
SET_FEATURE(wideLines);
SET_FEATURE(largePoints);
SET_FEATURE(alphaToOne);
SET_FEATURE(multiViewport);
SET_FEATURE(samplerAnisotropy);
SET_FEATURE(textureCompressionETC2);
SET_FEATURE(textureCompressionASTC_LDR);
SET_FEATURE(textureCompressionBC);
SET_FEATURE(occlusionQueryPrecise);
SET_FEATURE(pipelineStatisticsQuery);
SET_FEATURE(vertexPipelineStoresAndAtomics);
SET_FEATURE(fragmentStoresAndAtomics);
SET_FEATURE(shaderTessellationAndGeometryPointSize);
SET_FEATURE(shaderImageGatherExtended);
SET_FEATURE(shaderStorageImageExtendedFormats);
SET_FEATURE(shaderStorageImageMultisample);
SET_FEATURE(shaderStorageImageReadWithoutFormat);
SET_FEATURE(shaderStorageImageWriteWithoutFormat);
SET_FEATURE(shaderUniformBufferArrayDynamicIndexing);
SET_FEATURE(shaderSampledImageArrayDynamicIndexing);
SET_FEATURE(shaderStorageBufferArrayDynamicIndexing);
SET_FEATURE(shaderStorageImageArrayDynamicIndexing);
SET_FEATURE(shaderClipDistance);
SET_FEATURE(shaderCullDistance);
SET_FEATURE(shaderFloat64);
SET_FEATURE(shaderInt64);
SET_FEATURE(shaderInt16);
SET_FEATURE(shaderResourceResidency);
SET_FEATURE(shaderResourceMinLod);
SET_FEATURE(sparseBinding);
SET_FEATURE(sparseResidencyBuffer);
SET_FEATURE(sparseResidencyImage2D);
SET_FEATURE(sparseResidencyImage3D);
SET_FEATURE(sparseResidency2Samples);
SET_FEATURE(sparseResidency4Samples);
SET_FEATURE(sparseResidency8Samples);
SET_FEATURE(sparseResidency16Samples);
SET_FEATURE(sparseResidencyAliased);
SET_FEATURE(variableMultisampleRate);
SET_FEATURE(inheritedQueries);
#undef SET_FEATURE
const float queue_priority[] = { 1.0f };
iris->queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
iris->queue_create_info.queueFamilyIndex = iris->queue_family;
iris->queue_create_info.queueCount = 1;
iris->queue_create_info.pQueuePriorities = queue_priority;
iris->device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
iris->device_create_info.queueCreateInfoCount = 1;
iris->device_create_info.pQueueCreateInfos = &iris->queue_create_info;
iris->device_create_info.enabledExtensionCount = iris->enabled_device_extensions.size();
iris->device_create_info.ppEnabledExtensionNames = iris->enabled_device_extensions.data();
iris->device_create_info.enabledLayerCount = iris->enabled_device_layers.size();
iris->device_create_info.ppEnabledLayerNames = iris->enabled_device_layers.data();
iris->device_create_info.pEnabledFeatures = VK_NULL_HANDLE;
iris->device_create_info.pNext = &iris->device_features;
if (vkCreateDevice(iris->physical_device, &iris->device_create_info, nullptr, &device) != VK_SUCCESS) {
return VK_NULL_HANDLE;
}
return device;
}
void enumerate_physical_devices(iris::instance* iris) {
uint32_t count = 0;
vkEnumeratePhysicalDevices(iris->instance, &count, nullptr);
if (!count) {
return;
}
std::vector devices(count);
vkEnumeratePhysicalDevices(iris->instance, &count, devices.data());
iris->vulkan_gpus.clear();
for (const VkPhysicalDevice& device : devices) {
VkPhysicalDeviceProperties properties;
vkGetPhysicalDeviceProperties(device, &properties);
iris::vulkan_gpu gpu;
gpu.device = device;
gpu.type = properties.deviceType;
gpu.name = properties.deviceName;
gpu.api_version = properties.apiVersion;
iris->vulkan_gpus.push_back(gpu);
}
}
VkPhysicalDevice find_suitable_physical_device(iris::instance* iris) {
if (!iris->vulkan_gpus.size())
return VK_NULL_HANDLE;
for (int i = 0; i < iris->vulkan_gpus.size(); i++) {
auto& dev = iris->vulkan_gpus[i];
if (dev.type == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) {
iris->vulkan_selected_device_index = i;
return dev.device;
}
}
iris->vulkan_selected_device_index = 0;
// Just pick the first device for now
return iris->vulkan_gpus[0].device;
}
int find_graphics_queue_family_index(iris::instance* iris) {
uint32_t count = 0;
vkGetPhysicalDeviceQueueFamilyProperties(iris->physical_device, &count, nullptr);
if (!count) {
return -1;
}
std::vector queue_families(count);
vkGetPhysicalDeviceQueueFamilyProperties(iris->physical_device, &count, queue_families.data());
// Just return the first graphics-capable queue family, we should
// actually be looking for dedicated compute/transfer queues
for (uint32_t i = 0; i < count; i++) {
if (queue_families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
return i;
}
}
return -1;
}
VkBuffer create_buffer(iris::instance* iris, VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkDeviceMemory& buffer_memory) {
VkBuffer buffer = VK_NULL_HANDLE;
VkBufferCreateInfo buffer_info = {};
buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
buffer_info.size = size;
buffer_info.usage = usage;
buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
if (vkCreateBuffer(iris->device, &buffer_info, nullptr, &buffer) != VK_SUCCESS) {
fprintf(stderr, "vulkan: Failed to create buffer\n");
return VK_NULL_HANDLE;
}
VkMemoryRequirements memory_requirements;
vkGetBufferMemoryRequirements(iris->device, buffer, &memory_requirements);
VkMemoryAllocateInfo alloc_info = {};
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
alloc_info.allocationSize = memory_requirements.size;
VkPhysicalDeviceMemoryProperties memory_properties;
vkGetPhysicalDeviceMemoryProperties(iris->physical_device, &memory_properties);
for (uint32_t i = 0; i < memory_properties.memoryTypeCount; i++) {
if ((memory_requirements.memoryTypeBits & (1 << i)) &&
(memory_properties.memoryTypes[i].propertyFlags & properties) == properties) {
alloc_info.memoryTypeIndex = i;
break;
}
}
if (vkAllocateMemory(iris->device, &alloc_info, nullptr, &buffer_memory) != VK_SUCCESS) {
fprintf(stderr, "vulkan: Failed to allocate buffer memory\n");
vkDestroyBuffer(iris->device, buffer, nullptr);
return VK_NULL_HANDLE;
}
vkBindBufferMemory(iris->device, buffer, buffer_memory, 0);
return buffer;
}
void load_buffer(iris::instance* iris, VkDeviceMemory buffer_memory, void* data, VkDeviceSize size) {
void* ptr;
vkMapMemory(iris->device, buffer_memory, 0, size, 0, &ptr);
memcpy(ptr, data, (size_t)size);
vkUnmapMemory(iris->device, buffer_memory);
}
bool copy_buffer(iris::instance* iris, VkBuffer src, VkBuffer dst, VkDeviceSize size) {
VkCommandPool command_pool = VK_NULL_HANDLE;
VkCommandPoolCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
info.queueFamilyIndex = iris->queue_family;
if (vkCreateCommandPool(iris->device, &info, VK_NULL_HANDLE, &command_pool) != VK_SUCCESS) {
fprintf(stderr, "vulkan: Failed to create command pool\n");
return false;
}
VkCommandBufferAllocateInfo alloc_info = {};
alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
alloc_info.commandPool = command_pool;
alloc_info.commandBufferCount = 1;
VkCommandBuffer command_buffer;
vkAllocateCommandBuffers(iris->device, &alloc_info, &command_buffer);
VkCommandBufferBeginInfo begin_info{};
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
vkBeginCommandBuffer(command_buffer, &begin_info);
VkBufferCopy copy_region{};
copy_region.size = size;
vkCmdCopyBuffer(command_buffer, src, dst, 1, ©_region);
vkEndCommandBuffer(command_buffer);
VkSubmitInfo submit_info{};
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submit_info.commandBufferCount = 1;
submit_info.pCommandBuffers = &command_buffer;
vkQueueSubmit(iris->queue, 1, &submit_info, VK_NULL_HANDLE);
vkQueueWaitIdle(iris->queue);
vkFreeCommandBuffers(iris->device, command_pool, 1, &command_buffer);
vkDestroyCommandPool(iris->device, command_pool, VK_NULL_HANDLE);
return true;
}
bool create_descriptor_pool(iris::instance* iris) {
VkDescriptorPoolSize pool_sizes[] = {
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 512 }
};
VkDescriptorPoolCreateInfo pool_info = {};
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
pool_info.maxSets = 0;
for (VkDescriptorPoolSize& pool_size : pool_sizes)
pool_info.maxSets += pool_size.descriptorCount;
pool_info.poolSizeCount = (uint32_t)IM_ARRAYSIZE(pool_sizes);
pool_info.pPoolSizes = pool_sizes;
if (vkCreateDescriptorPool(iris->device, &pool_info, VK_NULL_HANDLE, &iris->descriptor_pool) != VK_SUCCESS) {
fprintf(stderr, "imgui: Failed to create descriptor pool\n");
return false;
}
return true;
}
texture upload_texture(iris::instance* iris, void* pixels, int width, int height, int stride) {
texture tex = {};
tex.width = width;
tex.height = height;
tex.stride = stride;
tex.image_size = width * height * 4;
VkDeviceMemory staging_buffer_memory = VK_NULL_HANDLE;
VkBuffer staging_buffer = create_buffer(
iris,
tex.image_size,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
staging_buffer_memory
);
if (staging_buffer == VK_NULL_HANDLE)
return {};
load_buffer(iris, staging_buffer_memory, pixels, tex.image_size);
// To-do: Transition image layout and copy buffer to image
// Create the Vulkan image.
{
VkImageCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
info.imageType = VK_IMAGE_TYPE_2D;
info.format = VK_FORMAT_R8G8B8A8_UNORM;
info.extent.width = width;
info.extent.height = height;
info.extent.depth = 1;
info.mipLevels = 1;
info.arrayLayers = 1;
info.samples = VK_SAMPLE_COUNT_1_BIT;
info.tiling = VK_IMAGE_TILING_OPTIMAL;
info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
if (vkCreateImage(iris->device, &info, VK_NULL_HANDLE, &tex.image) != VK_SUCCESS) {
fprintf(stderr, "vulkan: Failed to create image\n");
return {};
}
VkMemoryRequirements req;
vkGetImageMemoryRequirements(iris->device, tex.image, &req);
VkMemoryAllocateInfo alloc_info = {};
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
alloc_info.allocationSize = req.size;
VkPhysicalDeviceMemoryProperties memory_properties;
vkGetPhysicalDeviceMemoryProperties(iris->physical_device, &memory_properties);
for (uint32_t i = 0; i < memory_properties.memoryTypeCount; i++) {
if ((req.memoryTypeBits & (1 << i)) &&
(memory_properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) == VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) {
alloc_info.memoryTypeIndex = i;
break;
}
}
if (vkAllocateMemory(iris->device, &alloc_info, VK_NULL_HANDLE, &tex.image_memory) != VK_SUCCESS) {
fprintf(stderr, "vulkan: Failed to allocate image memory\n");
return {};
}
if (vkBindImageMemory(iris->device, tex.image, tex.image_memory, 0) != VK_SUCCESS) {
fprintf(stderr, "vulkan: Failed to bind image memory\n");
return {};
}
}
// Create the Image View
{
VkImageViewCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
info.image = tex.image;
info.viewType = VK_IMAGE_VIEW_TYPE_2D;
info.format = VK_FORMAT_R8G8B8A8_UNORM;
info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
info.subresourceRange.levelCount = 1;
info.subresourceRange.layerCount = 1;
if (vkCreateImageView(iris->device, &info, VK_NULL_HANDLE, &tex.image_view) != VK_SUCCESS) {
fprintf(stderr, "vulkan: Failed to create image view\n");
return {};
}
}
// Create Sampler
{
VkSamplerCreateInfo sampler_info{};
sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
sampler_info.magFilter = VK_FILTER_LINEAR;
sampler_info.minFilter = VK_FILTER_LINEAR;
sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; // outside image bounds just use border color
sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
sampler_info.minLod = -1000;
sampler_info.maxLod = 1000;
sampler_info.maxAnisotropy = 1.0f;
if (vkCreateSampler(iris->device, &sampler_info, VK_NULL_HANDLE, &tex.sampler) != VK_SUCCESS) {
fprintf(stderr, "vulkan: Failed to create sampler\n");
return {};
}
}
{
VkDescriptorSetAllocateInfo alloc_info = {};
alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
alloc_info.descriptorPool = iris->descriptor_pool;
alloc_info.descriptorSetCount = 1;
alloc_info.pSetLayouts = &iris->descriptor_set_layout;
if (vkAllocateDescriptorSets(iris->device, &alloc_info, &tex.descriptor_set) != VK_SUCCESS) {
fprintf(stderr, "vulkan: Failed to allocate descriptor sets\n");
return {};
}
}
// Update the Descriptor Set:
{
VkDescriptorImageInfo desc_image[1] = {};
desc_image[0].sampler = tex.sampler;
desc_image[0].imageView = tex.image_view;
desc_image[0].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
VkWriteDescriptorSet write_desc[1] = {};
write_desc[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write_desc[0].dstSet = tex.descriptor_set;
write_desc[0].descriptorCount = 1;
write_desc[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
write_desc[0].pImageInfo = desc_image;
vkUpdateDescriptorSets(iris->device, 1, write_desc, 0, nullptr);
}
VkCommandPool command_pool = VK_NULL_HANDLE;
VkCommandPoolCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
info.queueFamilyIndex = iris->queue_family;
if (vkCreateCommandPool(iris->device, &info, VK_NULL_HANDLE, &command_pool) != VK_SUCCESS) {
fprintf(stderr, "vulkan: Failed to create command pool\n");
return {};
}
VkCommandBuffer command_buffer;
{
VkCommandBufferAllocateInfo alloc_info{};
alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
alloc_info.commandPool = command_pool;
alloc_info.commandBufferCount = 1;
if (vkAllocateCommandBuffers(iris->device, &alloc_info, &command_buffer) != VK_SUCCESS) {
fprintf(stderr, "vulkan: Failed to allocate command buffers\n");
return {};
}
VkCommandBufferBeginInfo begin_info = {};
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
begin_info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
if (vkBeginCommandBuffer(command_buffer, &begin_info) != VK_SUCCESS) {
printf("vulkan: Failed to begin command buffer\n");
return {};
}
}
// Copy to Image
{
VkImageMemoryBarrier copy_barrier[1] = {};
copy_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
copy_barrier[0].dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
copy_barrier[0].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
copy_barrier[0].newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
copy_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
copy_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
copy_barrier[0].image = tex.image;
copy_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copy_barrier[0].subresourceRange.levelCount = 1;
copy_barrier[0].subresourceRange.layerCount = 1;
vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1, copy_barrier);
VkBufferImageCopy region = {};
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.layerCount = 1;
region.imageExtent.width = width;
region.imageExtent.height = height;
region.imageExtent.depth = 1;
vkCmdCopyBufferToImage(command_buffer, staging_buffer, tex.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
VkImageMemoryBarrier use_barrier[1] = {};
use_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
use_barrier[0].srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
use_barrier[0].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
use_barrier[0].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
use_barrier[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
use_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
use_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
use_barrier[0].image = tex.image;
use_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
use_barrier[0].subresourceRange.levelCount = 1;
use_barrier[0].subresourceRange.layerCount = 1;
vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, use_barrier);
}
// End command buffer
{
VkSubmitInfo end_info = {};
end_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
end_info.commandBufferCount = 1;
end_info.pCommandBuffers = &command_buffer;
if (vkEndCommandBuffer(command_buffer) != VK_SUCCESS) {
fprintf(stderr, "vulkan: Failed to end command buffer\n");
return {};
}
if (vkQueueSubmit(iris->queue, 1, &end_info, VK_NULL_HANDLE) != VK_SUCCESS) {
fprintf(stderr, "vulkan: Failed to submit queue\n");
return {};
}
if (vkDeviceWaitIdle(iris->device) != VK_SUCCESS) {
fprintf(stderr, "vulkan: Failed to wait device idle\n");
return {};
}
}
vkDestroyCommandPool(iris->device, command_pool, nullptr);
vkDestroyBuffer(iris->device, staging_buffer, nullptr);
vkFreeMemory(iris->device, staging_buffer_memory, nullptr);
return tex;
}
void free_texture(iris::instance* iris, texture& tex) {
if (!iris->device)
return;
if (tex.sampler) vkDestroySampler(iris->device, tex.sampler, nullptr);
if (tex.image_view) vkDestroyImageView(iris->device, tex.image_view, nullptr);
if (tex.image) vkDestroyImage(iris->device, tex.image, nullptr);
if (tex.image_memory) vkFreeMemory(iris->device, tex.image_memory, nullptr);
}
bool init(iris::instance* iris, bool enable_validation) {
if (volkInitialize() != VK_SUCCESS) {
fprintf(stderr, "vulkan: Failed to initialize volk loader\n");
return false;
}
iris->instance_extensions = get_instance_extensions();
iris->instance_layers = get_instance_layers();
std::vector extensions = {
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME
};
std::vector layers;
if (enable_validation) {
layers.push_back("VK_LAYER_KHRONOS_validation");
}
// Push SDL extensions
uint32_t sdl_extension_count;
auto sdl_extensions = SDL_Vulkan_GetInstanceExtensions(&sdl_extension_count);
for (uint32_t i = 0; i < sdl_extension_count; i++) {
extensions.push_back(sdl_extensions[i]);
}
VkInstanceCreateFlags flags = 0;
// Needed for MoltenVK on macOS
#if defined(__APPLE__) && defined(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME)
extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
#endif
instance_create_info instance_info = {};
instance_info.enabled_extensions = extensions;
instance_info.enabled_layers = layers;
instance_info.flags = flags;
iris->instance = create_instance(iris, instance_info);
if (!iris->instance) {
fprintf(stderr, "vulkan: Failed to create Vulkan instance\n");
return false;
}
volkLoadInstance(iris->instance);
// Find a suitable Vulkan physical device (GPU)
enumerate_physical_devices(iris);
iris->vulkan_selected_device_index = 0;
if (iris->vulkan_physical_device < 0) {
iris->physical_device = find_suitable_physical_device(iris);
} else {
if (iris->vulkan_physical_device >= iris->vulkan_gpus.size()) {
iris->physical_device = find_suitable_physical_device(iris);
iris->vulkan_physical_device = iris->vulkan_selected_device_index;
} else {
iris->physical_device = iris->vulkan_gpus[iris->vulkan_physical_device].device;
iris->vulkan_selected_device_index = iris->vulkan_physical_device;
}
}
if (!iris->physical_device) {
fprintf(stderr, "vulkan: Failed to find a suitable Vulkan device\n");
return false;
}
VkPhysicalDeviceProperties properties;
vkGetPhysicalDeviceProperties(iris->physical_device, &properties);
printf("vulkan: Using Vulkan device \"%s\". API version %d.%d.%d.%d Driver version %x\n",
properties.deviceName,
VK_API_VERSION_MAJOR(properties.apiVersion),
VK_API_VERSION_MINOR(properties.apiVersion),
VK_API_VERSION_PATCH(properties.apiVersion),
VK_API_VERSION_VARIANT(properties.apiVersion),
properties.driverVersion
);
iris->device_extensions = get_device_extensions(iris);
iris->device_layers = get_device_layers(iris);
// Find a graphics-capable queue family
int queue_family = find_graphics_queue_family_index(iris);
if (queue_family == -1) {
fprintf(stderr, "vulkan: Failed to find a graphics-capable queue family\n");
return false;
}
iris->queue_family = queue_family;
// To-do: Query required extensions/features from backends here.
// For now we'll just initialize a fixed set of extensions
// and features.
device_create_info device_info = {};
device_info.enabled_extensions = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME,
VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME,
VK_KHR_8BIT_STORAGE_EXTENSION_NAME,
// VK_KHR_16BIT_STORAGE_EXTENSION_NAME,
VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME,
VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME,
VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME,
VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME,
VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME,
VK_EXT_SHADER_SUBGROUP_BALLOT_EXTENSION_NAME,
VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME,
VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME,
VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME,
VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME,
// VK_KHR_MAINTENANCE_5_EXTENSION_NAME,
VK_EXT_MEMORY_PRIORITY_EXTENSION_NAME,
VK_EXT_MEMORY_BUDGET_EXTENSION_NAME,
VK_EXT_PAGEABLE_DEVICE_LOCAL_MEMORY_EXTENSION_NAME,
// VK_EXT_MESH_SHADER_EXTENSION_NAME,
VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME,
VK_KHR_LOAD_STORE_OP_NONE_EXTENSION_NAME,
VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME,
VK_EXT_FILTER_CUBIC_EXTENSION_NAME
};
device_info.enabled_layers = {};
#ifdef VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME
device_info.enabled_extensions.push_back(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME);
#endif
device_info.enabled_features = {};
device_info.enabled_features.shaderInt16 = VK_TRUE;
iris->vulkan_11_features.pNext = &iris->vulkan_12_features;
iris->vulkan_12_features.pNext = &iris->subgroup_size_control_features;
iris->subgroup_size_control_features.pNext = VK_NULL_HANDLE;
iris->vulkan_11_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES;
iris->vulkan_12_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
iris->subgroup_size_control_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES;
iris->vulkan_11_features.storageBuffer16BitAccess = VK_TRUE;
iris->vulkan_11_features.uniformAndStorageBuffer16BitAccess = VK_TRUE;
iris->vulkan_12_features.descriptorIndexing = VK_TRUE;
iris->vulkan_12_features.descriptorBindingPartiallyBound = VK_TRUE;
iris->vulkan_12_features.descriptorBindingVariableDescriptorCount = VK_TRUE;
iris->vulkan_12_features.descriptorBindingSampledImageUpdateAfterBind = VK_TRUE;
iris->vulkan_12_features.runtimeDescriptorArray = VK_TRUE;
iris->vulkan_12_features.timelineSemaphore = VK_TRUE;
iris->vulkan_12_features.bufferDeviceAddress = VK_TRUE;
iris->vulkan_12_features.scalarBlockLayout = VK_TRUE;
iris->vulkan_12_features.storageBuffer8BitAccess = VK_TRUE;
iris->vulkan_12_features.uniformAndStorageBuffer8BitAccess = VK_TRUE;
iris->subgroup_size_control_features.subgroupSizeControl = VK_TRUE;
iris->subgroup_size_control_features.computeFullSubgroups = VK_TRUE;
// Chain in all feature structs
device_info.data = &iris->vulkan_11_features;
iris->device = create_device(iris, device_info);
if (!iris->device) {
fprintf(stderr, "vulkan: Failed to create Vulkan device\n");
return false;
}
vkGetDeviceQueue(iris->device, iris->queue_family, 0, &iris->queue);
iris->indices = { 0, 1, 2, 2, 3, 0 };
iris->vertex_buffer_size = sizeof(vertex) * iris->vertices.size();
// Create vertex and index buffers
// Create and populate index buffer immediately by creating
// a staging buffer, filling it, and then copying it over to
// the device local index buffer.
// The vertex buffer will be updated dynamically each frame.
VkDeviceMemory index_staging_buffer_memory;
VkDeviceSize index_buffer_size = sizeof(uint16_t) * iris->indices.size();
iris->index_buffer = create_buffer(
iris,
sizeof(uint16_t) * iris->indices.size(),
VK_BUFFER_USAGE_TRANSFER_DST_BIT |
VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
iris->index_buffer_memory
);
VkBuffer index_staging_buffer = create_buffer(
iris,
index_buffer_size,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
index_staging_buffer_memory
);
load_buffer(iris, index_staging_buffer_memory, iris->indices.data(), index_buffer_size);
copy_buffer(iris, index_staging_buffer, iris->index_buffer, index_buffer_size);
iris->vertex_buffer = create_buffer(
iris,
iris->vertex_buffer_size,
VK_BUFFER_USAGE_TRANSFER_DST_BIT |
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
iris->vertex_buffer_memory
);
iris->vertex_staging_buffer = create_buffer(
iris,
iris->vertex_buffer_size,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
iris->vertex_staging_buffer_memory
);
// We don't need the staging buffer anymore
vkFreeMemory(iris->device, index_staging_buffer_memory, nullptr);
vkDestroyBuffer(iris->device, index_staging_buffer, nullptr);
create_descriptor_pool(iris);
return true;
}
static int ic = 0;
void cleanup(iris::instance* iris) {
vulkan::wait_idle(iris);
if (iris->descriptor_set_layout) vkDestroyDescriptorSetLayout(iris->device, iris->descriptor_set_layout, nullptr);
if (iris->descriptor_pool) vkDestroyDescriptorPool(iris->device, iris->descriptor_pool, nullptr);
for (int i = 0; i < 3; i++)
if (iris->sampler[i]) vkDestroySampler(iris->device, iris->sampler[i], nullptr);
if (iris->vertex_buffer) vkDestroyBuffer(iris->device, iris->vertex_buffer, nullptr);
if (iris->vertex_staging_buffer) vkDestroyBuffer(iris->device, iris->vertex_staging_buffer, nullptr);
if (iris->index_buffer) vkDestroyBuffer(iris->device, iris->index_buffer, nullptr);
if (iris->vertex_staging_buffer_memory) vkFreeMemory(iris->device, iris->vertex_staging_buffer_memory, nullptr);
if (iris->vertex_buffer_memory) vkFreeMemory(iris->device, iris->vertex_buffer_memory, nullptr);
if (iris->index_buffer_memory) vkFreeMemory(iris->device, iris->index_buffer_memory, nullptr);
if (iris->pipeline) vkDestroyPipeline(iris->device, iris->pipeline, nullptr);
// ImGui takes care of this apparently (probably shouldn't)
// if (iris->surface) vkDestroySurfaceKHR(iris->instance, iris->surface, nullptr);
if (iris->render_pass) vkDestroyRenderPass(iris->device, iris->render_pass, nullptr);
if (iris->pipeline_layout) vkDestroyPipelineLayout(iris->device, iris->pipeline_layout, nullptr);
if (iris->device) vkDestroyDevice(iris->device, nullptr);
if (iris->instance) vkDestroyInstance(iris->instance, nullptr);
}
void insert_image_memory_barrier(
VkCommandBuffer buffer,
VkImage image,
VkAccessFlags src_access_mask,
VkAccessFlags dst_access_mask,
VkImageLayout old_image_layout,
VkImageLayout new_image_layout,
VkPipelineStageFlags src_stage_mask,
VkPipelineStageFlags dst_stage_mask,
VkImageSubresourceRange subresource_range)
{
VkImageMemoryBarrier image_memory_barrier = {};
image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
image_memory_barrier.srcAccessMask = src_access_mask;
image_memory_barrier.dstAccessMask = dst_access_mask;
image_memory_barrier.oldLayout = old_image_layout;
image_memory_barrier.newLayout = new_image_layout;
image_memory_barrier.image = image;
image_memory_barrier.subresourceRange = subresource_range;
vkCmdPipelineBarrier(
buffer,
src_stage_mask,
dst_stage_mask,
0,
0, nullptr,
0, nullptr,
1, &image_memory_barrier
);
}
void* read_image(iris::instance* iris, VkImage src_image, VkFormat format, int width, int height) {
if (src_image == VK_NULL_HANDLE) {
printf("vulkan: Source image is null\n");
return nullptr;
}
if (!width || !height) {
printf("vulkan: Invalid image dimensions for readback (%dx%d)\n", width, height);
return nullptr;
}
bool supports_blit = true;
// Check blit support for source and destination
VkFormatProperties format_props;
// Check if the device supports blitting from optimal images (the swapchain images are in optimal format)
vkGetPhysicalDeviceFormatProperties(iris->physical_device, format, &format_props);
if (!(format_props.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT)) {
printf("Device does not support blitting from optimal tiled images, using copy instead of blit!\n");
supports_blit = false;
}
// Check if the device supports blitting to linear images
vkGetPhysicalDeviceFormatProperties(iris->physical_device, VK_FORMAT_R8G8B8A8_UNORM, &format_props);
if (!(format_props.linearTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT)) {
printf("Device does not support blitting to linear tiled images, using copy instead of blit!\n");
supports_blit = false;
}
// Create the linear tiled destination image to copy to and to read the memory from
VkImageCreateInfo image_create_info = {};
image_create_info.imageType = VK_IMAGE_TYPE_2D;
image_create_info.format = VK_FORMAT_R8G8B8A8_UNORM;
image_create_info.extent.width = width;
image_create_info.extent.height = height;
image_create_info.extent.depth = 1;
image_create_info.arrayLayers = 1;
image_create_info.mipLevels = 1;
image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
image_create_info.tiling = VK_IMAGE_TILING_LINEAR;
image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;
// Create the image
VkImage dst_image;
if (vkCreateImage(iris->device, &image_create_info, nullptr, &dst_image) != VK_SUCCESS) {
printf("Failed to create image for readback\n");
return nullptr;
}
VkDeviceMemory dst_image_memory;
VkMemoryRequirements req;
vkGetImageMemoryRequirements(iris->device, dst_image, &req);
VkMemoryAllocateInfo alloc_info = {};
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
alloc_info.allocationSize = req.size;
alloc_info.memoryTypeIndex = find_memory_type(iris, req.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
if (vkAllocateMemory(iris->device, &alloc_info, VK_NULL_HANDLE, &dst_image_memory) != VK_SUCCESS) {
fprintf(stderr, "vulkan: Failed to allocate image memory for readback\n");
return {};
}
if (vkBindImageMemory(iris->device, dst_image, dst_image_memory, 0) != VK_SUCCESS) {
fprintf(stderr, "vulkan: Failed to bind image memory for readback\n");
return {};
}
// Do the actual blit from the swapchain image to our host visible destination image
VkCommandPool command_pool = VK_NULL_HANDLE;
VkCommandPoolCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
info.queueFamilyIndex = iris->queue_family;
if (vkCreateCommandPool(iris->device, &info, VK_NULL_HANDLE, &command_pool) != VK_SUCCESS) {
fprintf(stderr, "vulkan: Failed to create command pool for readback\n");
return {};
}
VkCommandBufferAllocateInfo cmd_buffer_alloc_info = {};
cmd_buffer_alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
cmd_buffer_alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
cmd_buffer_alloc_info.commandPool = command_pool;
cmd_buffer_alloc_info.commandBufferCount = 1;
VkCommandBuffer command_buffer;
vkAllocateCommandBuffers(iris->device, &cmd_buffer_alloc_info, &command_buffer);
VkCommandBufferBeginInfo begin_info{};
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
if (vkBeginCommandBuffer(command_buffer, &begin_info) != VK_SUCCESS) {
printf("vulkan: Failed to begin command buffer for readback\n");
return {};
}
// Transition destination image to transfer destination layout
insert_image_memory_barrier(
command_buffer,
dst_image,
0,
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }
);
// Transition swapchain image from present to transfer source layout
insert_image_memory_barrier(
command_buffer,
src_image,
VK_ACCESS_MEMORY_READ_BIT,
VK_ACCESS_TRANSFER_READ_BIT,
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }
);
// If source and destination support blit we'll blit as this also does automatic format conversion (e.g. from BGR to RGB)
if (supports_blit) {
// Define the region to blit (we will blit the whole swapchain image)
VkOffset3D blitSize;
blitSize.x = width;
blitSize.y = height;
blitSize.z = 1;
VkImageBlit imageBlitRegion{};
imageBlitRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
imageBlitRegion.srcSubresource.layerCount = 1;
imageBlitRegion.srcOffsets[1] = blitSize;
imageBlitRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
imageBlitRegion.dstSubresource.layerCount = 1;
imageBlitRegion.dstOffsets[1] = blitSize;
// Issue the blit command
vkCmdBlitImage(
command_buffer,
src_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1,
&imageBlitRegion,
VK_FILTER_NEAREST
);
} else {
// Otherwise use image copy (requires us to manually flip components)
VkImageCopy imageCopyRegion = {};
imageCopyRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
imageCopyRegion.srcSubresource.layerCount = 1;
imageCopyRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
imageCopyRegion.dstSubresource.layerCount = 1;
imageCopyRegion.extent.width = width;
imageCopyRegion.extent.height = height;
imageCopyRegion.extent.depth = 1;
// Issue the copy command
vkCmdCopyImage(
command_buffer,
src_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1,
&imageCopyRegion
);
}
// Transition destination image to general layout, which is the required layout for mapping the image memory later on
insert_image_memory_barrier(
command_buffer,
dst_image,
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_ACCESS_MEMORY_READ_BIT,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_IMAGE_LAYOUT_GENERAL,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }
);
// Transition back the swap chain image after the blit is done
insert_image_memory_barrier(
command_buffer,
src_image,
VK_ACCESS_TRANSFER_READ_BIT,
VK_ACCESS_MEMORY_READ_BIT,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }
);
// End command buffer
{
VkSubmitInfo end_info = {};
end_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
end_info.commandBufferCount = 1;
end_info.pCommandBuffers = &command_buffer;
if (vkEndCommandBuffer(command_buffer) != VK_SUCCESS) {
fprintf(stderr, "vulkan: Failed to end command buffer\n");
return {};
}
if (vkQueueSubmit(iris->queue, 1, &end_info, VK_NULL_HANDLE) != VK_SUCCESS) {
fprintf(stderr, "vulkan: Failed to submit queue\n");
return {};
}
if (vkDeviceWaitIdle(iris->device) != VK_SUCCESS) {
fprintf(stderr, "vulkan: Failed to wait device idle\n");
return {};
}
}
vkDestroyCommandPool(iris->device, command_pool, nullptr);
// Get layout of the image (including row pitch)
VkImageSubresource subresource { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0 };
VkSubresourceLayout subresource_layout;
vkGetImageSubresourceLayout(iris->device, dst_image, &subresource, &subresource_layout);
// Map image memory so we can start copying from it
const char* data;
vkMapMemory(iris->device, dst_image_memory, 0, VK_WHOLE_SIZE, 0, (void**)&data);
data += subresource_layout.offset;
void* buf = malloc(width * height * 4);
memcpy(buf, data, width * height * 4);
// Clean up resources
vkUnmapMemory(iris->device, dst_image_memory);
vkFreeMemory(iris->device, dst_image_memory, nullptr);
vkDestroyImage(iris->device, dst_image, nullptr);
return buf;
}
void wait_idle(iris::instance* iris) {
if (iris->device) {
vkDeviceWaitIdle(iris->device);
} else if (iris->queue) {
vkQueueWaitIdle(iris->queue);
}
}
}
================================================
FILE: main.cpp
================================================
// Standard includes
#include
#include
#include
#include
// Iris includes
#include "iris.hpp"
#include "config.hpp"
#include "ee/ee_def.hpp"
#include "ee/vu_def.hpp"
// ImGui includes
#include "imgui.h"
#include "imgui_impl_sdl3.h"
#include "imgui_impl_vulkan.h"
#include "implot.h"
// SDL3 includes
#include
// stb_image stuff
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#define SDL_MAIN_USE_CALLBACKS
#include
SDL_AppResult SDL_AppInit(void** appstate, int argc, char** argv) {
SDL_SetAppMetadata("Iris", STR(_IRIS_VERSION), "com.allkern.iris");
// Check if we got --help or --version in the commandline args
// if so, don't do anything else.
if (iris::settings::check_for_quick_exit(argc, (const char**)argv)) {
return SDL_APP_SUCCESS;
}
iris::instance* iris = iris::create();
if (!iris::init(iris, argc, (const char**)argv)) {
fprintf(stderr, "iris: Failed to initialize instance\n");
return SDL_APP_FAILURE;
}
// Initialize appstate
*appstate = iris;
return SDL_APP_CONTINUE;
}
SDL_AppResult SDL_AppIterate(void* appstate) {
iris::instance* iris = (iris::instance*)appstate;
return iris::update(iris);
}
SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) {
iris::instance* iris = (iris::instance*)appstate;
return iris::handle_events(iris, event);
}
void SDL_AppQuit(void* appstate, SDL_AppResult result) {
iris::instance* iris = (iris::instance*)appstate;
iris::destroy(iris);
}
================================================
FILE: res/iris.rc
================================================
id ICON "iris.ico"
1 VERSIONINFO
FILEVERSION 1,0,0,0
PRODUCTVERSION 1,0,0,0
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "080904E4"
BEGIN
VALUE "CompanyName", "Allkern"
VALUE "FileDescription", "An experimental PlayStation 2 Emulator"
VALUE "FileVersion", "0.10"
VALUE "ProductName", "Iris"
VALUE "ProductVersion", "0.10"
END
END
END
================================================
FILE: shaders/curvature.frag
================================================
#version 460
layout (location = 0) in vec2 TexCoord;
layout (location = 0) out vec4 FragColor;
layout (binding = 0) uniform sampler2D input_tex;
layout(push_constant) uniform constants {
vec2 resolution;
int frame;
} PushConstants;
#define PI 3.14159265358979323846
#define FIR_WIDTH 1
vec2 curvature(vec2 coord, float bend) {
// put in symmetrical coords
coord = (coord - 0.5) * 2.0;
coord *= 1.1;
// deform coords
coord.x *= 1.0 + pow((abs(coord.y) / bend), 2.0);
coord.y *= 1.0 + pow((abs(coord.x) / bend), 2.0);
// transform back to 0.0 - 1.0 space
coord = (coord / 2.0) + 0.5;
return coord;
}
const float a0 = 0.215578950;
const float a1 = 0.416631580;
const float a2 = 0.277263158;
const float a3 = 0.083578947;
const float a4 = 0.006947368;
float flat_top(float x, float n) {
return a0 - (a1 * cos((2.0 * PI * x) / n)) + (a2 * cos((4.0 * PI * x) / n)) - (a3 * cos((6.0 * PI * x) / n)) + (a4 * cos((8.0 * PI * x) / n));
}
const float freq = 9.0;
float sinc(float x) {
return sin(freq * PI * (x - 0.5)) / (freq * PI * (x - 0.5));
}
void main() {
vec2 uv = curvature(TexCoord, 5.5);
if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) {
FragColor = vec4(0.0);
return;
}
FragColor = texture(input_tex, uv).xyzw;
}
================================================
FILE: shaders/decoder.frag
================================================
#version 460
layout (location = 0) in vec2 TexCoord;
layout (location = 0) out vec4 FragColor;
layout (binding = 0) uniform sampler2D input_tex;
layout(push_constant) uniform constants {
vec2 resolution;
int frame;
} PushConstants;
// Decoder or Demodulator
// This pass takes the Composite signal generated on Buffer B
// and decodes it
#define PI 3.14159265358979323846
#define TAU 6.28318530717958647693
#define BRIGHTNESS_FACTOR 2.0
// The decoded IQ signals get multiplied by this
// factor. Bigger values yield more color saturation
#define CHROMA_SATURATION_FACTOR 2.0
// Size of the decoding FIR filter. bigger values
// yield more smudgy video and are more expensive
#define CHROMA_DECODER_FIR_SIZE 8
#define LUMA_DECODER_FIR_SIZE 4
float blackman(float n, float N) {
return 0.42 - 0.5 * cos((2.0 * PI * n) / (N - 1.0)) +
0.08 * cos((4.0 * PI * n) / (N - 1.0));
}
float sinc(float x) {
if (x == 0.0) return 1.0;
float pix = PI * x;
return sin(pix) / pix;
}
float sinc_f(float cutoff, float n, float N) {
float cut2 = cutoff * 2.0;
return cut2 * sinc(cut2 * (n - ((N - 1.0) / 2.0))) * 48.0;
}
float lowpass(float cutoff, float n, float N) {
return (sinc_f(cutoff, n, N) * 2.0 - 2.0) * blackman(n, N);
}
float highpass(float cutoff, float n, float N) {
return -(sinc_f(cutoff, n, N) * blackman(n, N));
}
// YIQ to RGB matrix
const mat3 yiq_to_rgb = mat3(
1.000, 1.000, 1.000,
0.956, -0.272, -1.106,
0.621, -0.647, 1.703
);
void main() {
vec2 uv = TexCoord * PushConstants.resolution;
// Chroma decoder oscillator frequency
float fc = 128.0;
float counter = 0.0;
// Sum and decode NTSC samples
// This is essentially a simple averaging filter
// that happens to be weighted by two cos and sin
// oscillators at a very specific frequency
vec3 yiq = vec3(0.0);
// Decode Luma first
for (int d = 0; d < 51; d++) {
vec2 p = vec2(uv.x + float(d) - 26.0, uv.y);
vec3 s = texture(input_tex, p / PushConstants.resolution).rgb;
float filt = lowpass(1.0/6.0, float(d), 51.0);
yiq.x += s.x * filt;
}
yiq.x /= 51.0;
// Then decode chroma
for (int d = 0; d < 51; d++) {
vec2 p = vec2(uv.x + float(d) - 26.0, uv.y);
vec3 s = texture(input_tex, p / PushConstants.resolution).rgb;
float t = fc * (uv.x + float(d) - 26.0) + ((PI / 2.0) * uv.y) + (PI * float((PushConstants.frame >> 1) & 1));
// Apply Blackman window for smoother colors
float filt = -highpass(0.25, float(d), 51.0);
yiq.yz += s.yz * filt * vec2(cos(t), sin(t));
}
yiq.yz /= 51.0;
yiq.yz *= CHROMA_SATURATION_FACTOR;
FragColor = vec4((yiq_to_rgb * yiq), 1.0);
}
================================================
FILE: shaders/default.frag
================================================
#version 460
layout (location = 0) in vec2 TexCoord;
layout (location = 0) out vec4 FragColor;
layout (binding = 0) uniform sampler2D input_tex;
void main() {
FragColor = vec4(texture(input_tex, TexCoord).xyz, 1.0);
}
================================================
FILE: shaders/default.vert
================================================
#version 460
layout (location = 0) in vec2 position;
layout (location = 1) in vec2 in_uv;
layout (location = 0) out vec2 out_uv;
void main() {
gl_Position = vec4(position, 0.0f, 1.0f);
out_uv = vec2(in_uv.x, 1.0 - in_uv.y);
}
================================================
FILE: shaders/encoder.frag
================================================
#version 460
layout (location = 0) in vec2 TexCoord;
layout (location = 0) out vec4 FragColor;
layout (binding = 0) uniform sampler2D input_tex;
layout(push_constant) uniform constants {
vec2 resolution;
int frame;
} PushConstants;
// Encoder or Modulator
// This pass converts RGB colors on iChannel0 to
// a YIQ (NTSC) Composite signal.
#define PI 3.14159265358979323846
#define TAU 6.28318530717958647693
const mat3 rgb_to_yiq = mat3(
0.299, 0.596, 0.211,
0.587, -0.274, -0.523,
0.114, -0.322, 0.312
);
float hash11(float p) {
p = fract(p * .1031);
p *= p + 33.33;
p *= p + p;
return fract(p);
}
float hash12(vec2 p) {
vec3 p3 = fract(vec3(p.xyx) * .1031);
p3 += dot(p3, p3.yzx + 33.33);
return fract((p3.x + p3.y) * p3.z);
}
float hash13(vec3 p3) {
p3 = fract(p3 * .1031);
p3 += dot(p3, p3.zyx + 31.32);
return fract((p3.x + p3.y) * p3.z);
}
void main() {
vec2 uv = TexCoord * PushConstants.resolution;
float noise_strength = 0.0;
// Get a pixel from iChannel0
// float noise_y = hash12(vec2(float(int(uv.y) >> 1), float(frame))) * noise_strength;
float noise_x = hash13(vec3(uv.xy, float(PushConstants.frame))) * noise_strength;
// float noise = noise_x + noise_y;
// Chroma encoder oscillator frequency
float fc = 128.0;
// Base oscillator angle for this dot
float t = uv.x;
// Get a pixel from input_texture
vec3 rgb = texture(input_tex, uv / PushConstants.resolution).rgb;
// Convert to YIQ
vec3 yiq = rgb_to_yiq * rgb;
// Final oscillator angle
float f = fc * t + ((PI / 2.0) * uv.y) + (PI * float((PushConstants.frame >> 1) & 1));
// Modulate IQ signals
float i = yiq.y * cos(f), // I signal
q = yiq.z * sin(f); // Q signal
// Add to Y to get the composite signal
float c = yiq.x + (i + q); // Composite
FragColor = vec4(vec3(c), 1.0);
}
================================================
FILE: shaders/noise.frag
================================================
#version 460
layout (location = 0) in vec2 TexCoord;
layout (location = 0) out vec4 FragColor;
layout (binding = 0) uniform sampler2D input_tex;
layout(push_constant) uniform constants {
vec2 resolution;
int frame;
} PushConstants;
float hash11(float p) {
p = fract(p * .1031);
p *= p + 33.33;
p *= p + p;
return fract(p);
}
float hash12(vec2 p) {
vec3 p3 = fract(vec3(p.xyx) * .1031);
p3 += dot(p3, p3.yzx + 33.33);
return fract((p3.x + p3.y) * p3.z);
}
float hash13(vec3 p3) {
p3 = fract(p3 * .1031);
p3 += dot(p3, p3.zyx + 31.32);
return fract((p3.x + p3.y) * p3.z);
}
void main() {
vec3 tex = texture(input_tex, TexCoord).xyz;
float noise = hash13(vec3(TexCoord * PushConstants.resolution, float(PushConstants.frame)));
if (noise < 0.1) {
if (noise < 0.05) {
tex += noise * 10.0;
} else {
tex -= noise * 10.0;
}
}
FragColor = vec4(tex, 1.0);
}
================================================
FILE: shaders/scanlines.frag
================================================
#version 460
layout (location = 0) in vec2 TexCoord;
layout (location = 0) out vec4 FragColor;
layout (binding = 0) uniform sampler2D input_tex;
layout(push_constant) uniform constants {
vec2 resolution;
int frame;
} PushConstants;
// NTSC-like shader
// Simulates scanlines, horizontal blur, and basic color bleeding
void main()
{
vec2 uv = TexCoord;
vec2 oneX = vec2(1.0 / PushConstants.resolution.x, 0.0);
// YIQ / RGB Matrices
// RGB to YIQ
const vec3 kY = vec3(0.299, 0.587, 0.114);
const vec3 kI = vec3(0.596, -0.274, -0.322);
const vec3 kQ = vec3(0.211, -0.523, 0.312);
// 1. & 3. Bandwidth extraction (Luma) & Bleed (Chroma)
// Sample a window for Chroma (Low Bandwidth)
// Sample center for Luma (High Bandwidth)
vec3 centerRGB = texture(input_tex, uv).rgb;
float y = dot(centerRGB, kY);
float iSum = 0.0;
float qSum = 0.0;
float weightSum = 0.0;
// NTSC color carrier is ~3.58MHz, bandwidth allows for ~1/3 PushConstants.resolution of luma.
// We smear chroma over a few pixels.
float bleed = 2.0;
for(float x = -bleed; x <= bleed; x += 1.0)
{
vec3 rgb = texture(input_tex, uv + oneX * x).rgb;
float i = dot(rgb, kI);
float q = dot(rgb, kQ);
// Gaussianish weight
float w = 1.0 / (1.0 + abs(x));
iSum += i * w;
qSum += q * w;
weightSum += w;
}
float finalI = iSum / weightSum;
float finalQ = qSum / weightSum;
// Convert back to RGB
// R = Y + 0.956I + 0.621Q
// G = Y - 0.272I - 0.647Q
// B = Y - 1.106I + 1.703Q
vec3 color = vec3(
y + 0.956 * finalI + 0.621 * finalQ,
y - 0.272 * finalI - 0.647 * finalQ,
y - 1.106 * finalI + 1.703 * finalQ
);
// 2. Scanlines
// Based on UV.y. 240 lines.
float scanline = sin(uv.y * 240.0 * 3.14159 * 2.0);
color *= (0.95 + 0.05 * scanline);
// 4. Gamma
// Input is usually linear or sRGB. Output to sRGB.
// Assuming input acts as sRGB for now.
FragColor = vec4(color, 1.0);
}
================================================
FILE: shaders/shader.vert
================================================
#version 460
layout (location = 0) out vec2 out_uv;
const vec2 vertices[4] = vec2[](
vec2(-1.0, -1.0),
vec2( 1.0, -1.0),
vec2( 1.0, 1.0),
vec2(-1.0, 1.0)
);
const vec2 uvs[4] = vec2[](
vec2(0.0, 1.0),
vec2(1.0, 1.0),
vec2(1.0, 0.0),
vec2(0.0, 0.0)
);
void main() {
gl_Position = vec4(vertices[gl_VertexIndex], 0.0f, 1.0f);
out_uv = uvs[gl_VertexIndex];
out_uv.y = 1.0 - out_uv.y;
}
================================================
FILE: src/dev/ds.c
================================================
#include "ds.h"
#include
#include
#define printf(fmt, ...)(0)
static inline uint8_t ds_get_model_byte(struct ds_state* ds) {
switch (ds->mode) {
case 0: return 0x41;
case 1: return 0x73;
case 2: return 0x79;
}
}
static inline void ds_cmd_set_vref_param(struct ps2_sio2* sio2, struct ds_state* ds) {
printf("ds: ds_cmd_set_vref_param\n");
queue_push(sio2->out, 0xff);
queue_push(sio2->out, 0xf3);
queue_push(sio2->out, 0x5a);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x02);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x5a);
}
static inline void ds_cmd_query_masked(struct ps2_sio2* sio2, struct ds_state* ds) {
printf("ds: ds_cmd_query_masked\n");
queue_push(sio2->out, 0xff);
queue_push(sio2->out, 0xf3);
queue_push(sio2->out, 0x5a);
if (!ds->mode) {
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
} else {
queue_push(sio2->out, ds->mask[0]);
queue_push(sio2->out, ds->mask[1]);
queue_push(sio2->out, 0x03);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x5a);
}
}
static inline void ds_cmd_read_data(struct ps2_sio2* sio2, struct ds_state* ds) {
printf("ds: ds_cmd_read_data(%04x)\n", ds->buttons);
queue_push(sio2->out, 0xff);
queue_push(sio2->out, ds_get_model_byte(ds));
queue_push(sio2->out, 0x5a);
queue_push(sio2->out, ds->buttons & 0xff);
queue_push(sio2->out, ds->buttons >> 8);
if (ds->mode) {
queue_push(sio2->out, ds->ax_right_x);
queue_push(sio2->out, ds->ax_right_y);
queue_push(sio2->out, ds->ax_left_x);
queue_push(sio2->out, ds->ax_left_y);
// Push pressure bytes (only in DualShock 2 mode)
// Note: Some games (e.g. OutRun 2 SP/2006) won't register inputs
// if the pressure values are 0, so we push the max value
// instead
if (ds->mode == 2) {
queue_push(sio2->out, 0xff);
queue_push(sio2->out, 0xff);
queue_push(sio2->out, 0xff);
queue_push(sio2->out, 0xff);
queue_push(sio2->out, 0xff);
queue_push(sio2->out, 0xff);
queue_push(sio2->out, 0xff);
queue_push(sio2->out, 0xff);
queue_push(sio2->out, 0xff);
queue_push(sio2->out, 0xff);
queue_push(sio2->out, 0xff);
queue_push(sio2->out, 0xff);
}
}
}
static inline void ds_cmd_config_mode(struct ps2_sio2* sio2, struct ds_state* ds) {
printf("ds: ds_cmd_config_mode(%02x)\n", queue_at(sio2->in, 3));
// Same as read_data, but without pressure data in DualShock 2 mode
if (!ds->config_mode) {
queue_push(sio2->out, 0xff);
// We don't use the model byte here because
// config_mode returns the same data as analog (DS1)
// when not in config mode regardless of the model
queue_push(sio2->out, ds->mode ? 0x73 : 0x41);
queue_push(sio2->out, 0x5a);
queue_push(sio2->out, ds->buttons & 0xff);
queue_push(sio2->out, ds->buttons >> 8);
if (ds->mode) {
queue_push(sio2->out, ds->ax_right_x);
queue_push(sio2->out, ds->ax_right_y);
queue_push(sio2->out, ds->ax_left_x);
queue_push(sio2->out, ds->ax_left_y);
}
} else {
queue_push(sio2->out, 0xff);
queue_push(sio2->out, 0xf3);
queue_push(sio2->out, 0x5a);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
}
ds->config_mode = queue_at(sio2->in, 3);
}
static inline void ds_cmd_set_mode(struct ps2_sio2* sio2, struct ds_state* ds) {
printf("ds: ds_cmd_set_mode(%02x, %02x)\n", queue_at(sio2->in, 3), queue_at(sio2->in, 4));
queue_push(sio2->out, 0xff);
queue_push(sio2->out, 0xf3);
queue_push(sio2->out, 0x5a);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
int mode = queue_at(sio2->in, 3);
int lock = queue_at(sio2->in, 4);
if (mode < 2 && !ds->lock) {
ds->mode = mode ? 1 : 0;
}
ds->lock = lock == 3;
}
static inline void ds_cmd_query_model(struct ps2_sio2* sio2, struct ds_state* ds) {
printf("ds: ds_cmd_query_model\n");
queue_push(sio2->out, 0xff); // Header
queue_push(sio2->out, 0xf3); // Mode (F3=Config)
queue_push(sio2->out, 0x5a);
queue_push(sio2->out, 0x03); // Model (01=Dualshock/Digital 03=Dualshock 2)
queue_push(sio2->out, 0x02);
queue_push(sio2->out, !!ds->mode); // Analog (00=no 01=yes)
queue_push(sio2->out, 0x02);
queue_push(sio2->out, 0x01);
queue_push(sio2->out, 0x00);
}
static inline void ds_cmd_query_act(struct ps2_sio2* sio2, struct ds_state* ds) {
printf("ds: ds_cmd_query_act(%02x)\n", queue_at(sio2->in, 3));
int index = queue_at(sio2->in, 3);
if (!index) {
queue_push(sio2->out, 0xff);
queue_push(sio2->out, 0xf3);
queue_push(sio2->out, 0x5a);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x01);
queue_push(sio2->out, 0x02);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x0a);
} else {
queue_push(sio2->out, 0xff);
queue_push(sio2->out, 0xf3);
queue_push(sio2->out, 0x5a);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x01);
queue_push(sio2->out, 0x01);
queue_push(sio2->out, 0x01);
queue_push(sio2->out, 0x14);
}
}
static inline void ds_cmd_query_comb(struct ps2_sio2* sio2, struct ds_state* ds) {
printf("ds: ds_cmd_query_comb\n");
queue_push(sio2->out, 0xff);
queue_push(sio2->out, 0xf3);
queue_push(sio2->out, 0x5a);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x02);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x01);
queue_push(sio2->out, 0x00);
}
static inline void ds_cmd_query_mode(struct ps2_sio2* sio2, struct ds_state* ds) {
printf("ds: ds_cmd_query_mode\n");
int index = queue_at(sio2->in, 3);
queue_push(sio2->out, 0xff);
queue_push(sio2->out, 0xf3);
queue_push(sio2->out, 0x5a);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, index ? 7 : 4);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
}
static inline void ds_cmd_vibration_toggle(struct ps2_sio2* sio2, struct ds_state* ds) {
printf("ds: ds_cmd_vibration_toggle\n");
queue_push(sio2->out, 0xff);
queue_push(sio2->out, 0xf3);
queue_push(sio2->out, 0x5a);
queue_push(sio2->out, ds->vibration[0]);
queue_push(sio2->out, ds->vibration[1]);
queue_push(sio2->out, 0xff);
queue_push(sio2->out, 0xff);
queue_push(sio2->out, 0xff);
queue_push(sio2->out, 0xff);
ds->vibration[0] = queue_at(sio2->in, 3);
ds->vibration[1] = queue_at(sio2->in, 4);
}
static inline void ds_cmd_set_native_mode(struct ps2_sio2* sio2, struct ds_state* ds) {
printf("ds: ds_cmd_set_native_mode(%02x, %02x, %02x, %02x)\n",
queue_at(sio2->in, 3),
queue_at(sio2->in, 4),
queue_at(sio2->in, 5),
queue_at(sio2->in, 6)
);
queue_push(sio2->out, 0xff);
queue_push(sio2->out, 0xf3);
queue_push(sio2->out, 0x5a);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x5a);
ds->mask[0] = queue_at(sio2->in, 3);
ds->mask[1] = queue_at(sio2->in, 4);
int value = queue_at(sio2->in, 5);
if ((value & 1) == 0) {
// Digital mode
ds->mode = 0;
} else if ((value & 2) == 0) {
// Analog mode
ds->mode = 1;
} else {
// DualShock 2 mode
ds->mode = 2;
}
}
void ds_handle_command(struct ps2_sio2* sio2, void* udata, int cmd) {
struct ds_state* ds = (struct ds_state*)udata;
switch (cmd) {
case 0x40: ds_cmd_set_vref_param(sio2, ds); return;
case 0x41: ds_cmd_query_masked(sio2, ds); return;
case 0x42: ds_cmd_read_data(sio2, ds); return;
case 0x43: ds_cmd_config_mode(sio2, ds); return;
case 0x44: ds_cmd_set_mode(sio2, ds); return;
case 0x45: ds_cmd_query_model(sio2, ds); return;
case 0x46: ds_cmd_query_act(sio2, ds); return;
case 0x47: ds_cmd_query_comb(sio2, ds); return;
case 0x4C: ds_cmd_query_mode(sio2, ds); return;
case 0x4D: ds_cmd_vibration_toggle(sio2, ds); return;
case 0x4F: ds_cmd_set_native_mode(sio2, ds); return;
}
fprintf(stderr, "ds: Unhandled command %02x\n", cmd);
// exit(1);
}
struct ds_state* ds_attach(struct ps2_sio2* sio2, int port) {
struct ds_state* ds = malloc(sizeof(struct ds_state));
struct sio2_device dev;
dev.detach = ds_detach;
dev.handle_command = ds_handle_command;
dev.udata = ds;
memset(ds, 0, sizeof(struct ds_state));
ds->port = port;
ds->ax_right_y = 0x7f;
ds->ax_right_x = 0x7f;
ds->ax_left_y = 0x7f;
ds->ax_left_x = 0x7f;
ds->buttons = 0xffff;
ds->vibration[0] = 0xff;
ds->vibration[1] = 0xff;
ds->mask[0] = 0xff;
ds->mask[1] = 0xff;
// Start in digital mode
ds->mode = 0;
ds->lock = 0;
ps2_sio2_attach_device(sio2, dev, port);
return ds;
}
void ds_button_press(struct ds_state* ds, uint32_t mask) {
if (mask == DS_BT_ANALOG) {
if (!ds->lock)
ds->mode = ds->mode ? 0 : 1;
return;
}
ds->buttons &= ~mask;
}
void ds_button_release(struct ds_state* ds, uint32_t mask) {
ds->buttons |= mask;
}
void ds_analog_change(struct ds_state* ds, int axis, uint8_t value) {
switch (axis) {
case 0: ds->ax_right_y = value; break;
case 1: ds->ax_right_x = value; break;
case 2: ds->ax_left_y = value; break;
case 3: ds->ax_left_x = value; break;
}
}
void ds_detach(void* udata) {
struct ds_state* ds = (struct ds_state*)udata;
free(ds);
}
================================================
FILE: src/dev/ds.h
================================================
#ifndef DS_H
#define DS_H
#ifdef __cplusplus
extern "C" {
#endif
#include
#include
#include "iop/sio2.h"
#define DS_BT_SELECT 0x0001
#define DS_BT_L3 0x0002
#define DS_BT_R3 0x0004
#define DS_BT_START 0x0008
#define DS_BT_UP 0x0010
#define DS_BT_RIGHT 0x0020
#define DS_BT_DOWN 0x0040
#define DS_BT_LEFT 0x0080
#define DS_BT_L2 0x0100
#define DS_BT_R2 0x0200
#define DS_BT_L1 0x0400
#define DS_BT_R1 0x0800
#define DS_BT_TRIANGLE 0x1000
#define DS_BT_CIRCLE 0x2000
#define DS_BT_CROSS 0x4000
#define DS_BT_SQUARE 0x8000
#define DS_BT_ANALOG 0x10000
#define DS_AX_RIGHT_V 0
#define DS_AX_RIGHT_H 1
#define DS_AX_LEFT_V 2
#define DS_AX_LEFT_H 3
struct ds_state {
int port;
uint16_t buttons;
uint8_t ax_right_y;
uint8_t ax_right_x;
uint8_t ax_left_y;
uint8_t ax_left_x;
int config_mode;
int act_index;
int mode_index;
int mode;
int vibration[2];
int mask[2];
int lock;
};
struct ds_state* ds_attach(struct ps2_sio2* sio2, int port);
void ds_button_press(struct ds_state* ds, uint32_t mask);
void ds_button_release(struct ds_state* ds, uint32_t mask);
void ds_analog_change(struct ds_state* ds, int axis, uint8_t value);
void ds_detach(void* udata);
#ifdef __cplusplus
}
#endif
#endif
================================================
FILE: src/dev/guncon.c
================================================
#include "guncon.h"
#include
#include
// #define printf(fmt, ...)(0)
static inline uint8_t guncon_get_model_byte(struct guncon_state* guncon) {
return 0x63;
}
static inline void guncon_cmd_set_vref_param(struct ps2_sio2* sio2, struct guncon_state* guncon) {
printf("guncon: guncon_cmd_set_vref_param\n");
queue_push(sio2->out, 0xff);
queue_push(sio2->out, 0xf3);
queue_push(sio2->out, 0x5a);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x02);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x5a);
}
static inline void guncon_cmd_query_masked(struct ps2_sio2* sio2, struct guncon_state* guncon) {
printf("guncon: guncon_cmd_query_masked\n");
queue_push(sio2->out, 0xff);
queue_push(sio2->out, 0xf3);
queue_push(sio2->out, 0x5a);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
}
static inline void guncon_cmd_read_data(struct ps2_sio2* sio2, struct guncon_state* guncon) {
printf("guncon: guncon_cmd_read_data(%04x)\n", guncon->buttons);
queue_push(sio2->out, 0xff);
queue_push(sio2->out, guncon_get_model_byte(guncon));
queue_push(sio2->out, 0x5a);
queue_push(sio2->out, guncon->buttons & 0xff);
queue_push(sio2->out, guncon->buttons >> 8);
queue_push(sio2->out, guncon->x & 0xff);
queue_push(sio2->out, guncon->x >> 8);
queue_push(sio2->out, guncon->y & 0xff);
queue_push(sio2->out, guncon->y >> 8);
}
static inline void guncon_cmd_config_mode(struct ps2_sio2* sio2, struct guncon_state* guncon) {
printf("guncon: guncon_cmd_config_mode(%02x)\n", queue_at(sio2->in, 3));
// Same as read_data, but without pressure data in DualShock 2 mode
if (!guncon->config_mode) {
queue_push(sio2->out, 0xff);
// We don't use the model byte here because
// config_mode returns the same data as analog (GUNCON1)
// when not in config mode regardless of the model
queue_push(sio2->out, 0x63);
queue_push(sio2->out, 0x5a);
queue_push(sio2->out, guncon->buttons & 0xff);
queue_push(sio2->out, guncon->buttons >> 8);
queue_push(sio2->out, guncon->x & 0xff);
queue_push(sio2->out, guncon->x >> 8);
queue_push(sio2->out, guncon->y & 0xff);
queue_push(sio2->out, guncon->y >> 8);
} else {
queue_push(sio2->out, 0xff);
queue_push(sio2->out, 0xf3);
queue_push(sio2->out, 0x5a);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
}
guncon->config_mode = queue_at(sio2->in, 3);
}
static inline void guncon_cmd_set_mode(struct ps2_sio2* sio2, struct guncon_state* guncon) {
printf("guncon: guncon_cmd_set_mode(%02x, %02x)\n", queue_at(sio2->in, 3), queue_at(sio2->in, 4));
queue_push(sio2->out, 0xff);
queue_push(sio2->out, 0xf3);
queue_push(sio2->out, 0x5a);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
}
static inline void guncon_cmd_query_model(struct ps2_sio2* sio2, struct guncon_state* guncon) {
printf("guncon: guncon_cmd_query_model\n");
queue_push(sio2->out, 0xff); // Header
queue_push(sio2->out, 0xf3); // Mode (F3=Config)
queue_push(sio2->out, 0x5a);
queue_push(sio2->out, 0x03); // Model (01=Dualshock/Digital 03=Dualshock 2)
queue_push(sio2->out, 0x02);
queue_push(sio2->out, 0x00); // Analog (00=no 01=yes)
queue_push(sio2->out, 0x02);
queue_push(sio2->out, 0x01);
queue_push(sio2->out, 0x00);
}
static inline void guncon_cmd_query_act(struct ps2_sio2* sio2, struct guncon_state* guncon) {
printf("guncon: guncon_cmd_query_act(%02x)\n", queue_at(sio2->in, 3));
int index = queue_at(sio2->in, 3);
if (!index) {
queue_push(sio2->out, 0xff);
queue_push(sio2->out, 0xf3);
queue_push(sio2->out, 0x5a);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x01);
queue_push(sio2->out, 0x02);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x0a);
} else {
queue_push(sio2->out, 0xff);
queue_push(sio2->out, 0xf3);
queue_push(sio2->out, 0x5a);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x01);
queue_push(sio2->out, 0x01);
queue_push(sio2->out, 0x01);
queue_push(sio2->out, 0x14);
}
}
static inline void guncon_cmd_query_comb(struct ps2_sio2* sio2, struct guncon_state* guncon) {
printf("guncon: guncon_cmd_query_comb\n");
queue_push(sio2->out, 0xff);
queue_push(sio2->out, 0xf3);
queue_push(sio2->out, 0x5a);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x02);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x01);
queue_push(sio2->out, 0x00);
}
static inline void guncon_cmd_query_mode(struct ps2_sio2* sio2, struct guncon_state* guncon) {
printf("guncon: guncon_cmd_query_mode\n");
int index = queue_at(sio2->in, 3);
queue_push(sio2->out, 0xff);
queue_push(sio2->out, 0xf3);
queue_push(sio2->out, 0x5a);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, index ? 7 : 4);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
}
static inline void guncon_cmd_vibration_toggle(struct ps2_sio2* sio2, struct guncon_state* guncon) {
printf("guncon: guncon_cmd_vibration_toggle\n");
queue_push(sio2->out, 0xff);
queue_push(sio2->out, 0xf3);
queue_push(sio2->out, 0x5a);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0xff);
queue_push(sio2->out, 0xff);
queue_push(sio2->out, 0xff);
queue_push(sio2->out, 0xff);
}
static inline void guncon_cmd_set_native_mode(struct ps2_sio2* sio2, struct guncon_state* guncon) {
printf("guncon: guncon_cmd_set_native_mode(%02x, %02x, %02x, %02x)\n",
queue_at(sio2->in, 3),
queue_at(sio2->in, 4),
queue_at(sio2->in, 5),
queue_at(sio2->in, 6)
);
queue_push(sio2->out, 0xff);
queue_push(sio2->out, 0xf3);
queue_push(sio2->out, 0x5a);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x5a);
}
void guncon_handle_command(struct ps2_sio2* sio2, void* udata, int cmd) {
struct guncon_state* guncon = (struct guncon_state*)udata;
switch (cmd) {
case 0x40: guncon_cmd_set_vref_param(sio2, guncon); return;
case 0x41: guncon_cmd_query_masked(sio2, guncon); return;
case 0x42: guncon_cmd_read_data(sio2, guncon); return;
case 0x43: guncon_cmd_config_mode(sio2, guncon); return;
case 0x44: guncon_cmd_set_mode(sio2, guncon); return;
case 0x45: guncon_cmd_query_model(sio2, guncon); return;
case 0x46: guncon_cmd_query_act(sio2, guncon); return;
case 0x47: guncon_cmd_query_comb(sio2, guncon); return;
case 0x4C: guncon_cmd_query_mode(sio2, guncon); return;
case 0x4D: guncon_cmd_vibration_toggle(sio2, guncon); return;
case 0x4F: guncon_cmd_set_native_mode(sio2, guncon); return;
}
printf("guncon: Unhandled command %02x\n", cmd);
exit(1);
}
struct guncon_state* guncon_attach(struct ps2_sio2* sio2, int port) {
struct guncon_state* guncon = malloc(sizeof(struct guncon_state));
struct sio2_device dev;
dev.detach = guncon_detach;
dev.handle_command = guncon_handle_command;
dev.udata = guncon;
memset(guncon, 0, sizeof(struct guncon_state));
guncon->port = port;
guncon->config_mode = 0;
guncon->x = 0x10d;
guncon->y = 0x88;
guncon->buttons = 0xffff;
ps2_sio2_attach_device(sio2, dev, port);
return guncon;
}
void guncon_button_press(struct guncon_state* guncon, uint16_t mask) {
guncon->buttons &= ~mask;
}
void guncon_button_release(struct guncon_state* guncon, uint16_t mask) {
guncon->buttons |= mask;
}
void guncon_analog_change(struct guncon_state* guncon, int axis, uint8_t value) {
switch (axis) {
case 0: guncon->x = value; break;
case 1: guncon->y = value; break;
}
}
void guncon_detach(void* udata) {
struct guncon_state* guncon = (struct guncon_state*)udata;
free(guncon);
}
================================================
FILE: src/dev/guncon.h
================================================
#ifndef GUNCON_H
#define GUNCON_H
#ifdef __cplusplus
extern "C" {
#endif
#include
#include
#include "iop/sio2.h"
#define GUNCON_BT_START 0x0008
#define GUNCON_BT_CIRCLE 0x2000
#define GUNCON_BT_CROSS 0x4000
#define GUNCON_AX_X 0
#define GUNCON_AX_Y 1
struct guncon_state {
int port;
uint16_t buttons;
uint16_t x;
uint16_t y;
int config_mode;
};
struct guncon_state* guncon_attach(struct ps2_sio2* sio2, int port);
void guncon_button_press(struct guncon_state* guncon, uint16_t mask);
void guncon_button_release(struct guncon_state* guncon, uint16_t mask);
void guncon_analog_change(struct guncon_state* guncon, int axis, uint8_t value);
void guncon_detach(void* udata);
#ifdef __cplusplus
}
#endif
#endif
================================================
FILE: src/dev/mcd.c
================================================
#include "mcd.h"
#include
#include
#include
#define printf(fmt,...)(0)
void mcd_flush_block(struct mcd_state* mcd, int addr, int size) {
fseek(mcd->file, addr, SEEK_SET);
fwrite(&mcd->buf[addr], 1, size, mcd->file);
}
void mcd_cmd_probe(struct ps2_sio2* sio2, struct mcd_state* mcd) {
printf("mcd: mcd_cmd_probe\n");
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x2b);
queue_push(sio2->out, mcd->term);
}
void mcd_cmd_unk_12(struct ps2_sio2* sio2, struct mcd_state* mcd) {
printf("mcd: mcd_cmd_unk_12\n");
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x2b);
queue_push(sio2->out, mcd->term);
}
void mcd_cmd_start_erase(struct ps2_sio2* sio2, struct mcd_state* mcd) {
uint32_t lba =
(sio2->in->buf[sio2->in->index + 2]) |
(sio2->in->buf[sio2->in->index + 3] << 8) |
(sio2->in->buf[sio2->in->index + 4] << 16) |
(sio2->in->buf[sio2->in->index + 5] << 24);
printf("mcd: mcd_cmd_start_erase(%08x)\n", lba);
mcd->addr = lba * MCD_SECTOR_SIZE;
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x2b);
queue_push(sio2->out, mcd->term);
}
void mcd_cmd_start_write(struct ps2_sio2* sio2, struct mcd_state* mcd) {
uint32_t lba =
(sio2->in->buf[sio2->in->index + 2]) |
(sio2->in->buf[sio2->in->index + 3] << 8) |
(sio2->in->buf[sio2->in->index + 4] << 16) |
(sio2->in->buf[sio2->in->index + 5] << 24);
printf("mcd: mcd_cmd_start_write(%08x)\n", lba);
mcd->addr = lba * MCD_SECTOR_SIZE;
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x2b);
queue_push(sio2->out, mcd->term);
}
void mcd_cmd_start_read(struct ps2_sio2* sio2, struct mcd_state* mcd) {
uint32_t lba =
(sio2->in->buf[sio2->in->index + 2]) |
(sio2->in->buf[sio2->in->index + 3] << 8) |
(sio2->in->buf[sio2->in->index + 4] << 16) |
(sio2->in->buf[sio2->in->index + 5] << 24);
printf("mcd: mcd_cmd_start_read(%08x)\n", lba);
mcd->addr = lba * MCD_SECTOR_SIZE;
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x2b);
queue_push(sio2->out, mcd->term);
}
void mcd_cmd_get_specs(struct ps2_sio2* sio2, struct mcd_state* mcd) {
printf("mcd: mcd_cmd_get_specs\n", sio2->in->buf[2]);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x2b);
queue_push(sio2->out, 0x00); // Sector size (2-byte)
queue_push(sio2->out, 0x02);
queue_push(sio2->out, 0x10); // Erase block size (2-byte)
queue_push(sio2->out, 0x00);
queue_push(sio2->out, (mcd->size >> 0) & 0xff); // Sector count (4-byte)
queue_push(sio2->out, (mcd->size >> 8) & 0xff);
queue_push(sio2->out, (mcd->size >> 16) & 0xff);
queue_push(sio2->out, (mcd->size >> 24) & 0xff);
queue_push(sio2->out, mcd->checksum); // Checksum
queue_push(sio2->out, mcd->term);
}
void mcd_cmd_set_terminator(struct ps2_sio2* sio2, struct mcd_state* mcd) {
printf("mcd: mcd_cmd_set_terminator(%02x)\n", sio2->in->buf[2]);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x2b);
queue_push(sio2->out, mcd->term);
mcd->term = sio2->in->buf[2];
}
void mcd_cmd_get_terminator(struct ps2_sio2* sio2, struct mcd_state* mcd) {
printf("mcd: mcd_cmd_get_terminator\n");
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x2b);
queue_push(sio2->out, mcd->term);
queue_push(sio2->out, 0x55);
}
void mcd_cmd_write_data(struct ps2_sio2* sio2, struct mcd_state* mcd) {
uint8_t size = queue_at(sio2->in, 2);
printf("mcd: mcd_cmd_write_data(%02x)\n", size);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x2b);
queue_push(sio2->out, mcd->term);
uint32_t addr = mcd->addr;
for (int i = 0; i < size; i++) {
mcd->buf[mcd->addr++] = queue_at(sio2->in, 3 + i);
queue_push(sio2->out, 0);
}
mcd_flush_block(mcd, addr, size);
queue_push(sio2->out, 0);
queue_push(sio2->out, mcd->term);
}
void mcd_cmd_read_data(struct ps2_sio2* sio2, struct mcd_state* mcd) {
printf("mcd: mcd_cmd_read_data(%02x)\n", queue_at(sio2->in, 2));
// assert(queue_at(sio2->in, 2) == 0x80);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x2b);
queue_push(sio2->out, mcd->term);
uint8_t checksum = 0;
for (int i = 0; i < queue_at(sio2->in, 2); i++) {
uint8_t data = mcd->buf[mcd->addr++];
checksum ^= data;
queue_push(sio2->out, data);
}
queue_push(sio2->out, checksum); // XOR checksum
queue_push(sio2->out, mcd->term);
}
void mcd_cmd_rw_end(struct ps2_sio2* sio2, struct mcd_state* mcd) {
printf("mcd: mcd_cmd_rw_end\n");
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x2b);
queue_push(sio2->out, mcd->term);
}
void mcd_cmd_erase_block(struct ps2_sio2* sio2, struct mcd_state* mcd) {
printf("mcd: mcd_cmd_erase_block\n");
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x2b);
queue_push(sio2->out, mcd->term);
uint32_t addr = mcd->addr;
for (int i = 0; i < MCD_SECTOR_SIZE * 16; i++) {
mcd->buf[mcd->addr++] = 0xff;
}
mcd_flush_block(mcd, addr, MCD_SECTOR_SIZE * 16);
}
void mcd_cmd_auth_f0(struct ps2_sio2* sio2, struct mcd_state* mcd) {
printf("mcd: mcd_cmd_auth_f0\n");
uint8_t param = sio2->in->buf[2];
switch (param) {
case 0x01:
case 0x02:
case 0x04:
case 0x0f:
case 0x11:
case 0x13: {
// Handle checksum
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x2b);
uint8_t checksum = 0;
for (int i = 0; i < 8; i++) {
checksum ^= sio2->in->buf[i+3];
queue_push(sio2->out, 0x00);
}
queue_push(sio2->out, checksum);
queue_push(sio2->out, mcd->term);
} break;
case 0x06:
case 0x07:
case 0x0b: {
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x2b);
queue_push(sio2->out, mcd->term);
} break;
default: {
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x2b);
queue_push(sio2->out, mcd->term);
} break;
}
}
void mcd_cmd_auth_f1(struct ps2_sio2* sio2, struct mcd_state* mcd) {
fprintf(stderr, "mcd: mcd_cmd_auth_f1\n");
fprintf(stderr, "mcd: params=");
for (int i = 0; i < 16; i++) {
fprintf(stderr, "%02x ", sio2->in->buf[2 + i]);
}
fprintf(stderr, "\n");
exit(1);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x2b);
queue_push(sio2->out, mcd->term);
}
void mcd_cmd_auth_f3(struct ps2_sio2* sio2, struct mcd_state* mcd) {
printf("mcd: mcd_cmd_auth_f3\n");
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x2b);
queue_push(sio2->out, mcd->term);
}
void mcd_cmd_auth_f7(struct ps2_sio2* sio2, struct mcd_state* mcd) {
printf("mcd: mcd_cmd_auth_f7\n");
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x2b);
queue_push(sio2->out, mcd->term);
}
void mcd_cmd_unk_bf(struct ps2_sio2* sio2, struct mcd_state* mcd) {
printf("mcd: mcd_cmd_unk_bf\n");
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x2b);
queue_push(sio2->out, mcd->term);
}
void mcd_handle_command(struct ps2_sio2* sio2, void* udata, int cmd) {
struct mcd_state* mcd = (struct mcd_state*)udata;
switch (cmd) {
case 0x11: mcd_cmd_probe(sio2, mcd); return;
case 0x12: mcd_cmd_unk_12(sio2, mcd); return;
case 0x21: mcd_cmd_start_erase(sio2, mcd); return;
case 0x22: mcd_cmd_start_write(sio2, mcd); return;
case 0x23: mcd_cmd_start_read(sio2, mcd); return;
case 0x26: mcd_cmd_get_specs(sio2, mcd); return;
case 0x27: mcd_cmd_set_terminator(sio2, mcd); return;
case 0x28: mcd_cmd_get_terminator(sio2, mcd); return;
case 0x42: mcd_cmd_write_data(sio2, mcd); return;
case 0x43: mcd_cmd_read_data(sio2, mcd); return;
// case 0x52: mcd_cmd_ps1_read(sio2, mcd); return;
case 0x81: mcd_cmd_rw_end(sio2, mcd); return;
case 0x82: mcd_cmd_erase_block(sio2, mcd); return;
case 0xf0: mcd_cmd_auth_f0(sio2, mcd); return;
case 0xf1: mcd_cmd_auth_f1(sio2, mcd); return;
case 0xf3: mcd_cmd_auth_f3(sio2, mcd); return;
case 0xf7: mcd_cmd_auth_f7(sio2, mcd); return;
case 0xbf: mcd_cmd_unk_bf(sio2, mcd); return;
}
printf("mcd: Unhandled command %02x\n", cmd);
exit(1);
}
struct mcd_state* mcd_attach(struct ps2_sio2* sio2, int port, const char* path) {
FILE* file = fopen(path, "r+b");
if (!file)
return NULL;
struct mcd_state* mcd = malloc(sizeof(struct mcd_state));
struct sio2_device dev;
memset(mcd, 0, sizeof(struct mcd_state));
// Get memcard size
fseek(file, 0, SEEK_END);
mcd->buf_size = ftell(file);
fseek(file, 0, SEEK_SET);
mcd->buf = (uint8_t*)malloc(mcd->buf_size);
fread(mcd->buf, 1, mcd->buf_size, file);
// Init card state
mcd->term = 0x55;
mcd->file = file;
mcd->size = (1 << (31 - __builtin_clz(mcd->buf_size))) >> 9;
mcd->checksum = 0x02 ^ 0x10;
for (int i = 0; i < 4; i++)
mcd->checksum ^= (mcd->size >> (i * 8)) & 0xff;
printf("mcd: Memory card at \'%s\' initialized.\n\tTotal size: %x (%d)\n\tSize (in sectors): %x (%d)\n\tChecksum: %02x\n",
path, mcd->buf_size, mcd->buf_size,
mcd->size, mcd->size,
mcd->checksum
);
dev.detach = mcd_detach;
dev.handle_command = mcd_handle_command;
dev.udata = mcd;
ps2_sio2_attach_device(sio2, dev, port);
return mcd;
}
void mcd_detach(void* udata) {
struct mcd_state* mcd = (struct mcd_state*)udata;
// Flush buffer back to file
fseek(mcd->file, 0, SEEK_SET);
fwrite(mcd->buf, 1, mcd->buf_size, mcd->file);
fclose(mcd->file);
free(mcd->buf);
free(mcd);
}
================================================
FILE: src/dev/mcd.h
================================================
#ifndef MCD_H
#define MCD_H
#ifdef __cplusplus
extern "C" {
#endif
#include
#include
#include "iop/sio2.h"
#define MCD_SIZE_8MB 0x4000
#define MCD_SIZE_16MB 0x8000
#define MCD_SIZE_32MB 0x10000
#define MCD_SIZE_64MB 0x20000
// 512 bytes data + 16 bytes ECC
#define MCD_SECTOR_SIZE (512+16)
struct mcd_state {
int port;
uint8_t term;
uint16_t buttons;
uint8_t ax_right_y;
uint8_t ax_right_x;
uint8_t ax_left_y;
uint8_t ax_left_x;
int config_mode;
int act_index;
int mode_index;
uint32_t size;
uint8_t checksum;
uint32_t addr;
uint32_t buf_size;
uint8_t* buf;
FILE* file;
};
struct mcd_state* mcd_attach(struct ps2_sio2* sio2, int port, const char* path);
void mcd_detach(void* udata);
#ifdef __cplusplus
}
#endif
#endif
================================================
FILE: src/dev/mtap.c
================================================
#include
#include
#include "mtap.h"
#define printf(fmt,...)(0)
void mtap_cmd_probe(struct ps2_sio2* sio2, struct mtap_state* mtap) {
printf("mtap: mtap_cmd_probe\n");
queue_push(sio2->out, 0xff);
queue_push(sio2->out, 0x80);
queue_push(sio2->out, 0x5a);
queue_push(sio2->out, 0x04);
queue_push(sio2->out, 0x00);
queue_push(sio2->out, 0x5a);
}
void mtap_handle_command(struct ps2_sio2* sio2, void* udata, int cmd) {
struct mtap_state* mtap = (struct mtap_state*)udata;
switch (cmd) {
case 0x12: mtap_cmd_probe(sio2, mtap); return;
}
printf("mtap: Unhandled command %02x\n", cmd);
exit(1);
}
struct mtap_state* mtap_attach(struct ps2_sio2* sio2, int port) {
struct mtap_state* mtap = malloc(sizeof(struct mtap_state));
struct sio2_device dev;
dev.detach = mtap_detach;
dev.handle_command = mtap_handle_command;
dev.udata = mtap;
memset(mtap, 0, sizeof(struct mtap_state));
ps2_sio2_attach_device(sio2, dev, port);
return mtap;
}
void mtap_detach(void* udata) {
struct mtap_state* mtap = (struct mtap_state*)udata;
free(mtap);
}
================================================
FILE: src/dev/mtap.h
================================================
#ifndef MTAP_H
#define MTAP_H
#ifdef __cplusplus
extern "C" {
#endif
#include
#include
#include "iop/sio2.h"
struct mtap_state {
struct sio2_device port[8];
};
struct mtap_state* mtap_attach(struct ps2_sio2* sio2, int port);
void mtap_detach(void* udata);
#ifdef __cplusplus
}
#endif
#endif
================================================
FILE: src/dev/ps1_mcd.c
================================================
#include "ps1_mcd.h"
#include