[EXPERIMENTAL] Add Rust support

This commit is contained in:
Fabrice de Gans 2024-03-09 14:38:47 -08:00
parent 68adb14b07
commit f4dcd55f98
29 changed files with 533 additions and 166 deletions

View File

@ -11,6 +11,9 @@ jobs:
runs-on: macos-latest runs-on: macos-latest
steps: steps:
- name: Install Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1
- name: Checkout the code - name: Checkout the code
uses: actions/checkout@v2 uses: actions/checkout@v2
with: with:

View File

@ -15,6 +15,9 @@ jobs:
shell: msys2 {0} shell: msys2 {0}
steps: steps:
- name: Install Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1
- name: Checkout the code - name: Checkout the code
uses: actions/checkout@v2 uses: actions/checkout@v2
with: with:

View File

@ -10,6 +10,9 @@ jobs:
cmake_options: ['', '-DENABLE_LINK=OFF', '-DENABLE_SDL=ON'] cmake_options: ['', '-DENABLE_LINK=OFF', '-DENABLE_SDL=ON']
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Install Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1
- name: Checkout the code - name: Checkout the code
uses: actions/checkout@v2 uses: actions/checkout@v2
with: with:

View File

@ -12,6 +12,9 @@ jobs:
cmake_options: ['', '-DENABLE_LINK=OFF', '-DENABLE_SDL=ON'] cmake_options: ['', '-DENABLE_LINK=OFF', '-DENABLE_SDL=ON']
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- name: Install Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1
- name: Checkout the code - name: Checkout the code
uses: actions/checkout@v2 uses: actions/checkout@v2
with: with:

4
.gitignore vendored
View File

@ -17,3 +17,7 @@ compile_commands.json
# mac finder crap # mac finder crap
*.DS_Store *.DS_Store
# Rust build files
src/core/rust/target/
src/core/rust/Cargo.lock

View File

@ -74,6 +74,15 @@ set(CMAKE_CXX_STANDARD_REQUIRED True)
set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED True) set(CMAKE_C_STANDARD_REQUIRED True)
# Rust library dependency
include(FetchContent)
FetchContent_Declare(
Corrosion
GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git
GIT_TAG v0.4
)
FetchContent_MakeAvailable(Corrosion)
project(VBA-M C CXX) project(VBA-M C CXX)
find_package(PkgConfig) find_package(PkgConfig)
@ -263,15 +272,6 @@ add_compile_definitions(PKGDATADIR="${CMAKE_INSTALL_FULL_DATADIR}/vbam")
add_compile_definitions(__STDC_FORMAT_MACROS) add_compile_definitions(__STDC_FORMAT_MACROS)
if(ENABLE_LINK) if(ENABLE_LINK)
# IPC linking code needs sem_timedwait which can be either in librt or pthreads
if(NOT WIN32)
find_library(RT_LIB rt)
if(RT_LIB)
set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${RT_LIB})
set(VBAMCORE_LIBS ${VBAMCORE_LIBS} ${RT_LIB})
endif()
endif()
include(CheckFunctionExists) include(CheckFunctionExists)
check_function_exists(sem_timedwait SEM_TIMEDWAIT) check_function_exists(sem_timedwait SEM_TIMEDWAIT)
if(SEM_TIMEDWAIT) if(SEM_TIMEDWAIT)

View File

@ -218,7 +218,6 @@ Here is the complete list:
| ENABLE_GBA_LOGGING | Enable extended GBA logging | ON | | ENABLE_GBA_LOGGING | Enable extended GBA logging | ON |
| ENABLE_DIRECT3D | Direct3D rendering for wxWidgets (Windows, **NOT IMPLEMENTED!!!**) | ON | | ENABLE_DIRECT3D | Direct3D rendering for wxWidgets (Windows, **NOT IMPLEMENTED!!!**) | ON |
| ENABLE_XAUDIO2 | Enable xaudio2 sound output for wxWidgets (Windows only) | ON | | ENABLE_XAUDIO2 | Enable xaudio2 sound output for wxWidgets (Windows only) | ON |
| ENABLE_OPENAL | Enable OpenAL for the wxWidgets port | AUTO |
| ENABLE_ASAN | Enable libasan sanitizers (by default address, only in debug mode) | OFF | | ENABLE_ASAN | Enable libasan sanitizers (by default address, only in debug mode) | OFF |
| UPSTREAM_RELEASE | Do some release tasks, like codesigning, making zip and gpg sigs. | OFF | | UPSTREAM_RELEASE | Do some release tasks, like codesigning, making zip and gpg sigs. | OFF |
| BUILD_TESTING | Build the tests and enable ctest support. | ON | | BUILD_TESTING | Build the tests and enable ctest support. | ON |

21
cmake/Flags.cmake Normal file
View File

@ -0,0 +1,21 @@
# Compiler stuff
include(ProcessorCount)
ProcessorCount(num_cpus)
if(CMAKE_C_COMPILER_ID STREQUAL Clang AND CMAKE_CXX_COMPILER_ID STREQUAL Clang AND NOT MSVC)
# TODO: This should also be done for clang-cl.
include(LLVMToolchain)
endif()
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID STREQUAL Clang AND NOT MSVC)
include(Toolchain-gcc-clang)
elseif(MSVC)
include(Toolchain-msvc)
elseif(MINGW)
include(Toolchain-mingw)
endif()
# Assembler flags
if(ASM_ENABLED)
string(REGEX REPLACE "<FLAGS>" "-I${CMAKE_SOURCE_DIR}/src/filters/hq/asm/ -O1 -w-orphan-labels" CMAKE_ASM_NASM_COMPILE_OBJECT ${CMAKE_ASM_NASM_COMPILE_OBJECT})
endif()

44
fex/CMakeLists.txt Normal file
View File

@ -0,0 +1,44 @@
#Do not use this file directly. Always use the top level CMakeLists.txt file
#File extractors so the user doesn't have to extract the rom before playing it
# Source files definition
SET(SRC_FEX
7z_C/7zAlloc.c
7z_C/7zBuf.c
7z_C/7zCrc.c
7z_C/7zCrcOpt.c
7z_C/7zDec.c
7z_C/7zIn.c
7z_C/7zStream.c
7z_C/Bcj2.c
7z_C/Bra86.c
7z_C/Bra.c
7z_C/CpuArch.c
7z_C/Lzma2Dec.c
7z_C/LzmaDec.c
7z_C/Ppmd7.c
7z_C/Ppmd7Dec.c
fex/Binary_Extractor.cpp
fex/blargg_common.cpp
fex/blargg_errors.cpp
fex/Data_Reader.cpp
fex/fex.cpp
fex/File_Extractor.cpp
fex/Gzip_Extractor.cpp
fex/Gzip_Reader.cpp
fex/Rar_Extractor.cpp
fex/Zip7_Extractor.cpp
fex/Zip_Extractor.cpp
fex/Zlib_Inflater.cpp
)
ADD_LIBRARY(
fex
STATIC
${SRC_FEX}
)
add_dependencies(fex generate)
target_include_directories(fex PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
target_link_libraries(fex PUBLIC ZLIB::ZLIB)

View File

@ -1,7 +1,6 @@
#!/bin/sh #!/bin/sh
CMAKE=cmake CMAKE=cmake
ENABLE_OPENAL=1
ENABLE_FFMPEG=1 ENABLE_FFMPEG=1
main() { main() {
@ -25,10 +24,6 @@ check_command_line_args() {
usage usage
quit 0 quit 0
;; ;;
--no-openal)
ENABLE_OPENAL=
shift
;;
--no-ffmpeg) --no-ffmpeg)
ENABLE_FFMPEG= ENABLE_FFMPEG=
shift shift
@ -355,9 +350,8 @@ debian_installdeps() {
;; ;;
esac esac
pkgs="build-essential g++ nasm cmake ccache gettext zlib1g-dev libgl1-mesa-dev libgettextpo-dev libsdl2-dev $sdl_lib libglu1-mesa-dev libglu1-mesa libgles2-mesa-dev libsfml-dev $sfml_libs $glew_lib $wx_libs libgtk2.0-dev libgtk-3-dev ccache zip ninja-build" pkgs="build-essential g++ nasm cmake ccache gettext zlib1g-dev libgl1-mesa-dev libgettextpo-dev libsdl2-dev $sdl_lib libglu1-mesa-dev libglu1-mesa libgles2-mesa-dev libsfml-dev $sfml_libs $glew_lib $wx_libs libgtk2.0-dev libgtk-3-dev ccache zip ninja-build libopenal-dev"
[ -n "$ENABLE_OPENAL" ] && pkgs="$pkgs libopenal-dev"
[ -n "$ENABLE_FFMPEG" ] && pkgs="$pkgs libavcodec-dev libavformat-dev libswscale-dev libavutil-dev $libswresample_dev" [ -n "$ENABLE_FFMPEG" ] && pkgs="$pkgs libavcodec-dev libavformat-dev libswscale-dev libavutil-dev $libswresample_dev"
check sudo apt-get -qy install $pkgs check sudo apt-get -qy install $pkgs
@ -407,8 +401,7 @@ debian_installdeps() {
fi fi
fi fi
deps="gcc zlib ffmpeg gettext sdl2 sfml openal wxwidgets" deps="gcc zlib ffmpeg gettext sdl2 sfml openal wxwidgets openal"
[ -n "$ENABLE_OPENAL" ] && deps="$deps openal"
[ -n "$ENABLE_FFMPEG" ] && deps="$deps ffmpeg" [ -n "$ENABLE_FFMPEG" ] && deps="$deps ffmpeg"
set -- set --
@ -510,9 +503,6 @@ fedora_installdeps() {
*ffmpeg*) *ffmpeg*)
[ -z "$ENABLE_FFMPEG" ] && continue [ -z "$ENABLE_FFMPEG" ] && continue
;; ;;
*openal*)
[ -z "$ENABLE_OPENAL" ] && continue
;;
esac esac
pkg_arch= pkg_arch=
@ -601,15 +591,9 @@ fedora_installdeps() {
;; ;;
esac esac
# install static deps # install static deps
for pkg in zlib gettext SDL2 wxWidgets3; do for pkg in zlib gettext SDL2 wxWidgets3 openal-soft; do
set -- "$@" "${target}-${pkg}-static" set -- "$@" "${target}-${pkg}-static"
done done
# install deps that are not available as static
if [ -n "$ENABLE_OPENAL" ]; then
for pkg in openal-soft; do
set -- "$@" "${target}-${pkg}"
done
fi
# get the necessary win32 headers # get the necessary win32 headers
git submodule update --init --remote --recursive git submodule update --init --remote --recursive
@ -707,9 +691,6 @@ rhel_installdeps() {
*ffmpeg*) *ffmpeg*)
[ -z "$ENABLE_FFMPEG" ] && continue [ -z "$ENABLE_FFMPEG" ] && continue
;; ;;
*openal*)
[ -z "$ENABLE_OPENAL" ] && continue
;;
esac esac
if [ -n "$amd64" ]; then if [ -n "$amd64" ]; then
@ -790,15 +771,9 @@ rhel_installdeps() {
;; ;;
esac esac
# install static deps # install static deps
for pkg in zlib gettext SDL2 wxWidgets; do for pkg in zlib gettext SDL2 wxWidgets openal-soft; do
set -- "$@" "${target}-${pkg}-static" set -- "$@" "${target}-${pkg}-static"
done done
# install deps that are not available as static
if [ -n "$ENABLE_OPENAL" ]; then
for pkg in openal-soft; do
set -- "$@" "${target}-${pkg}"
done
fi
# get the necessary win32 headers # get the necessary win32 headers
git submodule update --init --remote --recursive git submodule update --init --remote --recursive
@ -824,11 +799,9 @@ suse_installdeps() {
tools="make cmake ccache nasm gettext-tools pkg-config ccache zip sfml2-devel ninja" tools="make cmake ccache nasm gettext-tools pkg-config ccache zip sfml2-devel ninja"
libs="gcc gcc-c++ libSDL2-devel wxWidgets-3_0-devel" # ffmpeg-devel libs="gcc gcc-c++ libSDL2-devel wxWidgets-3_0-devel openal-soft-devel" # ffmpeg-devel
[ -n "$ENABLE_OPENAL" ] && libs="$libs openal-soft-devel"
# ffmpeg requires packman repos # ffmpeg requires packman repos
if [ "$target" = m32 ]; then if [ "$target" = m32 ]; then
libs=$(echo "$libs" | sed -E 's/([^ ]) ([^ ])/\1-32bit \2/g; s/$/-32bit/;') libs=$(echo "$libs" | sed -E 's/([^ ]) ([^ ])/\1-32bit \2/g; s/$/-32bit/;')
fi fi
@ -894,9 +867,8 @@ archlinux_installdeps() {
$pacman -Q gtk3-classic >/dev/null 2>&1 && gtk=gtk3-classic $pacman -Q gtk3-classic >/dev/null 2>&1 && gtk=gtk3-classic
libs="zlib mesa gettext sdl2 wxgtk3 $gtk sfml" libs="zlib mesa gettext sdl2 wxgtk3 $gtk sfml openal"
[ -n "$ENABLE_OPENAL" ] && libs="$libs openal"
[ -n "$ENABLE_FFMPEG" ] && libs="$libs ffmpeg" [ -n "$ENABLE_FFMPEG" ] && libs="$libs ffmpeg"
if [ -z "$target" -o "$target" = m32 ]; then if [ -z "$target" -o "$target" = m32 ]; then
@ -991,9 +963,7 @@ EOF
fi fi
done done
deps="zlib gettext pkg-config sdl2 wxmsw" deps="zlib gettext pkg-config sdl2 wxmsw openal"
[ -n "$ENABLE_OPENAL" ] && deps="$deps openal"
# and the actual deps # and the actual deps
for p in $deps; do for p in $deps; do
@ -1024,9 +994,7 @@ solus_installdeps() {
check sudo eopkg -y install -c system.devel check sudo eopkg -y install -c system.devel
check sudo eopkg -y install git ccache ninja check sudo eopkg -y install git ccache ninja
set -- sdl2-devel wxwidgets-devel libgtk-2-devel libgtk-3-devel libglu-devel set -- sdl2-devel wxwidgets-devel libgtk-2-devel libgtk-3-devel libglu-devel openal-soft-devel
[ -n "$ENABLE_OPENAL" ] && set -- "$@" openal-soft-devel
if [ -n "$amd64" -a "$target" = m32 ]; then if [ -n "$amd64" -a "$target" = m32 ]; then
info_msg 'Calculating dependencies, this will take a while..' info_msg 'Calculating dependencies, this will take a while..'
@ -1102,14 +1070,13 @@ gentoo_installdeps() {
sys-devel/binutils \ sys-devel/binutils \
media-libs/libsdl2 \ media-libs/libsdl2 \
media-libs/libsfml \ media-libs/libsfml \
media-libs/openal \
x11-libs/wxGTK:$wx_slot \ x11-libs/wxGTK:$wx_slot \
sys-libs/zlib \ sys-libs/zlib \
dev-util/pkgconf \ dev-util/pkgconf \
dev-lang/nasm \ dev-lang/nasm \
dev-build/ninja" dev-build/ninja"
[ -n "$ENABLE_OPENAL" ] && ebuilds="$ebuilds media-libs/openal"
[ -n "$ENABLE_FFMPEG" ] && ebuilds="$ebuilds media-video/ffmpeg" [ -n "$ENABLE_FFMPEG" ] && ebuilds="$ebuilds media-video/ffmpeg"
check sudo emerge -vna $ebuilds check sudo emerge -vna $ebuilds
@ -1152,9 +1119,8 @@ windows_installdeps() {
;; ;;
esac esac
pkgs="$pkgs SDL2 sfml wxWidgets3.2 zlib binutils cmake crt-git extra-cmake-modules headers-git make pkgconf tools-git windows-default-manifest libmangle-git ninja gdb ccache" pkgs="$pkgs SDL2 sfml wxWidgets3.2 zlib binutils cmake crt-git extra-cmake-modules headers-git make pkgconf tools-git windows-default-manifest libmangle-git ninja gdb ccache openal"
[ -n "$ENABLE_OPENAL" ] && pkgs="$pkgs openal"
[ -n "$ENABLE_FFMPEG" ] && pkgs="$pkgs ffmpeg" [ -n "$ENABLE_FFMPEG" ] && pkgs="$pkgs ffmpeg"
set -- set --

3
rust-roolchain.toml Normal file
View File

@ -0,0 +1,3 @@
[toolchain]
channel = "stable"
profile = "minimal"

View File

@ -51,7 +51,6 @@ parts:
- libopenal-dev - libopenal-dev
- libwxgtk3.0-gtk3-dev - libwxgtk3.0-gtk3-dev
cmake-parameters: cmake-parameters:
- -DENABLE_OPENAL=ON
- -DENABLE_SDL=OFF - -DENABLE_SDL=OFF
- -DCMAKE_INSTALL_PREFIX=/usr - -DCMAKE_INSTALL_PREFIX=/usr

View File

@ -2,6 +2,9 @@ add_subdirectory(apu)
add_subdirectory(base) add_subdirectory(base)
add_subdirectory(fex) add_subdirectory(fex)
# Set up "vbam_rust" library.
corrosion_import_crate(MANIFEST_PATH ${CMAKE_CURRENT_LIST_DIR}/rust/Cargo.toml NO_STD)
# The vbam-core target contains both the Game Boy and Game Boy Advance # The vbam-core target contains both the Game Boy and Game Boy Advance
# emulators. These should be broken down into 2 separate targets. The issue lies # emulators. These should be broken down into 2 separate targets. The issue lies
# with the Link and Sound emulation, which are tangled together between the two # with the Link and Sound emulation, which are tangled together between the two
@ -94,7 +97,7 @@ target_include_directories(vbam-core
target_link_libraries(vbam-core target_link_libraries(vbam-core
PRIVATE vbam-core-apu vbam-fex PRIVATE vbam-core-apu vbam-fex
PUBLIC vbam-core-base ${ZLIB_LIBRARY} PUBLIC vbam-core-base ${ZLIB_LIBRARY} vbam_rust
) )
if(ENABLE_DEBUGGER) if(ENABLE_DEBUGGER)

View File

@ -6,8 +6,11 @@
#include "core/base/file_util.h" #include "core/base/file_util.h"
#include "core/base/message.h" #include "core/base/message.h"
#include "core/gba/gba.h" #include "core/gba/gba.h"
#include "core/gba/gbaInline.h"
#include "core/gba/gbaGlobals.h" #include "core/gba/gbaGlobals.h"
#include "core/gba/gbaInline.h"
#include "core/rust/bindings.hpp"
using namespace core;
/** /**
* Gameshark code types: (based on AR v1.0) * Gameshark code types: (based on AR v1.0)
@ -672,7 +675,7 @@ int cheatsCheckKeys(uint32_t keys, uint32_t extended)
case GSA_16_BIT_ROM_PATCH: case GSA_16_BIT_ROM_PATCH:
if ((cheatsList[i].status & 1) == 0) { if ((cheatsList[i].status & 1) == 0) {
if (CPUReadHalfWord(cheatsList[i].address) != cheatsList[i].value) { if (CPUReadHalfWord(cheatsList[i].address) != cheatsList[i].value) {
cheatsList[i].oldValue = CPUReadHalfWord(cheatsList[i].address); cheatsList[i].old_value = CPUReadHalfWord(cheatsList[i].address);
cheatsList[i].status |= 1; cheatsList[i].status |= 1;
CHEAT_PATCH_ROM_16BIT(cheatsList[i].address, cheatsList[i].value); CHEAT_PATCH_ROM_16BIT(cheatsList[i].address, cheatsList[i].value);
} }
@ -1280,6 +1283,33 @@ int cheatsCheckKeys(uint32_t keys, uint32_t extended)
return ticks; return ticks;
} }
void cheatsAdd(CheatsData data) {
if (cheatsNumber == MAX_CHEATS) {
return;
}
int x = cheatsNumber;
cheatsList[x] = std::move(data);
switch (cheatsList[x].size) {
case INT_8_BIT_WRITE:
cheatsList[x].old_value = CPUReadByte(cheatsList[x].address);
break;
case INT_16_BIT_WRITE:
cheatsList[x].old_value = CPUReadHalfWord(cheatsList[x].address);
break;
case INT_32_BIT_WRITE:
cheatsList[x].old_value = CPUReadMemory(cheatsList[x].address);
break;
case CHEATS_16_BIT_WRITE:
cheatsList[x].old_value = CPUReadHalfWord(cheatsList[x].address);
break;
case CHEATS_32_BIT_WRITE:
cheatsList[x].old_value = CPUReadMemory(cheatsList[x].address);
break;
}
cheatsNumber++;
}
void cheatsAdd(const char* codeStr, void cheatsAdd(const char* codeStr,
const char* desc, const char* desc,
uint32_t rawaddress, uint32_t rawaddress,
@ -1304,19 +1334,19 @@ void cheatsAdd(const char* codeStr,
// is taken care when it actually patches the ROM // is taken care when it actually patches the ROM
switch (cheatsList[x].size) { switch (cheatsList[x].size) {
case INT_8_BIT_WRITE: case INT_8_BIT_WRITE:
cheatsList[x].oldValue = CPUReadByte(address); cheatsList[x].old_value = CPUReadByte(address);
break; break;
case INT_16_BIT_WRITE: case INT_16_BIT_WRITE:
cheatsList[x].oldValue = CPUReadHalfWord(address); cheatsList[x].old_value = CPUReadHalfWord(address);
break; break;
case INT_32_BIT_WRITE: case INT_32_BIT_WRITE:
cheatsList[x].oldValue = CPUReadMemory(address); cheatsList[x].old_value = CPUReadMemory(address);
break; break;
case CHEATS_16_BIT_WRITE: case CHEATS_16_BIT_WRITE:
cheatsList[x].oldValue = CPUReadHalfWord(address); cheatsList[x].old_value = CPUReadHalfWord(address);
break; break;
case CHEATS_32_BIT_WRITE: case CHEATS_32_BIT_WRITE:
cheatsList[x].oldValue = CPUReadMemory(address); cheatsList[x].old_value = CPUReadMemory(address);
break; break;
} }
cheatsNumber++; cheatsNumber++;
@ -1331,33 +1361,33 @@ void cheatsDelete(int number, bool restore)
if (restore) { if (restore) {
switch (cheatsList[x].size) { switch (cheatsList[x].size) {
case INT_8_BIT_WRITE: case INT_8_BIT_WRITE:
CPUWriteByte(cheatsList[x].address, (uint8_t)cheatsList[x].oldValue); CPUWriteByte(cheatsList[x].address, (uint8_t)cheatsList[x].old_value);
break; break;
case INT_16_BIT_WRITE: case INT_16_BIT_WRITE:
CPUWriteHalfWord(cheatsList[x].address, (uint16_t)cheatsList[x].oldValue); CPUWriteHalfWord(cheatsList[x].address, (uint16_t)cheatsList[x].old_value);
break; break;
case INT_32_BIT_WRITE: case INT_32_BIT_WRITE:
CPUWriteMemory(cheatsList[x].address, cheatsList[x].oldValue); CPUWriteMemory(cheatsList[x].address, cheatsList[x].old_value);
break; break;
case CHEATS_16_BIT_WRITE: case CHEATS_16_BIT_WRITE:
if ((cheatsList[x].address >> 24) >= 0x08) { if ((cheatsList[x].address >> 24) >= 0x08) {
CHEAT_PATCH_ROM_16BIT(cheatsList[x].address, cheatsList[x].oldValue); CHEAT_PATCH_ROM_16BIT(cheatsList[x].address, cheatsList[x].old_value);
} else { } else {
CPUWriteHalfWord(cheatsList[x].address, cheatsList[x].oldValue); CPUWriteHalfWord(cheatsList[x].address, cheatsList[x].old_value);
} }
break; break;
case CHEATS_32_BIT_WRITE: case CHEATS_32_BIT_WRITE:
if ((cheatsList[x].address >> 24) >= 0x08) { if ((cheatsList[x].address >> 24) >= 0x08) {
CHEAT_PATCH_ROM_32BIT(cheatsList[x].address, cheatsList[x].oldValue); CHEAT_PATCH_ROM_32BIT(cheatsList[x].address, cheatsList[x].old_value);
} else { } else {
CPUWriteMemory(cheatsList[x].address, cheatsList[x].oldValue); CPUWriteMemory(cheatsList[x].address, cheatsList[x].old_value);
} }
/* fallthrough */ /* fallthrough */
case GSA_16_BIT_ROM_PATCH: case GSA_16_BIT_ROM_PATCH:
if (cheatsList[x].status & 1) { if (cheatsList[x].status & 1) {
cheatsList[x].status &= ~1; cheatsList[x].status &= ~1;
CHEAT_PATCH_ROM_16BIT(cheatsList[x].address, CHEAT_PATCH_ROM_16BIT(cheatsList[x].address,
cheatsList[x].oldValue); cheatsList[x].old_value);
} }
break; break;
case GSA_16_BIT_ROM_PATCH2C: case GSA_16_BIT_ROM_PATCH2C:
@ -1403,7 +1433,7 @@ void cheatsDisable(int i)
if (cheatsList[i].status & 1) { if (cheatsList[i].status & 1) {
cheatsList[i].status &= ~1; cheatsList[i].status &= ~1;
CHEAT_PATCH_ROM_16BIT(cheatsList[i].address, CHEAT_PATCH_ROM_16BIT(cheatsList[i].address,
cheatsList[i].oldValue); cheatsList[i].old_value);
} }
break; break;
case GSA_16_BIT_ROM_PATCH2C: case GSA_16_BIT_ROM_PATCH2C:
@ -1424,72 +1454,15 @@ void cheatsDisable(int i)
bool cheatsVerifyCheatCode(const char* code, const char* desc) bool cheatsVerifyCheatCode(const char* code, const char* desc)
{ {
size_t len = strlen(code); auto converted = core::decode_code(core::GbaCheatsType::Generic, code, desc);
if (len != 11 && len != 13 && len != 17) { if (converted.tag ==
systemMessage(MSG_INVALID_CHEAT_CODE, N_("Invalid cheat code '%s': wrong length"), code); core::Result_CheatsData__CheatsDecodeError_Tag::Err_CheatsData__CheatsDecodeError) {
systemMessage(MSG_INVALID_CHEAT_CODE, N_("Invalid cheat code '%s': %s"), code,
converted.err);
return false; return false;
} }
if (code[8] != ':') { cheatsAdd(std::move(converted.ok));
systemMessage(MSG_INVALID_CHEAT_CODE, N_("Invalid cheat code '%s': no colon"), code);
return false;
}
size_t i;
for (i = 0; i < 8; i++) {
if (!CHEAT_IS_HEX(code[i])) {
// wrong cheat
systemMessage(MSG_INVALID_CHEAT_CODE,
N_("Invalid cheat code '%s': first part is not hex"), code);
return false;
}
}
for (i = 9; i < len; i++) {
if (!CHEAT_IS_HEX(code[i])) {
// wrong cheat
systemMessage(MSG_INVALID_CHEAT_CODE,
N_("Invalid cheat code '%s' second part is not hex"), code);
return false;
}
}
uint32_t address = 0;
uint32_t value = 0;
char buffer[10];
strncpy(buffer, code, 8);
buffer[8] = 0;
sscanf(buffer, "%x", &address);
switch (address >> 24) {
case 0x02:
case 0x03:
case 0x04:
case 0x05:
case 0x06:
case 0x07:
case 0x08:
case 0x09:
case 0x0A:
case 0x0B:
case 0x0C:
case 0x0D:
break;
default:
systemMessage(MSG_INVALID_CHEAT_CODE_ADDRESS,
N_("Invalid cheat code address: %08x"),
address);
return false;
}
strncpy(buffer, &code[9], 8);
sscanf(buffer, "%x", &value);
int type = 0;
if (len == 13)
type = 114;
if (len == 17)
type = 115;
cheatsAdd(code, desc, address, address, value, type, type);
return true; return true;
} }
@ -2606,7 +2579,7 @@ void cheatsReadGame(gzFile file, int version)
utilGzRead(file, &cheatsList[i].address, sizeof(uint32_t)); utilGzRead(file, &cheatsList[i].address, sizeof(uint32_t));
cheatsList[i].rawaddress = cheatsList[i].address; cheatsList[i].rawaddress = cheatsList[i].address;
utilGzRead(file, &cheatsList[i].value, sizeof(uint32_t)); utilGzRead(file, &cheatsList[i].value, sizeof(uint32_t));
utilGzRead(file, &cheatsList[i].oldValue, sizeof(uint32_t)); utilGzRead(file, &cheatsList[i].old_value, sizeof(uint32_t));
utilGzRead(file, &cheatsList[i].codestring, 20 * sizeof(char)); utilGzRead(file, &cheatsList[i].codestring, 20 * sizeof(char));
utilGzRead(file, &cheatsList[i].desc, 32 * sizeof(char)); utilGzRead(file, &cheatsList[i].desc, 32 * sizeof(char));
} }
@ -2743,7 +2716,7 @@ bool cheatsLoadCheatList(const char* file)
FREAD_UNCHECKED(&cheatsList[i].address, 1, sizeof(uint32_t), f); FREAD_UNCHECKED(&cheatsList[i].address, 1, sizeof(uint32_t), f);
cheatsList[i].rawaddress = cheatsList[i].address; cheatsList[i].rawaddress = cheatsList[i].address;
FREAD_UNCHECKED(&cheatsList[i].value, 1, sizeof(uint32_t), f); FREAD_UNCHECKED(&cheatsList[i].value, 1, sizeof(uint32_t), f);
FREAD_UNCHECKED(&cheatsList[i].oldValue, 1, sizeof(uint32_t), f); FREAD_UNCHECKED(&cheatsList[i].old_value, 1, sizeof(uint32_t), f);
FREAD_UNCHECKED(&cheatsList[i].codestring, 1, 20 * sizeof(char), f); FREAD_UNCHECKED(&cheatsList[i].codestring, 1, 20 * sizeof(char), f);
if (fread(&cheatsList[i].desc, 1, 32 * sizeof(char), f) != 32 * sizeof(char)) { if (fread(&cheatsList[i].desc, 1, 32 * sizeof(char), f) != 32 * sizeof(char)) {
fclose(f); fclose(f);
@ -2829,9 +2802,9 @@ void cheatsWriteMemory(uint32_t address, uint32_t value)
{ {
if (cheatsNumber == 0) { if (cheatsNumber == 0) {
int type = cheatsGetType(address); int type = cheatsGetType(address);
uint32_t oldValue = debuggerReadMemory(address); uint32_t old_value = debuggerReadMemory(address);
if (type == 1 || (type == 2 && oldValue != value)) { if (type == 1 || (type == 2 && old_value != value)) {
debuggerBreakOnWrite(address, oldValue, value, 2, type); debuggerBreakOnWrite(address, old_value, value, 2, type);
cpuNextEvent = 0; cpuNextEvent = 0;
} }
debuggerWriteMemory(address, value); debuggerWriteMemory(address, value);
@ -2842,9 +2815,9 @@ void cheatsWriteHalfWord(uint32_t address, uint16_t value)
{ {
if (cheatsNumber == 0) { if (cheatsNumber == 0) {
int type = cheatsGetType(address); int type = cheatsGetType(address);
uint16_t oldValue = debuggerReadHalfWord(address); uint16_t old_value = debuggerReadHalfWord(address);
if (type == 1 || (type == 2 && oldValue != value)) { if (type == 1 || (type == 2 && old_value != value)) {
debuggerBreakOnWrite(address, oldValue, value, 1, type); debuggerBreakOnWrite(address, old_value, value, 1, type);
cpuNextEvent = 0; cpuNextEvent = 0;
} }
debuggerWriteHalfWord(address, value); debuggerWriteHalfWord(address, value);
@ -2855,9 +2828,9 @@ void cheatsWriteByte(uint32_t address, uint8_t value)
{ {
if (cheatsNumber == 0) { if (cheatsNumber == 0) {
int type = cheatsGetType(address); int type = cheatsGetType(address);
uint8_t oldValue = debuggerReadByte(address); uint8_t old_value = debuggerReadByte(address);
if (type == 1 || (type == 2 && oldValue != value)) { if (type == 1 || (type == 2 && old_value != value)) {
debuggerBreakOnWrite(address, oldValue, value, 0, type); debuggerBreakOnWrite(address, old_value, value, 0, type);
cpuNextEvent = 0; cpuNextEvent = 0;
} }
debuggerWriteByte(address, value); debuggerWriteByte(address, value);

View File

@ -9,18 +9,7 @@
#include <zlib.h> #include <zlib.h>
#endif // defined(__LIBRETRO__) #endif // defined(__LIBRETRO__)
struct CheatsData { #include "core/rust/bindings.hpp"
int code;
int size;
int status;
bool enabled;
uint32_t rawaddress;
uint32_t address;
uint32_t value;
uint32_t oldValue;
char codestring[20];
char desc[32];
};
void cheatsAdd(const char* codeStr, const char* desc, uint32_t rawaddress, uint32_t address, uint32_t value, void cheatsAdd(const char* codeStr, const char* desc, uint32_t rawaddress, uint32_t address, uint32_t value,
int code, int size); int code, int size);
@ -47,6 +36,6 @@ void cheatsWriteByte(uint32_t address, uint8_t value);
int cheatsCheckKeys(uint32_t keys, uint32_t extended); int cheatsCheckKeys(uint32_t keys, uint32_t extended);
extern int cheatsNumber; extern int cheatsNumber;
extern CheatsData cheatsList[MAX_CHEATS]; extern core::CheatsData cheatsList[MAX_CHEATS];
#endif // VBAM_CORE_GBA_GBACHEATS_H_ #endif // VBAM_CORE_GBA_GBACHEATS_H_

16
src/core/rust/Cargo.toml Normal file
View File

@ -0,0 +1,16 @@
[package]
name = "vbam_rust"
version = "0.1.0"
[lib]
name = "vbam_rust"
crate-type = ["staticlib"]
[profile.dev]
panic = "abort"
[profile.release]
panic = "abort"
[build-dependencies]
cbindgen = "0.26.0"

View File

@ -0,0 +1,76 @@
#ifndef VBAM_CORE_RUST_H_
#define VBAM_CORE_RUST_H_
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#ifdef __cplusplus
namespace core {
#endif // __cplusplus
typedef enum CheatsDecodeError {
UnsupportedType,
InvalidLength,
InvalidCharacter,
AddressNotHex,
ValueNotHex,
} CheatsDecodeError;
typedef enum GbaCheatsType {
Generic,
GameSharkV2,
CodeBreaker,
ActionReplay,
} GbaCheatsType;
typedef struct CheatsData {
int code;
int size;
int status;
bool enabled;
uint32_t rawaddress;
uint32_t address;
uint32_t value;
uint32_t old_value;
char codestring[20];
char desc[32];
} CheatsData;
typedef enum Result_CheatsData__CheatsDecodeError_Tag {
Ok_CheatsData__CheatsDecodeError,
Err_CheatsData__CheatsDecodeError,
} Result_CheatsData__CheatsDecodeError_Tag;
typedef struct Result_CheatsData__CheatsDecodeError {
Result_CheatsData__CheatsDecodeError_Tag tag;
union {
struct {
struct CheatsData ok;
};
struct {
enum CheatsDecodeError err;
};
};
} Result_CheatsData__CheatsDecodeError;
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
enum GbaCheatsType some_type(void);
struct Result_CheatsData__CheatsDecodeError decode_code(enum GbaCheatsType cheat_type,
const char *code_string,
const char *description);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#ifdef __cplusplus
} // namespace core
#endif // __cplusplus
#endif /* VBAM_CORE_RUST_H_ */

17
src/core/rust/build.rs Normal file
View File

@ -0,0 +1,17 @@
extern crate cbindgen;
use std::env;
fn main() {
let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
cbindgen::Builder::new()
.with_crate(crate_dir)
.with_language(cbindgen::Language::C)
.with_cpp_compat(true)
.with_include_guard("VBAM_CORE_RUST_H_")
.with_namespace("core")
.generate()
.expect("Unable to generate bindings")
.write_to_file("bindings.hpp");
}

View File

@ -0,0 +1,7 @@
mod panic;
mod result;
pub use crate::base::result::Result;
mod string;
pub(crate) use crate::base::string::StrLike;

View File

@ -0,0 +1,6 @@
#[panic_handler]
#[cfg(not(test))]
/// Panic handler, called on panic. Does nothing.
fn panic(_info: &core::panic::PanicInfo) -> ! {
loop {}
}

View File

@ -0,0 +1,5 @@
#[repr(C)]
pub enum Result<T, E> {
Ok(T),
Err(E),
}

View File

@ -0,0 +1,100 @@
use core::ffi::c_char;
use core::ffi::CStr;
pub(crate) trait CharLike {
fn to_hex(&self) -> Option<u8>;
fn is_hex(&self) -> bool {
self.to_hex().is_some()
}
}
impl CharLike for u8 {
fn to_hex(&self) -> Option<u8> {
if *self >= b'0' && *self <= b'9' {
Some(*self - b'0')
} else if *self >= b'A' && *self <= b'F' {
Some(*self - b'A' + 10)
} else {
None
}
}
}
pub(crate) trait StrLike {
fn to_hex(&self) -> Option<u32>;
fn clone_buffer<const LEN: usize>(&self) -> [c_char; LEN];
}
impl StrLike for [u8] {
fn to_hex(&self) -> Option<u32> {
let mut result: u32 = 0;
for byte in self.iter() {
if let Some(value) = byte.to_hex() {
result = result.wrapping_mul(16).wrapping_add(value as u32);
} else {
return None;
}
}
Some(result)
}
fn clone_buffer<const LEN: usize>(&self) -> [c_char; LEN] {
let mut buffer = [0; LEN];
let mut i: usize = 0;
for byte in self.iter() {
if i == LEN {
break;
}
buffer[i] = *byte as c_char;
i += 1;
}
if i == LEN {
buffer[LEN - 1] = 0;
} else {
buffer[i] = 0;
}
buffer
}
}
impl StrLike for CStr {
fn to_hex(&self) -> Option<u32> {
self.to_bytes().to_hex()
}
fn clone_buffer<const LEN: usize>(&self) -> [c_char; LEN] {
self.to_bytes().clone_buffer()
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_char_to_hex() {
assert_eq!(b'0'.to_hex(), Some(0));
assert_eq!(b'9'.to_hex(), Some(9));
assert_eq!(b'A'.to_hex(), Some(10));
assert_eq!(b'F'.to_hex(), Some(15));
assert_eq!(b'G'.to_hex(), None);
}
#[test]
fn test_str_to_hex() {
assert_eq!([].to_hex(), Some(0));
assert_eq!([b'0'].to_hex(), Some(0));
assert_eq!([b'1', b'0'].to_hex(), Some(16));
assert_eq!([b'F', b'F'].to_hex(), Some(255));
assert_eq!([b'G'].to_hex(), None);
assert_eq!(b"ABCDEF".to_hex(), Some(11259375));
}
#[test]
fn test_str_clone_buffer() {
assert_eq!(b"test".clone_buffer(), [116, 101, 115, 116, 0]);
assert_eq!(b"test".clone_buffer(), [116, 101, 0]);
assert_eq!(b"test".clone_buffer(), [0]);
assert_eq!(b"".clone_buffer(), [0]);
}
}

2
src/core/rust/src/gba.rs Normal file
View File

@ -0,0 +1,2 @@
mod cheats;
pub use crate::gba::cheats::GbaCheatsType;

View File

@ -0,0 +1,108 @@
use core::ffi::c_char;
use core::ffi::c_int;
use core::ffi::CStr;
use crate::base::StrLike;
use crate::prelude::*;
#[repr(C)]
pub enum GbaCheatsType {
Generic,
GameSharkV2,
CodeBreaker,
ActionReplay,
}
impl GbaCheatsType {
#[no_mangle]
pub extern "C" fn some_type() -> GbaCheatsType {
GbaCheatsType::Generic
}
}
#[repr(C)]
pub struct CheatsData {
code: c_int,
size: c_int,
status: c_int,
enabled: bool,
rawaddress: u32,
address: u32,
value: u32,
old_value: u32,
codestring: [c_char; 20],
desc: [c_char; 32],
}
#[repr(C)]
pub enum CheatsDecodeError {
UnsupportedType,
InvalidLength,
InvalidCharacter,
AddressNotHex,
ValueNotHex,
}
impl CheatsData {
fn decode_generic(code_string: &CStr, description: &CStr) -> Result<CheatsData, CheatsDecodeError> {
let code_length = code_string.to_bytes().len();
if code_length != 11 && code_length != 13 && code_length != 17 {
return Err(CheatsDecodeError::InvalidLength);
}
if code_string.to_bytes()[8] != b':' {
return Err(CheatsDecodeError::InvalidCharacter);
}
let address =
match code_string.to_bytes()[..8].to_hex() {
Some(address) => address,
None => {
return Err(CheatsDecodeError::AddressNotHex);
}
};
let value =
match code_string.to_bytes()[9..].to_hex() {
Some(value) => value,
None => {
return Err(CheatsDecodeError::ValueNotHex);
}
};
let code = if code_length == 1 {
0
} else if code_length == 13 {
114
} else {
115
};
Ok(CheatsData {
code,
size: code,
status: 0,
enabled: true,
rawaddress: address,
address,
value,
old_value: 0,
codestring: code_string.clone_buffer(),
desc: description.clone_buffer(),
})
}
#[no_mangle]
pub extern "C" fn decode_code(
cheat_type: GbaCheatsType,
code_string: *const c_char,
description: *const c_char,
) -> Result<CheatsData, CheatsDecodeError> {
let code = unsafe { CStr::from_ptr(code_string) };
let description = unsafe { CStr::from_ptr(description) };
match cheat_type {
GbaCheatsType::Generic => CheatsData::decode_generic(code, description),
_ => Err(CheatsDecodeError::UnsupportedType),
}
}
}

22
src/core/rust/src/lib.rs Normal file
View File

@ -0,0 +1,22 @@
//! A crate implementing part of the VBA-M core in Rust. This is meant to be
//! used as a library in the VBA-M core.
//!
//! Guidelines:
//! * `no_std`. In particular, this means no I/O and no file I/O. It's fine to
//! pass buffers of memory to and from the Rust code, but the Rust code itself
//! should not perform any I/O.
//! * Core code only. We may want to use Rust in other parts of the codebase but
//! let's leave it at the core for now.
//! * Exported types must be `#[repr(C)]`. Exported functions must be `extern
//! "C"` with `[no_mangle]`.
#![no_std]
pub mod base;
pub mod gba;
mod prelude {
pub use crate::base::Result;
pub use crate::base::Result::Err;
pub use crate::base::Result::Ok;
}

View File

@ -34,10 +34,6 @@ if(MSVC)
) )
endif() endif()
if(ENABLE_LIRC)
set(LIRC_CLIENT_LIBRARY lirc_client)
endif()
target_link_libraries(vbam target_link_libraries(vbam
vbam-core vbam-core
vbam-components-audio-sdl vbam-components-audio-sdl

View File

@ -427,7 +427,7 @@ host_compile(${CMAKE_CURRENT_SOURCE_DIR}/bin2c.c ${BIN2C})
# Override wxrc when cross-compiling. # Override wxrc when cross-compiling.
if(CMAKE_HOST_WIN32 AND CMAKE_CROSSCOMPILING) if(CMAKE_HOST_WIN32 AND CMAKE_CROSSCOMPILING)
set(WXRC ${CMAKE_SOURCE_DIR}/dependencies/wxrc/wxrc.exe) find_program(WXRC NAMES wxrc)
endif() endif()
# Configure wxrc. # Configure wxrc.

View File

@ -8,6 +8,6 @@
#define DOCTEST_THREAD_LOCAL // Avoid MinGW thread_local bug. #define DOCTEST_THREAD_LOCAL // Avoid MinGW thread_local bug.
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include "doctest.h" #include <doctest.h>
#endif #endif

View File

@ -524,7 +524,6 @@ setup() {
# binary smaller. # binary smaller.
if [ "$target_os" = windows ] && [ "$target_bits" -eq 32 ]; then if [ "$target_os" = windows ] && [ "$target_bits" -eq 32 ]; then
BUILD_FFMPEG= BUILD_FFMPEG=
PROJECT_ARGS="$PROJECT_ARGS -DENABLE_OPENAL=NO"
fi fi
if [ -z "$BUILD_FFMPEG" ]; then if [ -z "$BUILD_FFMPEG" ]; then