From fa55ddca926d3c3ad2285911646919def4aa6fa3 Mon Sep 17 00:00:00 2001 From: M4xw Date: Tue, 6 Feb 2024 12:43:31 +0100 Subject: [PATCH 01/58] Attempt android fix --- libretro/jni/Android.mk | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libretro/jni/Android.mk b/libretro/jni/Android.mk index 9f3d8c86..f1dc2fe3 100644 --- a/libretro/jni/Android.mk +++ b/libretro/jni/Android.mk @@ -68,6 +68,10 @@ ifeq ($(LLE), 1) COREFLAGS += -DHAVE_LLE endif +ifeq ($(HAVE_PARALLEL_RSP), 1) + COREFLAGS += -DHAVE_MMAP=1 +endif + GIT_VERSION := " $(shell git rev-parse --short HEAD || echo unknown)" ifneq ($(GIT_VERSION)," unknown") COREFLAGS += -DGIT_VERSION=\"$(GIT_VERSION)\" From 65691d4b40c87216848508854bafbf6421fc5c3d Mon Sep 17 00:00:00 2001 From: M4xw Date: Thu, 7 Mar 2024 15:35:56 +0100 Subject: [PATCH 02/58] git subrepo clone --force git@github.com:libretro-fork/libretro-common-mupen64plus-nx.git libretro-common/ subrepo: subdir: "libretro-common" merged: "f9f6ac61c" upstream: origin: "git@github.com:libretro-fork/libretro-common-mupen64plus-nx.git" branch: "master" commit: "f9f6ac61c" git-subrepo: version: "0.4.6" origin: "???" commit: "???" --- libretro-common/.gitrepo | 4 ++-- libretro-common/memmap/memmap.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libretro-common/.gitrepo b/libretro-common/.gitrepo index a3d3ab6a..d5554a89 100644 --- a/libretro-common/.gitrepo +++ b/libretro-common/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/libretro-fork/libretro-common-mupen64plus-nx.git branch = master - commit = b524671aab017c027e8ae7d98711a9a53ad189fb - parent = d908986871908e19f426bc6779fd5dc43006fd75 + commit = f9f6ac61c3c7a4c1766c19ee9af70302d1914470 + parent = fa55ddca926d3c3ad2285911646919def4aa6fa3 method = rebase cmdver = 0.4.6 diff --git a/libretro-common/memmap/memmap.c b/libretro-common/memmap/memmap.c index 5062abaa..52724061 100644 --- a/libretro-common/memmap/memmap.c +++ b/libretro-common/memmap/memmap.c @@ -131,14 +131,14 @@ int mprotect(void *addr, size_t len, int prot) #endif -#if defined(__MACH__) && defined(__arm__) +#if defined(__MACH__) && (defined(__arm__) || defined(__arm64__)) #include #endif int memsync(void *start, void *end) { size_t len = (char*)end - (char*)start; -#if defined(__MACH__) && defined(__arm__) +#if defined(__MACH__) && (defined(__arm__) || defined(__arm64__)) sys_dcache_flush(start ,len); sys_icache_invalidate(start, len); return 0; From 977f5427f6c43057d7c7af893d71bab352703a34 Mon Sep 17 00:00:00 2001 From: Eric Warmenhoven Date: Fri, 23 Feb 2024 15:13:59 -0500 Subject: [PATCH 03/58] Enable parallel-rdp on iOS/tvOS --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index 45524ecd..630f5258 100644 --- a/Makefile +++ b/Makefile @@ -430,6 +430,7 @@ else ifneq (,$(findstring ios,$(platform))) GLES3=1 FORCE_GLES3=1 EGL := 0 + HAVE_PARALLEL_RDP = 1 PLATCFLAGS += -DHAVE_POSIX_MEMALIGN -DNO_ASM PLATCFLAGS += -DIOS -marm -DOS_IOS -DDONT_WANT_ARM_OPTIMIZATIONS CPUFLAGS += -marm -mfpu=neon -mfloat-abi=softfp @@ -473,6 +474,7 @@ else ifneq (,$(findstring tvos,$(platform))) GLES3=1 FORCE_GLES3=1 EGL := 0 + HAVE_PARALLEL_RDP = 1 PLATCFLAGS += -DHAVE_POSIX_MEMALIGN -DNO_ASM PLATCFLAGS += -DIOS -DTVOS -marm -DOS_IOS -DOS_TVOS -DDONT_WANT_ARM_OPTIMIZATIONS CPUFLAGS += -marm -mfpu=neon -mfloat-abi=softfp From 3f794eec4dc4af2f22ecce507f2da324381d3d92 Mon Sep 17 00:00:00 2001 From: Eric Warmenhoven Date: Sun, 7 May 2023 13:13:55 -0400 Subject: [PATCH 04/58] parallel rsp/rdp on ios and tvos --- Makefile | 3 +++ Makefile.common | 2 ++ custom/ios/compat.c | 8 ++++++++ libretro/libretro.c | 18 ++++++++++++++++++ 4 files changed, 31 insertions(+) create mode 100644 custom/ios/compat.c diff --git a/Makefile b/Makefile index 630f5258..3a638854 100644 --- a/Makefile +++ b/Makefile @@ -425,6 +425,8 @@ else ifneq (,$(findstring ios,$(platform))) DEFINES += -DIOS GLES = 1 ifeq ($(platform),ios-arm64) + HAVE_PARALLEL_RSP = 1 + HAVE_PARALLEL_RDP = 1 WITH_DYNAREC= GLES=1 GLES3=1 @@ -474,6 +476,7 @@ else ifneq (,$(findstring tvos,$(platform))) GLES3=1 FORCE_GLES3=1 EGL := 0 + HAVE_PARALLEL_RSP = 1 HAVE_PARALLEL_RDP = 1 PLATCFLAGS += -DHAVE_POSIX_MEMALIGN -DNO_ASM PLATCFLAGS += -DIOS -DTVOS -marm -DOS_IOS -DOS_TVOS -DDONT_WANT_ARM_OPTIMIZATIONS diff --git a/Makefile.common b/Makefile.common index fbc86bf9..b1425312 100644 --- a/Makefile.common +++ b/Makefile.common @@ -483,6 +483,8 @@ endif ifneq ($(platform), $(filter $(platform), ios-arm64 tvos-arm64)) EGL_LIB ?= -lEGL +else + SOURCES_C += $(ROOT_DIR)/custom/ios/compat.c endif ifeq ($(GLES3),1) diff --git a/custom/ios/compat.c b/custom/ios/compat.c new file mode 100644 index 00000000..ab3ce9fd --- /dev/null +++ b/custom/ios/compat.c @@ -0,0 +1,8 @@ +#include + +void __clear_cache(void *start, void *end) +{ + size_t len = (char*)end - (char*)start; + sys_dcache_flush(start, len); + sys_icache_invalidate(start, len); +} diff --git a/libretro/libretro.c b/libretro/libretro.c index f235be81..30990251 100644 --- a/libretro/libretro.c +++ b/libretro/libretro.c @@ -911,6 +911,24 @@ static void update_variables(bool startup) } } +#ifdef IOS + bool can_jit = false; + if (!environ_cb(RETRO_ENVIRONMENT_GET_JIT_CAPABLE, &can_jit) || !can_jit) + { + if(current_rsp_type == RSP_PLUGIN_PARALLEL) + { +#if defined(HAVE_LLE) + plugin_connect_rsp_api(RSP_PLUGIN_CXD4); + log_cb(RETRO_LOG_INFO, "Selected Parallel RSP without JIT, falling back to CXD4!\n"); +#else + log_cb(RETRO_LOG_INFO, "Selected Parallel RSP without JIT, falling back to GLideN64!\n"); + plugin_connect_rsp_api(RSP_PLUGIN_HLE); + plugin_connect_rdp_api(RDP_PLUGIN_GLIDEN64); +#endif + } + } +#endif + var.key = CORE_NAME "-ThreadedRenderer"; var.value = NULL; if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) From 5d2ac21adb784ad72d6101290117702eef0411dd Mon Sep 17 00:00:00 2001 From: M4xw Date: Tue, 21 May 2024 11:50:43 +0200 Subject: [PATCH 05/58] Misc fixes for reinit --- libretro-common/glsm/glsm.c | 1 + mupen64plus-core/src/main/rom.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/libretro-common/glsm/glsm.c b/libretro-common/glsm/glsm.c index b5f488b8..01f53461 100644 --- a/libretro-common/glsm/glsm.c +++ b/libretro-common/glsm/glsm.c @@ -3278,6 +3278,7 @@ static void glsm_state_unbind(void) static bool glsm_state_ctx_destroy(void *data) { + window_first = 0; return true; } diff --git a/mupen64plus-core/src/main/rom.c b/mupen64plus-core/src/main/rom.c index aba78924..5862db97 100644 --- a/mupen64plus-core/src/main/rom.c +++ b/mupen64plus-core/src/main/rom.c @@ -694,6 +694,8 @@ void romdatabase_close(void) free(g_romdatabase.list); g_romdatabase.list = search; } + + g_romdatabase.have_database = 0; } static romdatabase_entry* ini_search_by_md5(md5_byte_t* md5) From b7e23a7e900123573228566186aff0b6817e5542 Mon Sep 17 00:00:00 2001 From: Eric Warmenhoven Date: Wed, 29 May 2024 13:48:15 -0400 Subject: [PATCH 06/58] iOS/tvOS: change a few build flags to try to get a bit more performance --- Makefile | 23 ++++++++++++++--------- libretro/libretro.c | 2 +- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index 3a638854..09eb5478 100644 --- a/Makefile +++ b/Makefile @@ -433,10 +433,11 @@ else ifneq (,$(findstring ios,$(platform))) FORCE_GLES3=1 EGL := 0 HAVE_PARALLEL_RDP = 1 - PLATCFLAGS += -DHAVE_POSIX_MEMALIGN -DNO_ASM - PLATCFLAGS += -DIOS -marm -DOS_IOS -DDONT_WANT_ARM_OPTIMIZATIONS - CPUFLAGS += -marm -mfpu=neon -mfloat-abi=softfp - HAVE_NEON=0 + PLATCFLAGS += -DHAVE_POSIX_MEMALIGN -DIOS -DOS_IOS + PLATCFLAGS += -Ofast -ffast-math -funsafe-math-optimizations -DNO_ASM + COREFLAGS += -Ofast -ffast-math -funsafe-math-optimizations -DNO_ASM + CPUFLAGS += -Ofast -ffast-math -funsafe-math-optimizations -DNO_ASM + HAVE_NEON=1 CC += -miphoneos-version-min=8.0 CC_AS += -miphoneos-version-min=8.0 CXX += -miphoneos-version-min=8.0 @@ -478,10 +479,11 @@ else ifneq (,$(findstring tvos,$(platform))) EGL := 0 HAVE_PARALLEL_RSP = 1 HAVE_PARALLEL_RDP = 1 - PLATCFLAGS += -DHAVE_POSIX_MEMALIGN -DNO_ASM - PLATCFLAGS += -DIOS -DTVOS -marm -DOS_IOS -DOS_TVOS -DDONT_WANT_ARM_OPTIMIZATIONS - CPUFLAGS += -marm -mfpu=neon -mfloat-abi=softfp - HAVE_NEON=0 + PLATCFLAGS += -DHAVE_POSIX_MEMALIGN -DIOS -DOS_IOS + PLATCFLAGS += -Ofast -ffast-math -funsafe-math-optimizations -DNO_ASM + COREFLAGS += -Ofast -ffast-math -funsafe-math-optimizations -DNO_ASM + CPUFLAGS += -Ofast -ffast-math -funsafe-math-optimizations -DNO_ASM + HAVE_NEON=1 CC += -mappletvos-version-min=8.0 CC_AS += -mappletvos-version-min=8.0 CXX += -mappletvos-version-min=8.0 @@ -599,7 +601,10 @@ endif include Makefile.common ifeq ($(HAVE_NEON), 1) - COREFLAGS += -DHAVE_NEON -D__ARM_NEON__ -D__NEON_OPT -ftree-vectorize -mvectorize-with-neon-quad -ftree-vectorizer-verbose=2 -funsafe-math-optimizations -fno-finite-math-only + COREFLAGS += -DHAVE_NEON -D__ARM_NEON__ -D__NEON_OPT -ftree-vectorize -funsafe-math-optimizations -fno-finite-math-only + ifeq (,$(filter $(platform),ios-arm64 tvos-arm64)) + COREFLAGS += -mvectorize-with-neon-quad -ftree-vectorizer-verbose=2 + endif endif ifeq ($(LLE), 1) diff --git a/libretro/libretro.c b/libretro/libretro.c index 30990251..0147d955 100644 --- a/libretro/libretro.c +++ b/libretro/libretro.c @@ -719,7 +719,7 @@ void retro_init(void) initializing = true; retro_thread = co_active(); - game_thread = co_create(65536 * sizeof(void*) * 16, EmuThreadFunction); + game_thread = co_create(65536 * sizeof(void*) * 16, (void (*)(void))EmuThreadFunction); } m64p_error ret = CoreStartup(FRONTEND_API_VERSION, ".", ".", NULL, n64DebugCallback, 0, n64StateCallback); From 80e48a0f6e3e6b4a295d1e96ba010b3223b96c78 Mon Sep 17 00:00:00 2001 From: M4xw Date: Thu, 11 Jul 2024 15:47:56 +0200 Subject: [PATCH 07/58] Some stability improvements --- libretro-common/glsm/glsm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libretro-common/glsm/glsm.c b/libretro-common/glsm/glsm.c index 01f53461..b3e8c60e 100644 --- a/libretro-common/glsm/glsm.c +++ b/libretro-common/glsm/glsm.c @@ -2956,6 +2956,8 @@ bool isExtensionSupported(const char *extension) return false; const GLubyte *extensions = glGetString(GL_EXTENSIONS); + if(extensions == NULL) + return false; // Some cursed drivers return NULL here const GLubyte *start = extensions; for (;;) { From 9d940bacb95c4d86733f42b67b57fc83046a6d39 Mon Sep 17 00:00:00 2001 From: Jonian Guveli Date: Sat, 22 Jun 2024 10:25:39 +0300 Subject: [PATCH 08/58] Add support for RPi5 builds --- Makefile | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Makefile b/Makefile index 09eb5478..9aa30195 100644 --- a/Makefile +++ b/Makefile @@ -175,6 +175,16 @@ else ifneq (,$(findstring rpi,$(platform))) CPUFLAGS += -march=armv8-a+crc -mtune=cortex-a72 ARM_CPUFLAGS = -mfpu=neon-fp-armv8 endif + else ifneq (,$(findstring rpi5,$(platform))) + ifneq (,$(findstring rpi5_64,$(platform))) + CPUFLAGS += -mcpu=cortex-a76 -mtune=cortex-a76 + else + CPUFLAGS += -march=armv8-a+crc+crypto -mtune=cortex-a76 + ARM_CPUFLAGS = -mfpu=neon-fp-armv8 + endif + HAVE_PARALLEL_RSP = 1 + HAVE_THR_AL = 1 + LLE = 1 else ifneq (,$(findstring rpi,$(platform))) CPUFLAGS += -mcpu=arm1176jzf-s ARM_CPUFLAGS = -mfpu=vfp From 508f06cef25b3c4581b7782ee71c090ba4a1443e Mon Sep 17 00:00:00 2001 From: M4xw Date: Fri, 19 Jul 2024 12:22:39 +0200 Subject: [PATCH 09/58] git subrepo clone --force --branch=rebase ../mupen64plus-core mupen64plus-core/ subrepo: subdir: "mupen64plus-core" merged: "efb46b8a1" upstream: origin: "../mupen64plus-core" branch: "rebase" commit: "efb46b8a1" git-subrepo: version: "0.4.6" origin: "???" commit: "???" --- mupen64plus-core/.github/workflows/build.yml | 169 + .../.github/workflows/schedule.yml | 165 + .../workflows/scripts/ci_backup_mingw_deps.sh | 21 + .../workflows/scripts/ci_backup_msvc_deps.cmd | 36 + .../.github/workflows/scripts/ci_build.sh | 116 + .../workflows/scripts/ci_get_libglew_i386.sh | 23 + .../scripts/ci_install_ubuntu_deps.sh | 48 + .../workflows/scripts/ci_msvc_build.cmd | 62 + .../workflows/scripts/ci_nightly_artifacts.sh | 48 + mupen64plus-core/.gitignore | 3 + mupen64plus-core/.gitrepo | 8 +- mupen64plus-core/README | 4 + mupen64plus-core/README.md | 8 +- mupen64plus-core/RELEASE | 828 ++- mupen64plus-core/appveyor.yml | 15 + mupen64plus-core/data/mupen64plus.ini | 368 +- .../Mupen64Plus-v2.0-API-Versioning.mediawiki | 11 +- .../Mupen64Plus-v2.0-Core-Front-End.mediawiki | 18 +- ...64Plus-v2.0-Core-Video-Extension.mediawiki | 46 + .../Mupen64Plus-v2.0-Plugin-API.mediawiki | 15 + .../Mupen64Plus-v2.0-headers.mediawiki | 6 + .../projects/msvc/mupen64plus-core.vcxproj | 76 +- .../msvc/mupen64plus-core.vcxproj.filters | 6 + mupen64plus-core/projects/unix/Makefile | 110 +- mupen64plus-core/src/api/api_export.ver | 3 + mupen64plus-core/src/api/frontend.c | 37 +- mupen64plus-core/src/api/m64p_plugin.h | 15 + mupen64plus-core/src/api/m64p_types.h | 33 +- mupen64plus-core/src/api/m64p_vidext.h | 33 + mupen64plus-core/src/api/vidext.c | 177 +- mupen64plus-core/src/api/vidext_sdl2_compat.h | 56 +- .../src/asm_defines/asm_defines.c | 4 +- mupen64plus-core/src/backends/api/joybus.h | 6 + .../plugins_compat/input_plugin_compat.c | 4 +- mupen64plus-core/src/debugger/dbg_memory.c | 14 + mupen64plus-core/src/device/cart/cart.c | 3 + mupen64plus-core/src/device/cart/is_viewer.c | 14 +- .../src/device/controllers/game_controller.c | 1 - .../src/device/controllers/game_controller.h | 6 + .../src/device/controllers/paks/mempak.c | 154 +- .../src/device/controllers/paks/mempak.h | 10 +- .../src/device/controllers/vru_controller.c | 254 + .../src/device/controllers/vru_controller.h | 37 + .../src/device/dd/dd_controller.c | 295 +- .../src/device/dd/dd_controller.h | 10 +- mupen64plus-core/src/device/dd/disk.h | 1 + mupen64plus-core/src/device/device.c | 19 +- mupen64plus-core/src/device/device.h | 2 +- mupen64plus-core/src/device/pif/bootrom_hle.c | 2 +- .../src/device/r4300/cached_interp.c | 24 +- mupen64plus-core/src/device/r4300/cp0.c | 25 +- mupen64plus-core/src/device/r4300/cp0.h | 24 +- mupen64plus-core/src/device/r4300/cp1.c | 16 +- mupen64plus-core/src/device/r4300/cp1.h | 4 +- mupen64plus-core/src/device/r4300/cp2.c | 54 + mupen64plus-core/src/device/r4300/cp2.h | 45 + mupen64plus-core/src/device/r4300/fpu.h | 776 ++- mupen64plus-core/src/device/r4300/idec.c | 12 +- mupen64plus-core/src/device/r4300/interrupt.c | 15 + mupen64plus-core/src/device/r4300/interrupt.h | 27 +- .../src/device/r4300/mips_instructions.def | 339 +- .../device/r4300/new_dynarec/arm/assem_arm.c | 42 +- .../r4300/new_dynarec/arm64/assem_arm64.c | 42 +- .../device/r4300/new_dynarec/new_dynarec.c | 15 +- .../device/r4300/new_dynarec/new_dynarec.h | 6 +- .../device/r4300/new_dynarec/x64/assem_x64.c | 48 +- .../device/r4300/new_dynarec/x86/assem_x86.c | 43 +- mupen64plus-core/src/device/r4300/opcodes.md | 4 + .../src/device/r4300/pure_interp.c | 46 +- .../src/device/r4300/r4300_core.c | 4 + .../src/device/r4300/r4300_core.h | 3 + mupen64plus-core/src/device/r4300/recomp.c | 4 + .../src/device/rcp/ai/ai_controller.c | 10 +- .../src/device/rcp/ai/ai_controller.h | 4 +- .../src/device/rcp/pi/pi_controller.c | 8 - .../src/device/rcp/rsp/rsp_core.c | 80 +- mupen64plus-core/src/device/rdram/rdram.c | 16 +- mupen64plus-core/src/main/eventloop.c | 4 + mupen64plus-core/src/main/main.c | 189 +- mupen64plus-core/src/main/main.h | 3 + mupen64plus-core/src/main/netplay.c | 58 +- mupen64plus-core/src/main/netplay.h | 1 - mupen64plus-core/src/main/rom.c | 208 +- mupen64plus-core/src/main/rom.h | 5 + mupen64plus-core/src/main/savestates.c | 72 +- mupen64plus-core/src/main/screenshot.c | 16 +- mupen64plus-core/src/main/util.c | 237 +- mupen64plus-core/src/main/util.h | 47 + mupen64plus-core/src/main/version.h | 6 +- mupen64plus-core/src/osal/files.h | 8 +- mupen64plus-core/src/plugin/plugin.c | 3 +- mupen64plus-core/src/plugin/plugin.h | 7 +- mupen64plus-core/subprojects/md5/md5.c | 145 +- mupen64plus-core/subprojects/md5/md5.h | 21 +- mupen64plus-core/subprojects/minizip/crypt.h | 29 +- mupen64plus-core/subprojects/minizip/ioapi.c | 78 +- mupen64plus-core/subprojects/minizip/ioapi.h | 52 +- mupen64plus-core/subprojects/minizip/unzip.c | 566 +- mupen64plus-core/subprojects/minizip/unzip.h | 154 +- mupen64plus-core/subprojects/minizip/zip.c | 391 +- mupen64plus-core/subprojects/minizip/zip.h | 310 +- mupen64plus-core/subprojects/xxhash/xxhash.h | 4563 ++++++++++++----- mupen64plus-core/tools/gen_asm_script.cmd | 47 +- 103 files changed, 9338 insertions(+), 3018 deletions(-) create mode 100644 mupen64plus-core/.github/workflows/build.yml create mode 100644 mupen64plus-core/.github/workflows/schedule.yml create mode 100755 mupen64plus-core/.github/workflows/scripts/ci_backup_mingw_deps.sh create mode 100644 mupen64plus-core/.github/workflows/scripts/ci_backup_msvc_deps.cmd create mode 100755 mupen64plus-core/.github/workflows/scripts/ci_build.sh create mode 100755 mupen64plus-core/.github/workflows/scripts/ci_get_libglew_i386.sh create mode 100755 mupen64plus-core/.github/workflows/scripts/ci_install_ubuntu_deps.sh create mode 100644 mupen64plus-core/.github/workflows/scripts/ci_msvc_build.cmd create mode 100755 mupen64plus-core/.github/workflows/scripts/ci_nightly_artifacts.sh create mode 100644 mupen64plus-core/src/device/controllers/vru_controller.c create mode 100644 mupen64plus-core/src/device/controllers/vru_controller.h create mode 100644 mupen64plus-core/src/device/r4300/cp2.c create mode 100644 mupen64plus-core/src/device/r4300/cp2.h diff --git a/mupen64plus-core/.github/workflows/build.yml b/mupen64plus-core/.github/workflows/build.yml new file mode 100644 index 00000000..e9ed56f8 --- /dev/null +++ b/mupen64plus-core/.github/workflows/build.yml @@ -0,0 +1,169 @@ +name: Core + +on: + push: + paths-ignore: + - '.{gitattributes,gitignore,travis.yml}' + - '*.md,appveyor.yml,README' + pull_request: + paths-ignore: + - '.{gitattributes,gitignore,travis.yml}' + - '*.md,appveyor.yml,README' + workflow_dispatch: + +jobs: + + Linux: + strategy: + fail-fast: false + matrix: + include: + - cc: GCC + arch: x64 + - cc: GCC + arch: x86 + - cc: Clang + arch: x64 + - cc: Clang + arch: x86 + name: Linux / ${{ matrix.cc }} / ${{ matrix.arch }} + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - name: Get build dependencies and arrange the environment + run: | + export C_CLANG_SUFFIX="-15" C_GCC_SUFFIX="-12" + export BUILD_DEPS="libfreetype6-dev libgl1-mesa-dev libglu1-mesa-dev libpng-dev libsdl1.2-dev libsdl2-dev libvulkan-dev zlib1g-dev" + ./.github/workflows/scripts/ci_install_ubuntu_deps.sh ${{ matrix.arch }} ${{ matrix.cc }} + - name: Build and related stuff, backup binaries + run: | + export C_CLANG_SUFFIX="-15" C_GCC_SUFFIX="-12" + export PATH="$(pwd)/.github/workflows/scripts:${PATH}" + export CONFIG_OVERRIDE="ACCURATE_FPU=1 NEW_DYNAREC=1 SDL_CONFIG="sdl-config"" + ci_build.sh ${{ matrix.arch }} ${{ matrix.cc }} + export CONFIG_OVERRIDE="ACCURATE_FPU=1 NEW_DYNAREC=1 SDL_CONFIG="sdl2-config"" + ci_build.sh ${{ matrix.arch }} ${{ matrix.cc }} + export CONFIG_OVERRIDE="ACCURATE_FPU=0 NEW_DYNAREC=0 SDL_CONFIG="sdl-config" POSTFIX="-old"" + ci_build.sh ${{ matrix.arch }} ${{ matrix.cc }} + export CONFIG_OVERRIDE="ACCURATE_FPU=0 NEW_DYNAREC=0 SDL_CONFIG="sdl2-config" POSTFIX="-old"" + ci_build.sh ${{ matrix.arch }} ${{ matrix.cc }} makepkg + - name: Upload artifact + if: matrix.cc == 'GCC' + uses: actions/upload-artifact@v4 + with: + name: ${{ env.PKG_NAME }} + path: pkg/*.tar.gz + + MSYS2: + strategy: + fail-fast: false + matrix: + include: + - cc: GCC + arch: x64 + cross: x86_64 + env: MINGW64 + - cc: GCC + arch: x86 + cross: i686 + env: MINGW32 + name: Windows / MSYS2 ${{ matrix.cc }} / ${{ matrix.arch }} + runs-on: windows-2022 + defaults: + run: + shell: msys2 {0} + steps: + - uses: actions/checkout@v4 + - uses: msys2/setup-msys2@v2 + with: + msystem: ${{ matrix.env }} + update: true + install: >- + git + libtool + make + mingw-w64-${{ matrix.cross }}-gcc + mingw-w64-${{ matrix.cross }}-toolchain + mingw-w64-${{ matrix.cross }}-freetype + mingw-w64-${{ matrix.cross }}-libpng + mingw-w64-${{ matrix.cross }}-nasm + mingw-w64-${{ matrix.cross }}-ntldd + mingw-w64-${{ matrix.cross }}-SDL2 + mingw-w64-${{ matrix.cross }}-vulkan-headers + mingw-w64-${{ matrix.cross }}-zlib + - name: Build and related stuff, backup binaries + run: | + export PATH="$(pwd)/.github/workflows/scripts:${PATH}" + export CONFIG_OVERRIDE="ACCURATE_FPU=1 NEW_DYNAREC=1" + ci_build.sh ${{ matrix.arch }} ${{ matrix.cc }} + export CONFIG_OVERRIDE="ACCURATE_FPU=0 NEW_DYNAREC=0 POSTFIX="-old"" + ci_build.sh ${{ matrix.arch }} ${{ matrix.cc }} + - name: Backup dependencies, etc... + run: | + ./.github/workflows/scripts/ci_backup_mingw_deps.sh ${{ matrix.env }} + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ env.PKG_NAME }} + path: pkg/* + + MSVC: + strategy: + fail-fast: false + matrix: + include: + - toolset: v143 + arch: x64 + vs: 2022 + - toolset: v141_xp + arch: x86 + vs: 2019 + name: Windows / MSVC with ${{ matrix.toolset }} / ${{ matrix.arch }} + runs-on: windows-${{ matrix.vs }} + defaults: + run: + shell: cmd + steps: + - uses: actions/checkout@v4 + - uses: microsoft/setup-msbuild@v2 + - name: Build and related stuff, backup binaries + run: | + set PATH=%CD%\.github\workflows\scripts;%PATH% + set TOOLSET=${{ matrix.toolset }} + call ci_msvc_build.cmd ${{ matrix.arch }} + ci_msvc_build.cmd ${{ matrix.arch }} newdyn + - name: Backup dependencies, etc... + run: | + .\.github\workflows\scripts\ci_backup_msvc_deps.cmd ${{ matrix.arch }} freetype.dll libpng16.dll SDL2.dll SDL2_net.dll zlib.dll + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ env.PKG_NAME }} + path: pkg/* + + Nightly-build: + runs-on: ubuntu-latest + if: github.ref_name == 'master' + needs: [Linux, MSYS2, MSVC] + steps: + - uses: actions/checkout@v4 + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + path: binaries + - name: Get some tools + run: | + sudo apt-get update + sudo apt-get -y install hashdeep + - name: Creating new artifacts and update nightly-build + run: | + ./.github/workflows/scripts/ci_nightly_artifacts.sh + - name: Nightly-build + uses: ncipollo/release-action@v1 + with: + prerelease: true + allowUpdates: true + removeArtifacts: true + replacesArtifacts: false + tag: nightly-build + artifacts: pkg/* diff --git a/mupen64plus-core/.github/workflows/schedule.yml b/mupen64plus-core/.github/workflows/schedule.yml new file mode 100644 index 00000000..6b48bcd7 --- /dev/null +++ b/mupen64plus-core/.github/workflows/schedule.yml @@ -0,0 +1,165 @@ +name: Core - Scheduled + +on: + schedule: + - cron: '55 14 21 * *' + +jobs: + + Linux: + strategy: + fail-fast: false + matrix: + include: + - cc: GCC + arch: x64 + - cc: GCC + arch: x86 + - cc: Clang + arch: x64 + - cc: Clang + arch: x86 + name: Linux / ${{ matrix.cc }} / ${{ matrix.arch }} + runs-on: ubuntu-22.04 + if: github.repository == 'mupen64plus/mupen64plus-core' + steps: + - uses: actions/checkout@v4 + - name: Get build dependencies and arrange the environment + run: | + export C_CLANG_SUFFIX="-15" C_GCC_SUFFIX="-12" + export BUILD_DEPS="libfreetype6-dev libgl1-mesa-dev libglu1-mesa-dev libpng-dev libsdl1.2-dev libsdl2-dev libvulkan-dev zlib1g-dev" + ./.github/workflows/scripts/ci_install_ubuntu_deps.sh ${{ matrix.arch }} ${{ matrix.cc }} + - name: Build and related stuff, backup binaries + run: | + export C_CLANG_SUFFIX="-15" C_GCC_SUFFIX="-12" + export PATH="$(pwd)/.github/workflows/scripts:${PATH}" + export CONFIG_OVERRIDE="ACCURATE_FPU=1 NEW_DYNAREC=1 SDL_CONFIG="sdl-config"" + ci_build.sh ${{ matrix.arch }} ${{ matrix.cc }} + export CONFIG_OVERRIDE="ACCURATE_FPU=1 NEW_DYNAREC=1 SDL_CONFIG="sdl2-config"" + ci_build.sh ${{ matrix.arch }} ${{ matrix.cc }} + export CONFIG_OVERRIDE="ACCURATE_FPU=0 NEW_DYNAREC=0 SDL_CONFIG="sdl-config" POSTFIX="-old"" + ci_build.sh ${{ matrix.arch }} ${{ matrix.cc }} + export CONFIG_OVERRIDE="ACCURATE_FPU=0 NEW_DYNAREC=0 SDL_CONFIG="sdl2-config" POSTFIX="-old"" + ci_build.sh ${{ matrix.arch }} ${{ matrix.cc }} makepkg + - name: Upload artifact + if: matrix.cc == 'GCC' + uses: actions/upload-artifact@v4 + with: + name: ${{ env.PKG_NAME }} + path: pkg/*.tar.gz + + MSYS2: + strategy: + fail-fast: false + matrix: + include: + - cc: GCC + arch: x64 + cross: x86_64 + env: MINGW64 + - cc: GCC + arch: x86 + cross: i686 + env: MINGW32 + name: Windows / MSYS2 ${{ matrix.cc }} / ${{ matrix.arch }} + runs-on: windows-2022 + if: github.repository == 'mupen64plus/mupen64plus-core' + defaults: + run: + shell: msys2 {0} + steps: + - uses: actions/checkout@v4 + - uses: msys2/setup-msys2@v2 + with: + msystem: ${{ matrix.env }} + update: true + install: >- + git + libtool + make + mingw-w64-${{ matrix.cross }}-gcc + mingw-w64-${{ matrix.cross }}-toolchain + mingw-w64-${{ matrix.cross }}-freetype + mingw-w64-${{ matrix.cross }}-libpng + mingw-w64-${{ matrix.cross }}-nasm + mingw-w64-${{ matrix.cross }}-ntldd + mingw-w64-${{ matrix.cross }}-SDL2 + mingw-w64-${{ matrix.cross }}-vulkan-headers + mingw-w64-${{ matrix.cross }}-zlib + - name: Build and related stuff, backup binaries + run: | + export PATH="$(pwd)/.github/workflows/scripts:${PATH}" + export CONFIG_OVERRIDE="ACCURATE_FPU=1 NEW_DYNAREC=1" + ci_build.sh ${{ matrix.arch }} ${{ matrix.cc }} + export CONFIG_OVERRIDE="ACCURATE_FPU=0 NEW_DYNAREC=0 POSTFIX="-old"" + ci_build.sh ${{ matrix.arch }} ${{ matrix.cc }} + - name: Backup dependencies, etc... + run: | + ./.github/workflows/scripts/ci_backup_mingw_deps.sh ${{ matrix.env }} + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ env.PKG_NAME }} + path: pkg/* + + MSVC: + strategy: + fail-fast: false + matrix: + include: + - toolset: v143 + arch: x64 + vs: 2022 + - toolset: v141_xp + arch: x86 + vs: 2019 + name: Windows / MSVC with ${{ matrix.toolset }} / ${{ matrix.arch }} + runs-on: windows-${{ matrix.vs }} + if: github.repository == 'mupen64plus/mupen64plus-core' + defaults: + run: + shell: cmd + steps: + - uses: actions/checkout@v4 + - uses: microsoft/setup-msbuild@v2 + - name: Build and related stuff, backup binaries + run: | + set PATH=%CD%\.github\workflows\scripts;%PATH% + set TOOLSET=${{ matrix.toolset }} + call ci_msvc_build.cmd ${{ matrix.arch }} + ci_msvc_build.cmd ${{ matrix.arch }} newdyn + - name: Backup dependencies, etc... + run: | + .\.github\workflows\scripts\ci_backup_msvc_deps.cmd ${{ matrix.arch }} freetype.dll libpng16.dll SDL2.dll SDL2_net.dll zlib.dll + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ env.PKG_NAME }} + path: pkg/* + + Nightly-build: + runs-on: ubuntu-latest + if: github.ref_name == 'master' + needs: [Linux, MSYS2, MSVC] + steps: + - uses: actions/checkout@v4 + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + path: binaries + - name: Get some tools + run: | + sudo apt-get update + sudo apt-get -y install hashdeep + - name: Creating new artifacts and update nightly-build + run: | + ./.github/workflows/scripts/ci_nightly_artifacts.sh + - name: Nightly-build + uses: ncipollo/release-action@v1 + with: + prerelease: true + allowUpdates: true + removeArtifacts: true + replacesArtifacts: false + tag: nightly-build + artifacts: pkg/* diff --git a/mupen64plus-core/.github/workflows/scripts/ci_backup_mingw_deps.sh b/mupen64plus-core/.github/workflows/scripts/ci_backup_mingw_deps.sh new file mode 100755 index 00000000..6520d980 --- /dev/null +++ b/mupen64plus-core/.github/workflows/scripts/ci_backup_mingw_deps.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +set -e +u + +if [[ ${#} -ne 1 ]]; then exit 9; fi + +export ENV_MSYS="$(echo "${1}" | tr [A-Z] [a-z])" +export DEPS="$(LC_ALL=C grep "${ENV_MSYS}" pkg/ldd.log | tr -s '\t' ' ' | sort | cut -d ' ' -f4 | tr '\\' '/' | tr -d ':')" + +rm -f pkg/ldd.log + +if [[ "${DEPS}" == "" ]]; then + echo ":: None..." + exit 0 +fi + +for LIB in ${DEPS}; do + echo ":: Copying ${LIB##*/}" + cp "/${LIB}" pkg/ +done + +exit 0 diff --git a/mupen64plus-core/.github/workflows/scripts/ci_backup_msvc_deps.cmd b/mupen64plus-core/.github/workflows/scripts/ci_backup_msvc_deps.cmd new file mode 100644 index 00000000..ab5effdc --- /dev/null +++ b/mupen64plus-core/.github/workflows/scripts/ci_backup_msvc_deps.cmd @@ -0,0 +1,36 @@ +@echo off +setlocal enableextensions enabledelayedexpansion +cls +if not exist "projects\msvc\" exit /b 9 + +set ARCH= +set DEPS= +if "%*" == "" exit /b 8 +for %%P in (%*) do ( + if /i "%%P" == "x86" (set ARCH=x86) else ( + if /i "%%P" == "x64" (set ARCH=x64) else ( + set "DEPS=!DEPS! %%P") ) +) + +if not defined ARCH exit /b 7 + +if exist "data\" xcopy /e data pkg +if errorlevel 1 exit /b 6 +if exist "pkg\mupen64plus.desktop" del /f /q pkg\mupen64plus.desktop + +if not defined DEPS ( + echo :: None... + exit /b 0 +) + +set PKG=%CD%\pkg +cd ..\mupen64plus-win32-deps +if errorlevel 1 exit /b 5 +for %%D in (%DEPS%) do ( + for /f "tokens=*" %%T in ('dir /b /s %%D ^| findstr "%ARCH%"') do ( + if exist "%%T" xcopy "%%T" "%PKG%\" + if errorlevel 1 exit /b 4 + ) +) + +exit /b 0 diff --git a/mupen64plus-core/.github/workflows/scripts/ci_build.sh b/mupen64plus-core/.github/workflows/scripts/ci_build.sh new file mode 100755 index 00000000..ec808de3 --- /dev/null +++ b/mupen64plus-core/.github/workflows/scripts/ci_build.sh @@ -0,0 +1,116 @@ +#!/usr/bin/env bash +set -e +u + +if [[ ${#} -lt 2 ]]; then exit 9; fi + +unset ARCH_ARG CC_ARG +export MAKE_PKG=0 MULTILIB=0 +export ENV_ARGS="$(echo "${*}" | tr [A-Z] [a-z])" + +for ARG in ${ENV_ARGS}; do + case "${ARG}" in + clang ) + export CC_ARG="Clang" CC="clang${C_CLANG_SUFFIX}" CXX="clang++${C_CLANG_SUFFIX}" + ;; + gcc ) + export CC_ARG="GCC" CC="gcc${C_GCC_SUFFIX}" CXX="g++${C_GCC_SUFFIX}" + ;; + makepkg ) + export MAKE_PKG=1 + ;; + multilib ) + export MULTILIB=1 + ;; + x64 ) + export ARCH_ARG="x64" CPU_TUNE="-march=nocona" BITS=64 + ;; + x86 ) + export ARCH_ARG="x86" CPU_TUNE="-march=pentium4" BITS=32 + ;; + esac +done + +if [[ -z ${ARCH_ARG} ]]; then exit 8; fi +if [[ -z ${CC_ARG} ]]; then exit 7; fi + +export REPO="${PWD##*/}" +if [[ "${REPO}" == "" ]]; then exit 6; fi + +export BIN_OS="$(uname -s | tr [A-Z] [a-z])" +if [[ ! -z ${MSYSTEM} ]]; then export BIN_OS="msys2"; fi +export ENV_NAME="$(uname -s)" +export LDD="ldd" +export PKG_PATH="usr/local/lib/mupen64plus/" +if [[ "${REPO}" == *"core"* ]]; then + export PKG_PATH="usr/local/lib/" +elif [[ "${REPO}" == *"ui-console"* ]]; then + export PKG_PATH="usr/local/bin/" +fi + +if [[ "${ENV_NAME}" == *"Linux"* ]]; then + if [[ "${ARCH_ARG}" == "x86" ]]; then export PIC=1 PIE=1; fi + if [[ ${MULTILIB} -eq 0 ]]; then + if [[ "${CC_ARG}" == "GCC" ]]; then + if [[ "${ARCH_ARG}" == "x86" ]]; then export CC="i686-linux-gnu-${CC}" CXX="i686-linux-gnu-${CXX}"; fi + fi + fi +fi + +if [[ "${ENV_NAME}" == *"MINGW"* ]]; then + export INSTALL_OVERRIDE="PLUGINDIR="" SHAREDIR="" BINDIR="" MANDIR="" LIBDIR="" APPSDIR="" ICONSDIR="icons" INCDIR="api"" + export LDD="ntldd -R" + unset PKG_PATH +fi + +export G_REV="$(git rev-parse --short HEAD)" +export PKG_NAME="${REPO}-${BIN_OS}-${ARCH_ARG}-g${G_REV}" +if [[ -f "${GITHUB_ENV}" ]]; then + set +e + grep "PKG_NAME=${PKG_NAME}" "${GITHUB_ENV}" > /dev/null + if [[ ${?} -ne 0 ]]; then echo "PKG_NAME=${PKG_NAME}" >> "${GITHUB_ENV}"; fi + set -e +fi + +if [[ -z ${OPTFLAGS} ]]; then export OPTFLAGS="-O3 -flto ${CPU_TUNE}"; fi + +echo "" +echo ":: CC=${CC} CXX=${CXX} BITS=${BITS} ${CONFIG_OVERRIDE} ::" +echo "" +${CC} --version +echo "" + +make_clean () { + make CC="${CC}" CXX="${CXX}" BITS=${BITS} ${CONFIG_OVERRIDE} -C projects/unix clean + echo "" +} + +make_clean +make CC="${CC}" CXX="${CXX}" BITS=${BITS} ${CONFIG_OVERRIDE} -C projects/unix all -j4 +echo "" + +if [[ ! -d pkg ]]; then + mkdir pkg + chmod -R 755 pkg +fi + +pushd projects/unix > /dev/null +export ARTIFACT="$(find *mupen64plus* -type f 2> /dev/null | head -n 1)" +popd > /dev/null + +make CC="${CC}" CXX="${CXX}" BITS=${BITS} ${CONFIG_OVERRIDE} -C projects/unix install ${INSTALL_OVERRIDE} DESTDIR="$(pwd)/pkg/" +echo "" +make_clean + +if [[ -z ${ARTIFACT} ]]; then + exit 5 +else + cd pkg + ls -gG "${PKG_PATH}${ARTIFACT}" + echo "" + ${LDD} "${PKG_PATH}${ARTIFACT}" > ldd.log + cat ldd.log + echo "" + if [[ ${MAKE_PKG} -eq 1 ]]; then tar --owner=0 --group=0 --mode='og-w' -czf "${PKG_NAME}.tar.gz" usr; fi +fi + +exit 0 diff --git a/mupen64plus-core/.github/workflows/scripts/ci_get_libglew_i386.sh b/mupen64plus-core/.github/workflows/scripts/ci_get_libglew_i386.sh new file mode 100755 index 00000000..50bd7364 --- /dev/null +++ b/mupen64plus-core/.github/workflows/scripts/ci_get_libglew_i386.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +set -e +u + +# NOTE: There is no native "libglew-dev:i386" in Ubuntu 22.04, we will use Debian ones... + +export GLEWVER="$(apt list libglew-dev | grep "amd64" | cut -d ' ' -f2)" +export PKGS="$(apt list libglew* | grep "${GLEWVER}" | cut -d '/' -f1)" +export DEBSOURCE="http://http.us.debian.org/debian/pool/main/g/glew/" +export PKGVER_LS="$(curl -sS ${DEBSOURCE} | grep "${GLEWVER}" | grep "amd64" | cut -d '_' -f2 | sort)" +if [[ "${PKGVER_LS}" != "" ]]; then + for VER in ${PKGVER_LS}; do export PKGVER="${VER}"; done + cd /tmp + for PKG in ${PKGS}; do + for ARCH in amd64 i386; do curl -L -O "${DEBSOURCE}${PKG}_${PKGVER}_${ARCH}.deb"; done + done + sudo dpkg -i libglew*${PKGVER}*.deb +else + exit 9 +fi + +sudo ldconfig + +exit 0 diff --git a/mupen64plus-core/.github/workflows/scripts/ci_install_ubuntu_deps.sh b/mupen64plus-core/.github/workflows/scripts/ci_install_ubuntu_deps.sh new file mode 100755 index 00000000..19984467 --- /dev/null +++ b/mupen64plus-core/.github/workflows/scripts/ci_install_ubuntu_deps.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +set -e +u + +if [[ ${#} -lt 2 ]]; then exit 9; fi + +unset ARCH_DEP CC_DEP +export BUILD_DEPS_I386="crossbuild-essential-i386 g++${C_GCC_SUFFIX}-i686-linux-gnu gcc${C_GCC_SUFFIX}-i686-linux-gnu libc6-i386" +export HOTFIX_I386="libatomic1:i386 libgcc-s1:i386 libstdc++6:i386 ${HOTFIX_I386}" +export ENV_ARGS="$(echo "${*}" | tr [A-Z] [a-z])" + +for ARG in ${ENV_ARGS}; do + case "${ARG}" in + clang ) + export CC_DEP="clang${C_CLANG_SUFFIX}" + ;; + gcc ) + export CC_DEP="g++${C_GCC_SUFFIX} gcc${C_GCC_SUFFIX}" + ;; + multilib ) + export BUILD_DEPS_I386="g++${C_GCC_SUFFIX}-multilib gcc${C_GCC_SUFFIX}-multilib libc6-dev-i386" + ;; + x64 ) + export ARCH_DEP="x64" + ;; + x86 ) + export ARCH_DEP="x86" + ;; + esac +done + +if [[ -z ${ARCH_DEP} ]]; then exit 8; fi +if [[ -z ${CC_DEP} ]]; then exit 7; fi + +sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test +if [[ "${ARCH_DEP}" == "x86" ]]; then sudo dpkg --add-architecture i386; fi +sudo apt-get update +sudo apt-get -y install build-essential git nasm pkg-config ${CC_DEP} ${BUILD_DEPS} +if [[ "${ARCH_DEP}" == "x86" ]]; then + if [[ "${BUILD_DEPS}" != "" ]]; then + for DEP in ${BUILD_DEPS}; do + if [[ "${DEP}" != "libglew-dev" ]]; then export BUILD_DEPS_I386="${BUILD_DEPS_I386} ${DEP}:i386"; fi + done + fi + sudo apt-get --reinstall -y install ${BUILD_DEPS_I386} ${HOTFIX_I386} +fi +sudo ldconfig + +exit 0 diff --git a/mupen64plus-core/.github/workflows/scripts/ci_msvc_build.cmd b/mupen64plus-core/.github/workflows/scripts/ci_msvc_build.cmd new file mode 100644 index 00000000..2ab17503 --- /dev/null +++ b/mupen64plus-core/.github/workflows/scripts/ci_msvc_build.cmd @@ -0,0 +1,62 @@ +@echo off +setlocal enableextensions disabledelayedexpansion +cls +if not exist "projects\msvc\" exit /b 9 + +set ARCH= +set CONF=Release +if "%*" == "" exit /b 8 +for %%P in (%*) do ( + if /i "%%P" == "x86" set ARCH_ARG=x86& set ARCH=Win32 + if /i "%%P" == "x64" set ARCH_ARG=x64& set ARCH=x64 + if /i "%%P" == "newdyn" set CONF=New_Dynarec_Release +) + +if not defined ARCH exit /b 7 + +for %%T in (.) do set REPO=%%~nxT +if not defined REPO exit /b 6 + +set ARTIFACT= +set FPROJ= +set EXT=dll +echo %REPO% | findstr "ui-console" >nul 2>&1 +if not errorlevel 1 set EXT=exe +if not defined TOOLSET set TOOLSET=v143 + +for /f "tokens=1" %%R in ('git rev-parse --short HEAD') do set G_REV=%%R +set PKG_NAME=%REPO%-msvc-%ARCH_ARG%-g%G_REV% +if exist "%GITHUB_ENV%" ( + type "%GITHUB_ENV%" | findstr "PKG_NAME=%PKG_NAME%" >nul 2>&1 + if errorlevel 1 echo PKG_NAME=%PKG_NAME%>> "%GITHUB_ENV%" +) + +echo. +msbuild --version +echo. + +if not exist "..\mupen64plus-win32-deps\" git clone --depth 1 https://github.com/mupen64plus/mupen64plus-win32-deps.git ..\mupen64plus-win32-deps + +pushd projects\msvc\ +for /f "tokens=*" %%F in ('dir /b *.vcxproj') do set FPROJ=%%F +popd +if not defined FPROJ exit /b 5 + +echo. +msbuild "projects\msvc\%FPROJ%" /p:Configuration=%CONF%;Platform=%ARCH%;PlatformToolset=%TOOLSET% /t:Rebuild +if errorlevel 1 exit /b 4 +echo. + +if exist "projects\msvc\%ARCH%\Release\mupen64plus.dll" ren "projects\msvc\%ARCH%\Release\mupen64plus.dll" mupen64plus-old.dll + +pushd projects\msvc\%ARCH%\%CONF%\ +for /f "tokens=*" %%S in ('dir /b mupen64plus*%EXT%') do set ARTIFACT=%%S +popd +if not defined ARTIFACT exit /b 3 + +md pkg 2>nul +xcopy "projects\msvc\%ARCH%\%CONF%\%ARTIFACT%" pkg\ +if errorlevel 1 exit /b 2 +dir "pkg\%ARTIFACT%" + +exit /b 0 diff --git a/mupen64plus-core/.github/workflows/scripts/ci_nightly_artifacts.sh b/mupen64plus-core/.github/workflows/scripts/ci_nightly_artifacts.sh new file mode 100755 index 00000000..8b231449 --- /dev/null +++ b/mupen64plus-core/.github/workflows/scripts/ci_nightly_artifacts.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +set -e +u + +export REPO="${PWD##*/}" +if [[ "${REPO}" == "" ]]; then exit 9; fi + +rm -fr pkg +mkdir pkg +cd binaries +for BIN in *; do + cd "${BIN}" + case "${BIN}" in + *msvc* | *msys2* ) + echo ":: Creating ${BIN}.zip" + zip -r "../../pkg/${BIN}.zip" * + ;; + * ) + echo ":: Recovering ${BIN}.tar.gz" + mv *.tar.gz ../../pkg/ + ;; + esac + cd .. +done +cd ../pkg +echo "" + +for ZIP in *; do + ls -gG ${ZIP} + tigerdeep -lz ${ZIP} >> ../${REPO}.tiger.txt + sha256sum ${ZIP} >> ../${REPO}.sha256.txt + sha512sum ${ZIP} >> ../${REPO}.sha512.txt + b2sum ${ZIP} >> ../${REPO}.blake2.txt +done +mv ../${REPO}.*.txt . +echo "" + +for HASH in tiger sha256 sha512 blake2; do + echo "${HASH}:" | tr [a-z] [A-Z] + cat *.${HASH}.txt + echo "" +done + +if [[ -f "${GITHUB_ENV}" ]]; then + git tag -f nightly-build + git push -f origin nightly-build +fi + +exit 0 diff --git a/mupen64plus-core/.gitignore b/mupen64plus-core/.gitignore index 0b892f7b..7943afcb 100644 --- a/mupen64plus-core/.gitignore +++ b/mupen64plus-core/.gitignore @@ -1,2 +1,5 @@ /projects/unix/_obj*/ /projects/unix/libmupen64plus*.so* +/.vscode +/projects/unix/*.dll +/src/asm_defines/*.h diff --git a/mupen64plus-core/.gitrepo b/mupen64plus-core/.gitrepo index 40ab896e..485d903b 100644 --- a/mupen64plus-core/.gitrepo +++ b/mupen64plus-core/.gitrepo @@ -5,8 +5,8 @@ ; [subrepo] remote = https://github.com/libretro/mupen64plus-core.git - branch = master - commit = 1b8f83ea74f5a844030143a49887bf1de42cf9b5 - parent = 24609104ad745ae3649529721133e0b0ad95e76f + branch = rebase + commit = efb46b8a1cc7f7160d7bf7a0f1e4bf2ec3727fa3 + parent = 9d940bacb95c4d86733f42b67b57fc83046a6d39 method = rebase - cmdver = 0.4.1 + cmdver = 0.4.6 diff --git a/mupen64plus-core/README b/mupen64plus-core/README index 1045eef4..c346bff9 100644 --- a/mupen64plus-core/README +++ b/mupen64plus-core/README @@ -70,8 +70,12 @@ Type 'make' by itself to view all available build options: WARNFLAGS=flag == compiler warning levels (default: -Wall) PIC=(1|0) == Force enable/disable of position independent code OSD=(1|0) == Enable/disable build of OpenGL On-screen display + NETPLAY=1 == Enable netplay functionality, requires SDL2_net NEW_DYNAREC=1 == Replace dynamic recompiler with Ari64's experimental dynarec + KEYBINDINGS=0 == Disables the default keybindings + ACCURATE_FPU=1 == Enables accurate FPU behavior (i.e correct cause bits) OPENCV=1 == Enable OpenCV support + VULKAN=0 == Disable vulkan support for the default video extension implementation POSTFIX=name == String added to the name of the the build (default: '') Install Options: PREFIX=path == install/uninstall prefix (default: /usr/local/) diff --git a/mupen64plus-core/README.md b/mupen64plus-core/README.md index b74e3aa2..c7c5fc64 100644 --- a/mupen64plus-core/README.md +++ b/mupen64plus-core/README.md @@ -1,8 +1,8 @@ # Mupen64Plus-Core README -[![TravisCI Build Status](https://travis-ci.org/mupen64plus/mupen64plus-core.svg?branch=master)](https://travis-ci.org/mupen64plus/mupen64plus-core) +[![GitHub Actions Build Status](https://github.com/mupen64plus/mupen64plus-core/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/mupen64plus/mupen64plus-core/actions) [![Coverity Scan Build Status](https://scan.coverity.com/projects/4381/badge.svg)](https://scan.coverity.com/projects/mupen64plus-core) -[![AppVeyor Build status](https://ci.appveyor.com/api/projects/status/a1ua5t87n2w8a7fc?svg=true)](https://ci.appveyor.com/project/Narann/mupen64plus-core) +[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/a1ua5t87n2w8a7fc?svg=true)](https://ci.appveyor.com/project/Narann/mupen64plus-core) More documentation can be found on the [Mupen64Plus website](https://mupen64plus.org/docs/) and you can find a more complete README file on the [wiki](https://mupen64plus.org/wiki/index.php/README). @@ -76,8 +76,12 @@ Type `make` by itself to view all available build options: WARNFLAGS=flag == compiler warning levels (default: -Wall) PIC=(1|0) == Force enable/disable of position independent code OSD=(1|0) == Enable/disable build of OpenGL On-screen display + NETPLAY=1 == Enable netplay functionality, requires SDL2_net NEW_DYNAREC=1 == Replace dynamic recompiler with Ari64's experimental dynarec + KEYBINDINGS=0 == Disables the default keybindings + ACCURATE_FPU=1 == Enables accurate FPU behavior (i.e correct cause bits) OPENCV=1 == Enable OpenCV support + VULKAN=0 == Disable vulkan support for the default video extension implementation POSTFIX=name == String added to the name of the the build (default: '') Install Options: PREFIX=path == install/uninstall prefix (default: /usr/local/) diff --git a/mupen64plus-core/RELEASE b/mupen64plus-core/RELEASE index 0b0973bb..60f9f993 100644 --- a/mupen64plus-core/RELEASE +++ b/mupen64plus-core/RELEASE @@ -1,9 +1,831 @@ Mupen64Plus-Core Emulator Library RELEASE ----------------------------------------- -Mupen64Plus-core v2.5.9 - February 10, 2019 -------------------------------------------- - - *** BETA RELEASE *** For Testing Only *** +----------------------------------------- +# Mupen64Plus-core v2.6.0 - July 14, 2024 + +## Merged PRs + +* [1086](https://github.com/mupen64plus/mupen64plus-core/pull/1086): Backport mupen64plus-libretro-nx dynarec fix for OoTMM randomizer +* [1085](https://github.com/mupen64plus/mupen64plus-core/pull/1085): savestates: Only notify SAVECOMPLETE once save is actually done. + Fixes https://github.com/mupen64plus/mupen64plus-core/issues/1031 +* [1084](https://github.com/mupen64plus/mupen64plus-core/pull/1084): Add support for new PIF ROM and reset PIF +* [1082](https://github.com/mupen64plus/mupen64plus-core/pull/1082): Update "libmd5-rfc" code to the one used in CPython v2.7 repository +* [1079](https://github.com/mupen64plus/mupen64plus-core/pull/1079): CI/CD: Rearranged some parts of the logic, many details and more... +* [1077](https://github.com/mupen64plus/mupen64plus-core/pull/1077): CI/CD: Upgrade, generic scripts, independent schedule, etc +* [1076](https://github.com/mupen64plus/mupen64plus-core/pull/1076): Correct ROM file size checks +* [1075](https://github.com/mupen64plus/mupen64plus-core/pull/1075): ci: install vulkan-headers package in MSYS2 + (Fixes CI build failure) +* [1074](https://github.com/mupen64plus/mupen64plus-core/pull/1074): Update minizip +* [1061](https://github.com/mupen64plus/mupen64plus-core/pull/1061): RSP opcode fixes. + Ported from: https://gitlab.com/parallel-launcher/parallel-n64 +* [1060](https://github.com/mupen64plus/mupen64plus-core/pull/1060): src: fix the new_dynarec with musl +* [1059](https://github.com/mupen64plus/mupen64plus-core/pull/1059): Add missing ROM file size checks +* [1058](https://github.com/mupen64plus/mupen64plus-core/pull/1058): Added 3 translations and a game. +* [1057](https://github.com/mupen64plus/mupen64plus-core/pull/1057): Fix Big-Endian Incompatibilities. + (Tested on a Wii U.) +* [1056](https://github.com/mupen64plus/mupen64plus-core/pull/1056): Add redumps/correct bad ROM IDs and add some unlicensed releases. + Based on the latest (2024/5/1) version of the No-Intro database. +* [1054](https://github.com/mupen64plus/mupen64plus-core/pull/1054): Increase maximum allowed overclocking factor to 20 +* [1046](https://github.com/mupen64plus/mupen64plus-core/pull/1046): Added Italian translations. +* [1040](https://github.com/mupen64plus/mupen64plus-core/pull/1040): Fix incorrect OSAL_DIR_SEPARATORS when compiling using mingw. + Fixes https://github.com/mupen64plus/mupen64plus-core/issues/937 +* [1039](https://github.com/mupen64plus/mupen64plus-core/pull/1039): bfd version check fix for X.XX version number format +* [1036](https://github.com/mupen64plus/mupen64plus-core/pull/1036): use CS4 for netplay DSCP +* [1034](https://github.com/mupen64plus/mupen64plus-core/pull/1034): Added English translation of Bomberman 64 - Arcade Edition +* [1033](https://github.com/mupen64plus/mupen64plus-core/pull/1033): use memchr instead of strchr in is_viewer.c +* [1032](https://github.com/mupen64plus/mupen64plus-core/pull/1032): Adding 4 more translations and updating ROM database. +* [1030](https://github.com/mupen64plus/mupen64plus-core/pull/1030): rom: Fix closing the ROM database +* [1029](https://github.com/mupen64plus/mupen64plus-core/pull/1029): Update README* files +* [1027](https://github.com/mupen64plus/mupen64plus-core/pull/1027): new_dynarec: fix FPU crashes. + Fixes https://github.com/mupen64plus/mupen64plus-core/issues/1026 +* [1023](https://github.com/mupen64plus/mupen64plus-core/pull/1023): Added 3 more translations +* [1022](https://github.com/mupen64plus/mupen64plus-core/pull/1022): Add Video Extension functions to support Vulkan +* [1021](https://github.com/mupen64plus/mupen64plus-core/pull/1021): netplay fixes from simple64 +* [1020](https://github.com/mupen64plus/mupen64plus-core/pull/1020): Updating rom database +* [1019](https://github.com/mupen64plus/mupen64plus-core/pull/1019): Fix IS64 Truncating Lines +* [1018](https://github.com/mupen64plus/mupen64plus-core/pull/1018): Added French translation of Conker's Bad Fur Day +* [1017](https://github.com/mupen64plus/mupen64plus-core/pull/1017): Update xxhash to 0.8.2 +* [1015](https://github.com/mupen64plus/mupen64plus-core/pull/1015): Fix building with libbfd >=2.39 +* [1014](https://github.com/mupen64plus/mupen64plus-core/pull/1014): Add M64CORE_SCREENSHOT_CAPTURED to m64p_core_param. + (Allows front-ends to know when a screenshot has been captured.) +* [1012](https://github.com/mupen64plus/mupen64plus-core/pull/1012): Change IPL3 memory detection error to a warning +* [1011](https://github.com/mupen64plus/mupen64plus-core/pull/1011): Add Xeno Crisis to the ROM database +* [1009](https://github.com/mupen64plus/mupen64plus-core/pull/1009): Ensure out of bounds RDRAM reads 0 +* [1008](https://github.com/mupen64plus/mupen64plus-core/pull/1008): Add some Spanish translations. +* [1006](https://github.com/mupen64plus/mupen64plus-core/pull/1006): Implement unused RDRAM address range 0x0800000-0x03EFFFFF. + (Fixes an in-game crash in Paper Mario when hitting a specific tree with a hammer.) +* [1005](https://github.com/mupen64plus/mupen64plus-core/pull/1005): CI/CD: Fixes Ubuntu i386 builds, among other changes +* [1002](https://github.com/mupen64plus/mupen64plus-core/pull/1002): vidext.c: Fix DPI scaling issues on Windows +* [1001](https://github.com/mupen64plus/mupen64plus-core/pull/1001): Interpreter accuracy improvements +* [998](https://github.com/mupen64plus/mupen64plus-core/pull/998): Introduce SaveFilenameFormat parameter +* [995](https://github.com/mupen64plus/mupen64plus-core/pull/995): CI/CD: Integrate scheduled builds and other... +* [994](https://github.com/mupen64plus/mupen64plus-core/pull/994): [64DD] Shorten extra cycles timing depending on motor state for stability +* [993](https://github.com/mupen64plus/mupen64plus-core/pull/993): Sanitize save filename +* [992](https://github.com/mupen64plus/mupen64plus-core/pull/992): Fix screenshots when compiling with OSD=0 +* [990](https://github.com/mupen64plus/mupen64plus-core/pull/990): Improve 64DD emulation +* [989](https://github.com/mupen64plus/mupen64plus-core/pull/989): Update that helps diagnose and in some cases circumvent issues... +* [986](https://github.com/mupen64plus/mupen64plus-core/pull/986): Fix incorrect DDREGION being used for development disks +* [983](https://github.com/mupen64plus/mupen64plus-core/pull/983): Introduce M64CMD_DISK_OPEN & M64CMD_DISK_CLOSE +* [980](https://github.com/mupen64plus/mupen64plus-core/pull/980): Have netplay just use file extension for file syncing +* [979](https://github.com/mupen64plus/mupen64plus-core/pull/979): use MD5 for save filename +* [977](https://github.com/mupen64plus/mupen64plus-core/pull/977): Add support for Advanced Homebrew ROM Header save type +* [976](https://github.com/mupen64plus/mupen64plus-core/pull/976): Add ROM version to m64p_rom_header +* [974](https://github.com/mupen64plus/mupen64plus-core/pull/974): CI/CD: Update MSVC +* [970](https://github.com/mupen64plus/mupen64plus-core/pull/970): Fix crash due to SDL_SetVideoMode in SDL 2 compatibility code. +* [967](https://github.com/mupen64plus/mupen64plus-core/pull/967): Proper response when eeprom absent +* [965](https://github.com/mupen64plus/mupen64plus-core/pull/965): Add Smash Remix 1.2.0 +* [963](https://github.com/mupen64plus/mupen64plus-core/pull/963): Added GoldenEye X and Perfect Dark Plus to INI +* [961](https://github.com/mupen64plus/mupen64plus-core/pull/961): CI/CD: Update +* [960](https://github.com/mupen64plus/mupen64plus-core/pull/960): 3 LSBs of AI Length must be 0 +* [958](https://github.com/mupen64plus/mupen64plus-core/pull/958): Initialize dd_rom_size to 0 if load_dd_disk fails to fix possible runtime errors during initialization +* [954](https://github.com/mupen64plus/mupen64plus-core/pull/954): Add support for clang/mingw targets +* [946](https://github.com/mupen64plus/mupen64plus-core/pull/946): Use a predictable seed when using netplay +* [929](https://github.com/mupen64plus/mupen64plus-core/pull/929): CountPerOp=1 for Bottom of the 9th. + Fixes https://github.com/mupen64plus/mupen64plus-core/issues/927 +* [926](https://github.com/mupen64plus/mupen64plus-core/pull/926): Add ROM entries for SM64 splitscreen hack +* [925](https://github.com/mupen64plus/mupen64plus-core/pull/925): Entry for SmashRemix1.1.0 +* [922](https://github.com/mupen64plus/mupen64plus-core/pull/922): Update xxhash to 0.8.1 +* [920](https://github.com/mupen64plus/mupen64plus-core/pull/920): Allow frontends to know what DD ROM region they should use +* [919](https://github.com/mupen64plus/mupen64plus-core/pull/919): Generate unique mempaks ID when formatting them +* [912](https://github.com/mupen64plus/mupen64plus-core/pull/912): CI/CD: Implement GitHub Actions and public nightly builds +* [911](https://github.com/mupen64plus/mupen64plus-core/pull/911): Add Donkey Kong 64 - Tag Anywhere V5 (U) +* [910](https://github.com/mupen64plus/mupen64plus-core/pull/910): Native Apple Silicon (darwin-arm64) Support +* [909](https://github.com/mupen64plus/mupen64plus-core/pull/909): Fix IS Viewer crash. + Fixes a segfault in [The Missing Link](https://www.romhacking.net/hacks/5334/) when entering a door. +* [901](https://github.com/mupen64plus/mupen64plus-core/pull/901): Add SmashRemix1.0.0/1.0.1 +* [899](https://github.com/mupen64plus/mupen64plus-core/pull/899): Add NO_KEYBINDINGS for joy mappings +* [898](https://github.com/mupen64plus/mupen64plus-core/pull/898): Add RISC-V Support +* [893](https://github.com/mupen64plus/mupen64plus-core/pull/893): Add KEYBINDINGS=0 option to Makefile +* [890](https://github.com/mupen64plus/mupen64plus-core/pull/890): Fix https://github.com/mupen64plus/mupen64plus-core/issues/889 +* [887](https://github.com/mupen64plus/mupen64plus-core/pull/887): Fix https://github.com/mupen64plus/mupen64plus-core/issues/886 +* [884](https://github.com/mupen64plus/mupen64plus-core/pull/884): Fix https://github.com/mupen64plus/mupen64plus-core/issues/883 +* [881](https://github.com/mupen64plus/mupen64plus-core/pull/881): Fix for compiler warning +* [880](https://github.com/mupen64plus/mupen64plus-core/pull/880): Fix 64DD Write Address Check Regression +* [878](https://github.com/mupen64plus/mupen64plus-core/pull/878): Make 4K EEPROM default save type; support carts with no EEPROM +* [876](https://github.com/mupen64plus/mupen64plus-core/pull/876): IS Viewer support. + IS-Viewer 64 was a physical debugging tool (https://n64squid.com/homebrew/n64-sdk/development-hardware/). +* [873](https://github.com/mupen64plus/mupen64plus-core/pull/873): VRU support and Hey You Pikachu audio fix +* [872](https://github.com/mupen64plus/mupen64plus-core/pull/872): Fix address check for DD DMA Interrupt. + Fixes the issue identified in https://github.com/mupen64plus/mupen64plus-core/pull/868#issuecomment-861760293 +* [871](https://github.com/mupen64plus/mupen64plus-core/pull/871): Misc fixes +* [869](https://github.com/mupen64plus/mupen64plus-core/pull/869): Update RSP alignment code as per CEN64 +* [868](https://github.com/mupen64plus/mupen64plus-core/pull/868): Update PI DMA alignment as per CEN64 +* [867](https://github.com/mupen64plus/mupen64plus-core/pull/867): Rearranged order of Core settings in the config file +* [866](https://github.com/mupen64plus/mupen64plus-core/pull/866): invalidate alternate address on write. + Fixes https://github.com/mupen64plus/mupen64plus-core/issues/738 +* [865](https://github.com/mupen64plus/mupen64plus-core/pull/865): Fix Pokemon Puzzle League saving +* [863](https://github.com/mupen64plus/mupen64plus-core/pull/863): Get Xena (E) to boot +* [860](https://github.com/mupen64plus/mupen64plus-core/pull/860): Support Unicode on Windows +* [858](https://github.com/mupen64plus/mupen64plus-core/pull/858): Add SmashRemix0.9.7 +* [854](https://github.com/mupen64plus/mupen64plus-core/pull/854): Revert "Prevents game freezing unrelated to dynarecs" +* [853](https://github.com/mupen64plus/mupen64plus-core/pull/853): Prevents game freezing unrelated to dynarecs +* [848](https://github.com/mupen64plus/mupen64plus-core/pull/848): Fix https://github.com/mupen64plus/mupen64plus-core/issues/842 +* [847](https://github.com/mupen64plus/mupen64plus-core/pull/847): Netplay fixes from Android port +* [843](https://github.com/mupen64plus/mupen64plus-core/pull/843): [new_dynarec] Trivial perf improvement on initial recompilation +* [841](https://github.com/mupen64plus/mupen64plus-core/pull/841): Remove CountPerOp=1 for Dino Planet +* [840](https://github.com/mupen64plus/mupen64plus-core/pull/840): Add Dinosaur Planet to ini +* [836](https://github.com/mupen64plus/mupen64plus-core/pull/836): Fix some warnings about wrong pointer format "%p" needs a void* +* [835](https://github.com/mupen64plus/mupen64plus-core/pull/835): Change mempak formatting +* [834](https://github.com/mupen64plus/mupen64plus-core/pull/834): refactor lba_end computation to better convey intent +* [832](https://github.com/mupen64plus/mupen64plus-core/pull/832): 64dd save rebased +* [830](https://github.com/mupen64plus/mupen64plus-core/pull/830): Fix some unsigned/signed comparison warnings +* [828](https://github.com/mupen64plus/mupen64plus-core/pull/828): Mention save slot keybindings in docs +* [827](https://github.com/mupen64plus/mupen64plus-core/pull/827): Add prefix to m64p_rom_save_type members +* [825](https://github.com/mupen64plus/mupen64plus-core/pull/825): Ai fixes +* [820](https://github.com/mupen64plus/mupen64plus-core/pull/820): Add correct MD5 for SmashRemix0.9.5b to INI +* [817](https://github.com/mupen64plus/mupen64plus-core/pull/817): Failing to explain the asm scripts in a better way... +* [816](https://github.com/mupen64plus/mupen64plus-core/pull/816): [Disk] Make sure the wrong System Data is checked upon read of the System Area +* [815](https://github.com/mupen64plus/mupen64plus-core/pull/815): Simplify and make the asm script more robust to 3rd party scripts +* [814](https://github.com/mupen64plus/mupen64plus-core/pull/814): Add SmashRemix0.9.5b to INI +* [811](https://github.com/mupen64plus/mupen64plus-core/pull/811): build: Allow out of tree builds. +* [809](https://github.com/mupen64plus/mupen64plus-core/pull/809): docs: fix simple typo, unconditonal -> unconditional +* [808](https://github.com/mupen64plus/mupen64plus-core/pull/808): Fix to MacOSX building +* [803](https://github.com/mupen64plus/mupen64plus-core/pull/803): storage backend save method nows know about modified byte range +* [801](https://github.com/mupen64plus/mupen64plus-core/pull/801): Fixes +* [798](https://github.com/mupen64plus/mupen64plus-core/pull/798): Allow customizable save slot keybindings +* [796](https://github.com/mupen64plus/mupen64plus-core/pull/796): Align memory for parallel-rdp +* [794](https://github.com/mupen64plus/mupen64plus-core/pull/794): Clean up netplay linked list +* [793](https://github.com/mupen64plus/mupen64plus-core/pull/793): Just stop frontend from enabling cheats during netplay +* [791](https://github.com/mupen64plus/mupen64plus-core/pull/791): Allow front-ends to override user paths +* [789](https://github.com/mupen64plus/mupen64plus-core/pull/789): Add count factor overclock +* [788](https://github.com/mupen64plus/mupen64plus-core/pull/788): Add M64CMD_ROM_SET_SETTINGS +* [785](https://github.com/mupen64plus/mupen64plus-core/pull/785): Tag outgoing UDP packets with EF DSCP +* [784](https://github.com/mupen64plus/mupen64plus-core/pull/784): CountPerOp=1 for Smash +* [782](https://github.com/mupen64plus/mupen64plus-core/pull/782): Update xxHash to 0.8.0 +* [779](https://github.com/mupen64plus/mupen64plus-core/pull/779): Fix typo in M64CMD_NETPLAY_INIT docs +* [775](https://github.com/mupen64plus/mupen64plus-core/pull/775): Improve compatibility on restrictive platforms and other minor changes +* [771](https://github.com/mupen64plus/mupen64plus-core/pull/771): Makefile: add missing quotation mark. +* [768](https://github.com/mupen64plus/mupen64plus-core/pull/768): CountPerOp=3 for Wave Race 64 - Shindou Edition +* [766](https://github.com/mupen64plus/mupen64plus-core/pull/766): Allow to set custom platform toolset from commands +* [764](https://github.com/mupen64plus/mupen64plus-core/pull/764): Get/Set Refresh Rate +* [762](https://github.com/mupen64plus/mupen64plus-core/pull/762): [Disk] Major Disk Support Changes +* [758](https://github.com/mupen64plus/mupen64plus-core/pull/758): Allow frontends to manage SDL's lifetime +* [756](https://github.com/mupen64plus/mupen64plus-core/pull/756): NOT mempak checksum when pak is not present +* [753](https://github.com/mupen64plus/mupen64plus-core/pull/753): Netplay +* [752](https://github.com/mupen64plus/mupen64plus-core/pull/752): Mask DRAM address in PI DMA +* [751](https://github.com/mupen64plus/mupen64plus-core/pull/751): Enable FBInfo for CPU-RDP rendering +* [750](https://github.com/mupen64plus/mupen64plus-core/pull/750): Removed "not officially supported" for ARM +* [748](https://github.com/mupen64plus/mupen64plus-core/pull/748): CountPerOp=1 for Top Gear Overdrive/Hyperbike +* [747](https://github.com/mupen64plus/mupen64plus-core/pull/747): fix compilation on x86 +* [746](https://github.com/mupen64plus/mupen64plus-core/pull/746): Fix hard reset crash +* [745](https://github.com/mupen64plus/mupen64plus-core/pull/745): Implement trap instructions +* [744](https://github.com/mupen64plus/mupen64plus-core/pull/744): CountPerOp=1 for Battle for Naboo +* [743](https://github.com/mupen64plus/mupen64plus-core/pull/743): Rework flashram implementation +* [741](https://github.com/mupen64plus/mupen64plus-core/pull/741): Fix path expansion when building with VisualStudio so directories with spaces will not cause the build to fail. +* [740](https://github.com/mupen64plus/mupen64plus-core/pull/740): Use XXH3 hashing (v0.7.3) +* [739](https://github.com/mupen64plus/mupen64plus-core/pull/739): IP0 and IP1 CAUSE bits are writeable +* [737](https://github.com/mupen64plus/mupen64plus-core/pull/737): BattleTanx (U) games need CountPerOp=3 to boot +* [736](https://github.com/mupen64plus/mupen64plus-core/pull/736): Fix multiple definition errors +* [735](https://github.com/mupen64plus/mupen64plus-core/pull/735): PIF binary boot rom support +* [734](https://github.com/mupen64plus/mupen64plus-core/pull/734): Support FBWrites with a size of 3 +* [731](https://github.com/mupen64plus/mupen64plus-core/pull/731): Disable FTZ (flush to zero) mode for SSE when FS bit is set +* [729](https://github.com/mupen64plus/mupen64plus-core/pull/729): Improve glue code generation +* [727](https://github.com/mupen64plus/mupen64plus-core/pull/727): CountPerOp=1 for Elmo's Letter Adventure +* [725](https://github.com/mupen64plus/mupen64plus-core/pull/725): Implement PIF write flags properly +* [721](https://github.com/mupen64plus/mupen64plus-core/pull/721): Emulate RSP DMA FIFO queue +* [718](https://github.com/mupen64plus/mupen64plus-core/pull/718): Adjust DD DMA timing +* [717](https://github.com/mupen64plus/mupen64plus-core/pull/717): Don't check event count to test if event is pending +* [716](https://github.com/mupen64plus/mupen64plus-core/pull/716): Change some DD logging to only happen in verbose mode +* [714](https://github.com/mupen64plus/mupen64plus-core/pull/714): Use cheat code to allow Rat Attack to boot +* [710](https://github.com/mupen64plus/mupen64plus-core/pull/710): Disable FBInfo support for dynarecs +* [709](https://github.com/mupen64plus/mupen64plus-core/pull/709): Proper handling of divide by zero +* [708](https://github.com/mupen64plus/mupen64plus-core/pull/708): Rework interrupt scheduling +* [707](https://github.com/mupen64plus/mupen64plus-core/pull/707): Implement cart ROM writes as described by Jan Goldacker. + Fixes https://github.com/mupen64plus/mupen64plus-core/issues/609 +* [706](https://github.com/mupen64plus/mupen64plus-core/pull/706): Enable transferpak write +* [705](https://github.com/mupen64plus/mupen64plus-core/pull/705): Only align cart address in PI DMA. + This PR fixes https://github.com/mupen64plus/mupen64plus-core/issues/600 +* [702](https://github.com/mupen64plus/mupen64plus-core/pull/702): Remove next_vi. + Should fix https://github.com/mupen64plus/mupen64plus-core/issues/554 +* [699](https://github.com/mupen64plus/mupen64plus-core/pull/699): Revert "Don't call SDL_PumpEvents() in core" +* [698](https://github.com/mupen64plus/mupen64plus-core/pull/698): Don't set DPC_STATUS_START/END_VALID bits +* [697](https://github.com/mupen64plus/mupen64plus-core/pull/697): Update xxHash to 0.7.2 +* [696](https://github.com/mupen64plus/mupen64plus-core/pull/696): if config parameter already exists, add help text if missing, before returning successfully +* [694](https://github.com/mupen64plus/mupen64plus-core/pull/694): Let MSBuild do its job and implement a proper AppVeyor with artifact packaging +* [692](https://github.com/mupen64plus/mupen64plus-core/pull/692): Fix VI interrupt not being called in certain cases +* [687](https://github.com/mupen64plus/mupen64plus-core/pull/687): Database updates +* [682](https://github.com/mupen64plus/mupen64plus-core/pull/682): MPAL's refresh rate is 60 Hz +* [670](https://github.com/mupen64plus/mupen64plus-core/pull/670): Set VSync (SDL_GL_SetSwapInterval) after GL context is available. + This fixes https://github.com/mupen64plus/mupen64plus-core/issues/663 +* [666](https://github.com/mupen64plus/mupen64plus-core/pull/666): Add speed limiter toggle, reintroduce SDL_PumpEvents for non-android platforms +* [632](https://github.com/mupen64plus/mupen64plus-core/pull/632): Add ppc64le support + + +## Closed Issues + +* [1031](https://github.com/mupen64plus/mupen64plus-core/issues/1031): M64CORE_STATE_SAVECOMPLETE callback happens too early with M64P_PARALLEL +* [1081](https://github.com/mupen64plus/mupen64plus-core/issues/1081): Fix buffer overflow in RSP DMA - SECURITY CONCERN +* [1065](https://github.com/mupen64plus/mupen64plus-core/issues/1065): [Proposal] Features for tool-assisted speedrunning +* [622](https://github.com/mupen64plus/mupen64plus-core/issues/622): Kirby 64 texture stretching +* [849](https://github.com/mupen64plus/mupen64plus-core/issues/849): Random Game Crash/Freeze in Ocarina of Time with Dynamic Recompiler +* [551](https://github.com/mupen64plus/mupen64plus-core/issues/551): perfect dark flickers quite a bit. +* [915](https://github.com/mupen64plus/mupen64plus-core/issues/915): South Park - Chef's Luv Shack Not working +* [1049](https://github.com/mupen64plus/mupen64plus-core/issues/1049): Missing ROM file size validation +* [1042](https://github.com/mupen64plus/mupen64plus-core/issues/1042): mupen64plus-core no longer builds on Apple Silicon +* [1041](https://github.com/mupen64plus/mupen64plus-core/issues/1041): Help, unplayable stuttering +* [937](https://github.com/mupen64plus/mupen64plus-core/issues/937): 64DD: Wrong path to save file [Windows] +* [1037](https://github.com/mupen64plus/mupen64plus-core/issues/1037): Control Stick in Pokemon Snap not working +* [1028](https://github.com/mupen64plus/mupen64plus-core/issues/1028): mupen64plus-core.svg missing in Readme.md +* [1026](https://github.com/mupen64plus/mupen64plus-core/issues/1026): Crash (Segfault) on 32bit ARM & DynaRec enabled +* [493](https://github.com/mupen64plus/mupen64plus-core/issues/493): FBInfo missing some data with dynarec +* [1003](https://github.com/mupen64plus/mupen64plus-core/issues/1003): How To Correctly Install Mupen64Plus in Ubuntu +* [1000](https://github.com/mupen64plus/mupen64plus-core/issues/1000): Mupen64Plus has own Screen Resolution on 1.15.0 +* [999](https://github.com/mupen64plus/mupen64plus-core/issues/999): Error: dlopen('/usr/local/lib/libmupen64plus.so.2') failed: libminizip.so.1: cannot open shared object file: No such file or directory +* [157](https://github.com/mupen64plus/mupen64plus-core/issues/157): Hey You, Pikachu! VRU Emulation Support +* [646](https://github.com/mupen64plus/mupen64plus-core/issues/646): Pausing causes the emulator to lock up +* [988](https://github.com/mupen64plus/mupen64plus-core/issues/988): asm_defines_nasm.h is not generated on Windows +* [978](https://github.com/mupen64plus/mupen64plus-core/issues/978): With `AutoStateSlotIncrement = True` `CurrentStateSlot` is not updated +* [875](https://github.com/mupen64plus/mupen64plus-core/issues/875): [Feature Request] Overclocking support +* [823](https://github.com/mupen64plus/mupen64plus-core/issues/823): Various input-related bugs in Paper Mario +* [971](https://github.com/mupen64plus/mupen64plus-core/issues/971): Crash from Core Error: SDL_SetVIdeoMode failure due to invalid 'pitch' +* [975](https://github.com/mupen64plus/mupen64plus-core/issues/975): Coverity scan defects founds +* [972](https://github.com/mupen64plus/mupen64plus-core/issues/972): Audio crackling problem with ParaLLEl-RDP +* [969](https://github.com/mupen64plus/mupen64plus-core/issues/969): Crash with Core Error: SDL_SetVideoMode failed and multiple Core Warnings +* [966](https://github.com/mupen64plus/mupen64plus-core/issues/966): Document Vidext_GL_GetDefaultFramebuffer +* [956](https://github.com/mupen64plus/mupen64plus-core/issues/956): Add CMake project +* [957](https://github.com/mupen64plus/mupen64plus-core/issues/957): "print_insn_i386 undeclared" when building with DEBUGGER=1 +* [931](https://github.com/mupen64plus/mupen64plus-core/issues/931): Fix FBInfo implementation (for Jet Force Gemini) +* [948](https://github.com/mupen64plus/mupen64plus-core/issues/948): Per-Rom configuration C# interop broken +* [945](https://github.com/mupen64plus/mupen64plus-core/issues/945): Diddy Kong Racing freezes at 2nd Wizpig race cut scene +* [877](https://github.com/mupen64plus/mupen64plus-core/issues/877): Travis CI not working +* [159](https://github.com/mupen64plus/mupen64plus-core/issues/159): Mario Story (JPN) freeze +* [933](https://github.com/mupen64plus/mupen64plus-core/issues/933): Buck Bumble hangs on the ending cutscene of Mission 3 +* [927](https://github.com/mupen64plus/mupen64plus-core/issues/927): Bottom of the 9th sluggish when loading match +* [928](https://github.com/mupen64plus/mupen64plus-core/issues/928): Bottom of the 9th - some images are Blue tinted +* [885](https://github.com/mupen64plus/mupen64plus-core/issues/885): Legend of Zelda, The - Ocarina of Time (U) (V1.0) [T+Por1.5BetaFinal] can crash with new dynarec +* [917](https://github.com/mupen64plus/mupen64plus-core/issues/917): San Francisco Rush - No Sound +* [822](https://github.com/mupen64plus/mupen64plus-core/issues/822): Illegal instruction (core dumped) +* [245](https://github.com/mupen64plus/mupen64plus-core/issues/245): Mario Kart 64 item not usable bug +* [761](https://github.com/mupen64plus/mupen64plus-core/issues/761): [Audio Sync] Super Smash Bros. (Europe) Intro speed issues +* [861](https://github.com/mupen64plus/mupen64plus-core/issues/861): New Release +* [902](https://github.com/mupen64plus/mupen64plus-core/issues/902): 2.5.9 Linux 64 binary bundle - Illegal instruction +* [900](https://github.com/mupen64plus/mupen64plus-core/issues/900): [BUG] Zelda OOT, EnableFBEmulation = true +* [897](https://github.com/mupen64plus/mupen64plus-core/issues/897): CPU type "riscv64" not supported. +* [657](https://github.com/mupen64plus/mupen64plus-core/issues/657): Issue with the mupen64plus dynarec in Indiana Jones and the Infernal Machine +* [894](https://github.com/mupen64plus/mupen64plus-core/issues/894): unzip.h not found +* [889](https://github.com/mupen64plus/mupen64plus-core/issues/889): Dinosaur Planet crashes with new dynarec +* [888](https://github.com/mupen64plus/mupen64plus-core/issues/888): MINGW32 -> x86 compilation +* [886](https://github.com/mupen64plus/mupen64plus-core/issues/886): Road rush 64 black screen on new game with new dynarec +* [441](https://github.com/mupen64plus/mupen64plus-core/issues/441): Banjo-Tooie freeze +* [883](https://github.com/mupen64plus/mupen64plus-core/issues/883): NBA Jam '99/2000 crash on startup with dynarec (aarch64) +* [610](https://github.com/mupen64plus/mupen64plus-core/issues/610): Jet force gimini (U) locks up +* [372](https://github.com/mupen64plus/mupen64plus-core/issues/372): Top Gear Rally freezing frames +* [704](https://github.com/mupen64plus/mupen64plus-core/issues/704): Random crashes when loading save states +* [520](https://github.com/mupen64plus/mupen64plus-core/issues/520): Still some crashes, probably when loading save states +* [600](https://github.com/mupen64plus/mupen64plus-core/issues/600): Command and conquer in-game save corruption +* [171](https://github.com/mupen64plus/mupen64plus-core/issues/171): Glover crash +* [174](https://github.com/mupen64plus/mupen64plus-core/issues/174): Audio cuts off in Army Men: Sarge's Heroes with new dynarec +* [433](https://github.com/mupen64plus/mupen64plus-core/issues/433): MRC - Multi Racing Championship missing ground with new dynarec +* [631](https://github.com/mupen64plus/mupen64plus-core/issues/631): arm dynarec crash with stop_after_jal=0 +* [806](https://github.com/mupen64plus/mupen64plus-core/issues/806): World Driving Championship doesn't boot with new dynarec +* [856](https://github.com/mupen64plus/mupen64plus-core/issues/856): Forcing ROM-side High VSync Mode Gets Ignored By ALL Mupen Forks +* [679](https://github.com/mupen64plus/mupen64plus-core/issues/679): Cannot exit fullscreen with ALT+ENTER when launched with Glide64mk2 video plugin +* [608](https://github.com/mupen64plus/mupen64plus-core/issues/608): Minizip header build failure +* [633](https://github.com/mupen64plus/mupen64plus-core/issues/633): Display Issues when ResY > 1000 +* [639](https://github.com/mupen64plus/mupen64plus-core/issues/639): Crash on hard reset using Pure Interpreter/Dynarec +* [712](https://github.com/mupen64plus/mupen64plus-core/issues/712): fails to build with -fno-common or gcc-10 +* [755](https://github.com/mupen64plus/mupen64plus-core/issues/755): Black bar in many games when framebuffer emulation is ticked +* [821](https://github.com/mupen64plus/mupen64plus-core/issues/821): [mupen64plus:15] Failed to load library file +* [845](https://github.com/mupen64plus/mupen64plus-core/issues/845): video-glide64mk2 won't start game +* [594](https://github.com/mupen64plus/mupen64plus-core/issues/594): Compiling for ARM? +* [517](https://github.com/mupen64plus/mupen64plus-core/issues/517): Unable to build on Raspberry Pi 3 / Raspbian - error: ‘EAX’ undeclared +* [408](https://github.com/mupen64plus/mupen64plus-core/issues/408): Cant use emulation? +* [378](https://github.com/mupen64plus/mupen64plus-core/issues/378): Can't compile on Linux ARM (64 bit kernel, 32 bit user land) +* [292](https://github.com/mupen64plus/mupen64plus-core/issues/292): Mupen64plus.cfg changes being overwritten automatically after game launch. +* [252](https://github.com/mupen64plus/mupen64plus-core/issues/252): Compile fails on src Linux Armv7l +* [224](https://github.com/mupen64plus/mupen64plus-core/issues/224): Compilation not producing executable +* [305](https://github.com/mupen64plus/mupen64plus-core/issues/305): Worms Armageddon new dynarec crash +* [286](https://github.com/mupen64plus/mupen64plus-core/issues/286): FIFA 99: Players are floating (x64 Dynarec) +* [738](https://github.com/mupen64plus/mupen64plus-core/issues/738): libdragon games only work with Pure Interpreter +* [870](https://github.com/mupen64plus/mupen64plus-core/issues/870): Smash Remix 0.9.7 locked at 30fps on Retroarch Mupen64Plus-Next core +* [864](https://github.com/mupen64plus/mupen64plus-core/issues/864): Core won't compile. +* [862](https://github.com/mupen64plus/mupen64plus-core/issues/862): Error LNK1181 cannot open input file 'Win32\Release\dyna_start.obj' +* [838](https://github.com/mupen64plus/mupen64plus-core/issues/838): Input doesn't work in Wolfenstein 3d N64 port +* [850](https://github.com/mupen64plus/mupen64plus-core/issues/850): Mupen64Plus Core Configuring gamepad buggy +* [842](https://github.com/mupen64plus/mupen64plus-core/issues/842): Dinosaur planet beta TLB exceptions in new dynarec +* [846](https://github.com/mupen64plus/mupen64plus-core/issues/846): Virtual N64 controller Notches +* [812](https://github.com/mupen64plus/mupen64plus-core/issues/812): Unknown "no name" ROMs passes as known without even checking the MD5/CRC +* [640](https://github.com/mupen64plus/mupen64plus-core/issues/640): New dynarec crashing when opening 2nd ROM +* [837](https://github.com/mupen64plus/mupen64plus-core/issues/837): Input +* [673](https://github.com/mupen64plus/mupen64plus-core/issues/673): Uncached memory code execution support +* [826](https://github.com/mupen64plus/mupen64plus-core/issues/826): PR #788 breaks build of mupen64plus-video-rice +* [576](https://github.com/mupen64plus/mupen64plus-core/issues/576): banjo tooie tip-toe using keyboard (move banjo analog 5% to 10% range) +* [786](https://github.com/mupen64plus/mupen64plus-core/issues/786): Undefined behaviour in rsp-hle +* [813](https://github.com/mupen64plus/mupen64plus-core/issues/813): Video displays at the lower left corner, no full screen +* [797](https://github.com/mupen64plus/mupen64plus-core/issues/797): Unable to start Mupen64Plus on Slackware Linux (2.5.0) +* [800](https://github.com/mupen64plus/mupen64plus-core/issues/800): m64p_rom_header struct outdated? +* [787](https://github.com/mupen64plus/mupen64plus-core/issues/787): Rat Attack Not Booting +* [790](https://github.com/mupen64plus/mupen64plus-core/issues/790): Nuclear Escape button +* [350](https://github.com/mupen64plus/mupen64plus-core/issues/350): RSP DMA double buffering not implemented +* [732](https://github.com/mupen64plus/mupen64plus-core/issues/732): Donkey Kong 64 (U) [f2] does not boot +* [730](https://github.com/mupen64plus/mupen64plus-core/issues/730): Binary PIF ROM support +* [777](https://github.com/mupen64plus/mupen64plus-core/issues/777): Is there any way to add Gameshark Codes? +* [780](https://github.com/mupen64plus/mupen64plus-core/issues/780): Illegal instruction (core dumped) +* [778](https://github.com/mupen64plus/mupen64plus-core/issues/778): Every time I open mupen64plus-ui-console it crashes very quickly. +* [776](https://github.com/mupen64plus/mupen64plus-core/issues/776): mupen64plus-gui DK64 - Severe Slowdown +* [773](https://github.com/mupen64plus/mupen64plus-core/issues/773): Account creation on the wiki (or just answer to my question... 😅) +* [763](https://github.com/mupen64plus/mupen64plus-core/issues/763): Dynamically switch output resolution to that of console +* [760](https://github.com/mupen64plus/mupen64plus-core/issues/760): Fails to build under MSYS Windows 2004 +* [686](https://github.com/mupen64plus/mupen64plus-core/issues/686): New dynarec and F-Zero X Expansion Kit hacked rom +* [599](https://github.com/mupen64plus/mupen64plus-core/issues/599): N64 database errors? +* [722](https://github.com/mupen64plus/mupen64plus-core/issues/722): AppVeyor is broken +* [719](https://github.com/mupen64plus/mupen64plus-core/issues/719): Incorrect Folder Name In Source Code +* [703](https://github.com/mupen64plus/mupen64plus-core/issues/703): Can't build core +* [676](https://github.com/mupen64plus/mupen64plus-core/issues/676): CheckInterrupts is not implemented for Audio/Gfx/RSP plugins +* [674](https://github.com/mupen64plus/mupen64plus-core/issues/674): Mupen32.exe, more like +* [650](https://github.com/mupen64plus/mupen64plus-core/issues/650): Kaillera integration/support +* [689](https://github.com/mupen64plus/mupen64plus-core/issues/689): Aero Fighters Assault - Startup Error +* [749](https://github.com/mupen64plus/mupen64plus-core/issues/749): Missing "mupen64plus.dll" when compiling on windows +* [570](https://github.com/mupen64plus/mupen64plus-core/issues/570): Battle of Naboo: wrong gfx in level selection (core bug) +* [742](https://github.com/mupen64plus/mupen64plus-core/issues/742): Super Mario 64 not responding +* [720](https://github.com/mupen64plus/mupen64plus-core/issues/720): Mupen64Plus Compiled With Visual Studio Solution File Has No Sound +* [609](https://github.com/mupen64plus/mupen64plus-core/issues/609): WCW Backstage Assault locks up +* [669](https://github.com/mupen64plus/mupen64plus-core/issues/669): Test ROM hangs emulator +* [636](https://github.com/mupen64plus/mupen64plus-core/issues/636): Elmo's Letter Adventure - keys are not responsive +* [723](https://github.com/mupen64plus/mupen64plus-core/issues/723): Audio stops in Army Men Sarges Heroes +* [660](https://github.com/mupen64plus/mupen64plus-core/issues/660): Custom roms won't boot +* [711](https://github.com/mupen64plus/mupen64plus-core/issues/711): Memory leak when playing Indiana Jones (musyx_v1/v2_task) +* [713](https://github.com/mupen64plus/mupen64plus-core/issues/713): Adding webhook to Discord64 +* [554](https://github.com/mupen64plus/mupen64plus-core/issues/554): VI_CURRENT_REG not calculated properly if CP0_COUNT_REG changed by software +* [634](https://github.com/mupen64plus/mupen64plus-core/issues/634): screenshots can't be saved correctly when ROM had an invalid header. +* [546](https://github.com/mupen64plus/mupen64plus-core/issues/546): Pokemon Puzzle League FMV not dealt with by RSP-HLE +* [563](https://github.com/mupen64plus/mupen64plus-core/issues/563): Compiling for AARCH64/ARM64? +* [663](https://github.com/mupen64plus/mupen64plus-core/issues/663): VidExt_GL_SetAttribute sets SDL_GL_SetSwapInterval prematurely +* [675](https://github.com/mupen64plus/mupen64plus-core/issues/675): any way to take advantage of GLES 3.0 ES on rpi4? +* [656](https://github.com/mupen64plus/mupen64plus-core/issues/656): Failed to create GL context on RPI3 under mesa with GLideN64 +* [678](https://github.com/mupen64plus/mupen64plus-core/issues/678): segfault while loading World Driver Championship +* [661](https://github.com/mupen64plus/mupen64plus-core/issues/661): Does Mupen64 work with Windows 10 64-Bit? Or Does 64-Bit Windows not work with the 32-Bit? +* [658](https://github.com/mupen64plus/mupen64plus-core/issues/658): SetVideoMode returns NULL pointer to Screen +* [265](https://github.com/mupen64plus/mupen64plus-core/issues/265): Banjo-Kazooie intro puzzle effect not displaying correctly with Rice +* [655](https://github.com/mupen64plus/mupen64plus-core/issues/655): [Glide64] Compilation error on char signedness +* [653](https://github.com/mupen64plus/mupen64plus-core/issues/653): Opening game crashes on Mac +* [648](https://github.com/mupen64plus/mupen64plus-core/issues/648): How compile "mupen64plus-video-z64" on debian 9 +* [645](https://github.com/mupen64plus/mupen64plus-core/issues/645): Retrolinc Controller not found - Arch Linux mupen64plus +* [637](https://github.com/mupen64plus/mupen64plus-core/issues/637): LoS: Majoras Mask unable to move marker to items in inventory. +* [638](https://github.com/mupen64plus/mupen64plus-core/issues/638): Command Line Ends Process After Starting And Request A More Detailed Install Readme File +* [628](https://github.com/mupen64plus/mupen64plus-core/issues/628): 2.5.9: Print of size_t has to use z modifier +* [630](https://github.com/mupen64plus/mupen64plus-core/issues/630): 2.5.9: Missing INPUT_API_VERSION change (2.5 Input plugin not working with 2.5.9 core) + + +## Top Contributors (2 or more commits) +1. Rosalie241 +2. loganmc10 +3. LuigiBlood +4. bsmiles32 +5. Gillou68310 +6. Jj0YzL5nvJ +7. richard42 +8. Pcgaming575 +9. meeq +10. GhostlyDark +11. mpharoah +12. Clownacy +13. obrea +14. m4xw +15. alice-mkh +16. flagrama +17. dankcushions + +--------------------------------------------- +# Mupen64Plus-core v2.5.9 - February 10, 2019 + +## Merged PRs + +* [619](https://github.com/mupen64plus/mupen64plus-core/pull/619): do not force saving config when file not present + (Front-ends now ensure that configuration is saved (when desired) before starting the game.) +* [618](https://github.com/mupen64plus/mupen64plus-core/pull/618): __clear_cache fix for ARM 64 devices plus ANDROID cleanup +* [615](https://github.com/mupen64plus/mupen64plus-core/pull/615): Fixes for last save state update +* [614](https://github.com/mupen64plus/mupen64plus-core/pull/614): Make stop_after_jal a save state parameter instead of configuration. +* [612](https://github.com/mupen64plus/mupen64plus-core/pull/612): Allow valid interpretations of strings as non-strings +* [611](https://github.com/mupen64plus/mupen64plus-core/pull/611): Remove DelaySI from wiki + (This parameter was removed from the core in 0bcfd3cc7.) +* [607](https://github.com/mupen64plus/mupen64plus-core/pull/607): Fix joy_max for initialize loop +* [606](https://github.com/mupen64plus/mupen64plus-core/pull/606): new_dynarec fixes +* [605](https://github.com/mupen64plus/mupen64plus-core/pull/605): Preserve floating-point configuration values. +* [604](https://github.com/mupen64plus/mupen64plus-core/pull/604): Fix mingw build +* [602](https://github.com/mupen64plus/mupen64plus-core/pull/602): new_dynarec: Initial support for x64 and arm64 +* [598](https://github.com/mupen64plus/mupen64plus-core/pull/598): Fix errors introduced in #444 + (These changes allow Rugrats and Xena to boot.) +* [597](https://github.com/mupen64plus/mupen64plus-core/pull/597): Avoid nameless union in r4300_core.h (this is a non standard extension) +* [596](https://github.com/mupen64plus/mupen64plus-core/pull/596): Fix access to DD_ROM memory pointer. +* [593](https://github.com/mupen64plus/mupen64plus-core/pull/593): Two new debugger APIs: 'DebugBreakpointTriggeredBy' and 'DebugVirtualToPhysical' +* [590](https://github.com/mupen64plus/mupen64plus-core/pull/590): Fix missing header in biopak.c +* [589](https://github.com/mupen64plus/mupen64plus-core/pull/589): Cleanings +* [588](https://github.com/mupen64plus/mupen64plus-core/pull/588): added support for aarch64 +* [585](https://github.com/mupen64plus/mupen64plus-core/pull/585): Cleanings +* [583](https://github.com/mupen64plus/mupen64plus-core/pull/583): Update nasm path to use latest stable release 2.13.03 +* [582](https://github.com/mupen64plus/mupen64plus-core/pull/582): Move third-party code into subprojects directory. +* [581](https://github.com/mupen64plus/mupen64plus-core/pull/581): Bugfixes + (Various compilation fixes found while working on meson porting.) +* [579](https://github.com/mupen64plus/mupen64plus-core/pull/579): Make --nosaveoptions not ever save options. +* [578](https://github.com/mupen64plus/mupen64plus-core/pull/578): Update readme +* [573](https://github.com/mupen64plus/mupen64plus-core/pull/573): Rework Video Capture backend to allow better isolation. +* [569](https://github.com/mupen64plus/mupen64plus-core/pull/569): Cleanup tpak savestate loading. +* [566](https://github.com/mupen64plus/mupen64plus-core/pull/566): Initial bio pak support. +* [565](https://github.com/mupen64plus/mupen64plus-core/pull/565): Fix homepage link in dd_controller files. +* [564](https://github.com/mupen64plus/mupen64plus-core/pull/564): GB Camera support +* [561](https://github.com/mupen64plus/mupen64plus-core/pull/561): Update xxHash to 0.6.5 +* [557](https://github.com/mupen64plus/mupen64plus-core/pull/557): Increase DebugMessage buffer size to prevent overflows +* [556](https://github.com/mupen64plus/mupen64plus-core/pull/556): Add VidExt_GL_GetDefaultFramebuffer +* [553](https://github.com/mupen64plus/mupen64plus-core/pull/553): Fix write_memory_8 + (This was overflowing a u8, causing zeroes to be written most of the time instead of the intended value.) +* [550](https://github.com/mupen64plus/mupen64plus-core/pull/550): Don't call SDL_PumpEvents() in core +* [549](https://github.com/mupen64plus/mupen64plus-core/pull/549): Clear CLOCK_REG when requested +* [548](https://github.com/mupen64plus/mupen64plus-core/pull/548): Added missing Joy Mappings + (Added the 4 missing Joystick counterparts of the Keyboard Mappings: Reset, Speed Down, Speed Up, Frame Advance) +* [547](https://github.com/mupen64plus/mupen64plus-core/pull/547): R4300 refactorings +* [545](https://github.com/mupen64plus/mupen64plus-core/pull/545): Fix: try to keep savestates offsets aligned. +* [544](https://github.com/mupen64plus/mupen64plus-core/pull/544): Fix DSLLV in x86 dynarec +* [541](https://github.com/mupen64plus/mupen64plus-core/pull/541): Force alignment of PI DMA +* [538](https://github.com/mupen64plus/mupen64plus-core/pull/538): Alternative implementation of "Don't deal with save states when RSP task is locked" +* [537](https://github.com/mupen64plus/mupen64plus-core/pull/537): new_dynarec: Fix DMULT/DMULTU recompiled code +* [535](https://github.com/mupen64plus/mupen64plus-core/pull/535): Allow game controllers to be unplugged and plugged in on the fly +* [533](https://github.com/mupen64plus/mupen64plus-core/pull/533): Remove CountPerOp=1 for Vigilante games +* [532](https://github.com/mupen64plus/mupen64plus-core/pull/532): If a ROM is not in the database, use 4 players, enable mempak, and enable rumble pak +* [529](https://github.com/mupen64plus/mupen64plus-core/pull/529): Fix compile on MinGW +* [526](https://github.com/mupen64plus/mupen64plus-core/pull/526): Fix undefined reference to print_insn_i386 with libopcode >= 2.29 +* [521](https://github.com/mupen64plus/mupen64plus-core/pull/521): Fix empty dynarec +* [519](https://github.com/mupen64plus/mupen64plus-core/pull/519): Less g dev + (Reduce usage of global variable g_dev (and other derived variables) in emulation modules.) +* [515](https://github.com/mupen64plus/mupen64plus-core/pull/515): Fix compilation of new dynarec. +* [514](https://github.com/mupen64plus/mupen64plus-core/pull/514): Fix potential crash with FBInfo and save states +* [513](https://github.com/mupen64plus/mupen64plus-core/pull/513): Add SiDmaDuration option. +* [510](https://github.com/mupen64plus/mupen64plus-core/pull/510): Cleanings + (This is just some more internal source cleanings such as reindentation, forward declaration, header inclusion or just moving some code around.) +* [507](https://github.com/mupen64plus/mupen64plus-core/pull/507): Update homepage links +* [505](https://github.com/mupen64plus/mupen64plus-core/pull/505): Update xxHash to 0.6.4 +* [503](https://github.com/mupen64plus/mupen64plus-core/pull/503): Device: RDRAM: Use the R4300 helpers to get the R4300 registers +* [502](https://github.com/mupen64plus/mupen64plus-core/pull/502): Bugfixes + (Some small bugfixes found while looking around.) +* [501](https://github.com/mupen64plus/mupen64plus-core/pull/501): Implement enough of RDRAM subsystem to remove rdram detection hack. +* [500](https://github.com/mupen64plus/mupen64plus-core/pull/500): Fix reset bug introduced in commit 1c01c233. +* [499](https://github.com/mupen64plus/mupen64plus-core/pull/499): Fix copy pasting error in MBC2 messages. +* [498](https://github.com/mupen64plus/mupen64plus-core/pull/498): Fix crash when loading save state +* [496](https://github.com/mupen64plus/mupen64plus-core/pull/496): Various GB cart enhancements. +* [495](https://github.com/mupen64plus/mupen64plus-core/pull/495): Create files_macos.c + (First try to get a proper Mupen64Plus app on macOS) +* [494](https://github.com/mupen64plus/mupen64plus-core/pull/494): Update Makefile + (First try to get it fixed for macOS) +* [492](https://github.com/mupen64plus/mupen64plus-core/pull/492): If DEBUG not set, set NDEBUG +* [491](https://github.com/mupen64plus/mupen64plus-core/pull/491): Remove orphaned line related to removed GoldenEye TLB hack + (Needed to resolve build error on Raspberry Pi 3). +* [490](https://github.com/mupen64plus/mupen64plus-core/pull/490): Warnings + (With this PR all warnings except those about unused parameters should be gone. This is tested on GCC with -Wall -Wextra -pedantic.) +* [487](https://github.com/mupen64plus/mupen64plus-core/pull/487): Remove GoldenEye TLB hack. + Fixes https://github.com/mupen64plus/mupen64plus-core/issues/447 +* [485](https://github.com/mupen64plus/mupen64plus-core/pull/485): Modify DPC_STATUS_FREEZE hack +* [483](https://github.com/mupen64plus/mupen64plus-core/pull/483): Extract PIF, RCP and RDRAM modules + (Just moving some files around to isolate PIF and RCP modules.) +* [481](https://github.com/mupen64plus/mupen64plus-core/pull/481): Add more FB writes and reads +* [480](https://github.com/mupen64plus/mupen64plus-core/pull/480): Fix 40 winks ini +* [478](https://github.com/mupen64plus/mupen64plus-core/pull/478): Fix FbInfo 2 +* [476](https://github.com/mupen64plus/mupen64plus-core/pull/476): Preliminary refactorings + (This PR is just a collection of preliminary refactoring commits extracted from PR #446. They are not directly related to 64DD so we can incorporate them hopefully sooner.) +* [475](https://github.com/mupen64plus/mupen64plus-core/pull/475): Fix handlers[i].{read,write}32 comparison in dynarec. +* [474](https://github.com/mupen64plus/mupen64plus-core/pull/474): Big alloc fallback + (Another try at the memory allocation failure encountered in some Android devices.) +* [473](https://github.com/mupen64plus/mupen64plus-core/pull/473): CountPerOp=1 for Bokujou Monogatari 2 +* [472](https://github.com/mupen64plus/mupen64plus-core/pull/472): If an invalid controller pak is selected, use NONE instead +* [471](https://github.com/mupen64plus/mupen64plus-core/pull/471): Fix LWL, LWR, LDL, LDR, SWL, SWR, SDL, SDR implementations +* [470](https://github.com/mupen64plus/mupen64plus-core/pull/470): Build MXE targets as additional tests in travis build matrix +* [466](https://github.com/mupen64plus/mupen64plus-core/pull/466): CountPerOp=1 for Densha de Go! 64 +* [465](https://github.com/mupen64plus/mupen64plus-core/pull/465): new_dynarec: Fix Dance Dance revolution +* [464](https://github.com/mupen64plus/mupen64plus-core/pull/464): Increase Delay DP Interrupt time +* [461](https://github.com/mupen64plus/mupen64plus-core/pull/461): Get rid of USE_SDL flag +* [460](https://github.com/mupen64plus/mupen64plus-core/pull/460): Add support for different flashram types. +* [459](https://github.com/mupen64plus/mupen64plus-core/pull/459): Fix reset +* [457](https://github.com/mupen64plus/mupen64plus-core/pull/457): Add support for SRAM cpu read/write +* [455](https://github.com/mupen64plus/mupen64plus-core/pull/455): Complete (U) save types + Add four games +* [454](https://github.com/mupen64plus/mupen64plus-core/pull/454): CountPerOp=1 for Gauntlet Legends + (Allows for better performance when using GLideN64.) +* [453](https://github.com/mupen64plus/mupen64plus-core/pull/453): Do SI DMA in 2 stages. + Fixes https://github.com/mupen64plus/mupen64plus-core/issues/452 +* [451](https://github.com/mupen64plus/mupen64plus-core/pull/451): Enable CountPerOp=1 for Battletanx 2 & Army Men +* [450](https://github.com/mupen64plus/mupen64plus-core/pull/450): Fix issues listed in #385 +* [449](https://github.com/mupen64plus/mupen64plus-core/pull/449): Allow switching controller paks using CONTROLS.plugin API +* [448](https://github.com/mupen64plus/mupen64plus-core/pull/448): Fix some Save Type entries +* [446](https://github.com/mupen64plus/mupen64plus-core/pull/446): [WIP] Add support for 64 Disk Drive. +* [444](https://github.com/mupen64plus/mupen64plus-core/pull/444): Re-work the way VI interrupts are timed +* [443](https://github.com/mupen64plus/mupen64plus-core/pull/443): Remove CountPerOp=1 for Ep I Racer +* [440](https://github.com/mupen64plus/mupen64plus-core/pull/440): Mempak/Rumble fixes +* [439](https://github.com/mupen64plus/mupen64plus-core/pull/439): Fix crash when loading savestate. +* [437](https://github.com/mupen64plus/mupen64plus-core/pull/437): ROM database update +* [436](https://github.com/mupen64plus/mupen64plus-core/pull/436): PI refactorings +* [430](https://github.com/mupen64plus/mupen64plus-core/pull/430): Fix a few indentation warnings +* [428](https://github.com/mupen64plus/mupen64plus-core/pull/428): Raspberry Pi: use new vendor library names +* [427](https://github.com/mupen64plus/mupen64plus-core/pull/427): Do a proper open bus read. + Fixes https://github.com/mupen64plus/mupen64plus-core/issues/418 +* [425](https://github.com/mupen64plus/mupen64plus-core/pull/425): Add note about SP_STATUS_REG +* [424](https://github.com/mupen64plus/mupen64plus-core/pull/424): SDL_Delay is only called just before input polling. +* [423](https://github.com/mupen64plus/mupen64plus-core/pull/423): Add SP_STATUS_REG and RDRAM_SIZE to gfx_info +* [422](https://github.com/mupen64plus/mupen64plus-core/pull/422): Stubbing some 64dd conversion roms into database. +* [421](https://github.com/mupen64plus/mupen64plus-core/pull/421): CIC and Densha de Go Translation Patch +* [420](https://github.com/mupen64plus/mupen64plus-core/pull/420): Don't use IO_BUSY flags for DMA +* [417](https://github.com/mupen64plus/mupen64plus-core/pull/417): Fix rounding in interpreter +* [415](https://github.com/mupen64plus/mupen64plus-core/pull/415): Randomize PI/SI interrupt timing +* [411](https://github.com/mupen64plus/mupen64plus-core/pull/411): More si refactorings +* [410](https://github.com/mupen64plus/mupen64plus-core/pull/410): Fix slowdowns in Mischief Makers +* [407](https://github.com/mupen64plus/mupen64plus-core/pull/407): Add AppVeyor config +* [406](https://github.com/mupen64plus/mupen64plus-core/pull/406): Bump savestate format to 1.2 +* [404](https://github.com/mupen64plus/mupen64plus-core/pull/404): Add entry for 007 - Goldfinger +* [403](https://github.com/mupen64plus/mupen64plus-core/pull/403): Fix save size for Custom Robo V2. + Fixes https://github.com/mupen64plus/mupen64plus-core/issues/402 +* [401](https://github.com/mupen64plus/mupen64plus-core/pull/401): Add travisCI and coverity badges +* [399](https://github.com/mupen64plus/mupen64plus-core/pull/399): Fix screenshots for games with colon in the title. + Fix for https://github.com/mupen64plus/mupen64plus-core/issues/398 +* [394](https://github.com/mupen64plus/mupen64plus-core/pull/394): CountPerOp=1 for Yakouchuu II - Satsujin Kouro +* [393](https://github.com/mupen64plus/mupen64plus-core/pull/393): Fix description of some core config variables +* [390](https://github.com/mupen64plus/mupen64plus-core/pull/390): Fix set_fpr_pointers +* [388](https://github.com/mupen64plus/mupen64plus-core/pull/388): Remove DelaySI hack/option +* [387](https://github.com/mupen64plus/mupen64plus-core/pull/387): Remove Rat Attack hack for new_dynarec +* [386](https://github.com/mupen64plus/mupen64plus-core/pull/386): Switch from adler32 to xxHash +* [382](https://github.com/mupen64plus/mupen64plus-core/pull/382): Use faster container based Travis CI +* [381](https://github.com/mupen64plus/mupen64plus-core/pull/381): Let travis handle the package installation directly +* [375](https://github.com/mupen64plus/mupen64plus-core/pull/375): Fix duplicate saving in Paper Mario. + This fixes https://github.com/mupen64plus/mupen64plus-core/issues/154 +* [373](https://github.com/mupen64plus/mupen64plus-core/pull/373): Remove Rat Attack hack +* [371](https://github.com/mupen64plus/mupen64plus-core/pull/371): Have emulator signal RSP interrupts again +* [370](https://github.com/mupen64plus/mupen64plus-core/pull/370): Base count_per_scanline on N64 clockrate +* [367](https://github.com/mupen64plus/mupen64plus-core/pull/367): Fixing some warnings +* [365](https://github.com/mupen64plus/mupen64plus-core/pull/365): Fix order of operations for AI interrupt timing +* [364](https://github.com/mupen64plus/mupen64plus-core/pull/364): Get x86 new dynarec compiling again (when using PIC) +* [363](https://github.com/mupen64plus/mupen64plus-core/pull/363): Round PI_WR_LEN_REG up to nearest even number +* [362](https://github.com/mupen64plus/mupen64plus-core/pull/362): Add setting for Mischief Makers J +* [360](https://github.com/mupen64plus/mupen64plus-core/pull/360): Rewrite PIF emulation. +* [358](https://github.com/mupen64plus/mupen64plus-core/pull/358): Fix RevA (U) Rogue Squadron +* [356](https://github.com/mupen64plus/mupen64plus-core/pull/356): Get rid of "alternate VI timing" +* [355](https://github.com/mupen64plus/mupen64plus-core/pull/355): Change tpak/gb debug message level to verbose. +* [352](https://github.com/mupen64plus/mupen64plus-core/pull/352): CountPerOp=1 for Factor5 games +* [351](https://github.com/mupen64plus/mupen64plus-core/pull/351): Add SI_STATUS_DMA_BUSY. + This fixes the error: + ``` + Core Warning: two events of type 0x8 in interrupt queue + ``` + in some games. +* [343](https://github.com/mupen64plus/mupen64plus-core/pull/343): Send audio data to plugin as the DMA drains +* [339](https://github.com/mupen64plus/mupen64plus-core/pull/339): Use count_per_op instead of 2 +* [336](https://github.com/mupen64plus/mupen64plus-core/pull/336): Ignore TLB write if TLB entry is unmapping itself. + Fixes https://github.com/mupen64plus/mupen64plus-core/issues/221 +* [334](https://github.com/mupen64plus/mupen64plus-core/pull/334): Fix write_cart_rom/read_cart_rom. + This fixes https://github.com/mupen64plus/mupen64plus-core/issues/311 (International Track & Field 2000 and Midway's Greatest Arcade Hits Vol. 1) +* [333](https://github.com/mupen64plus/mupen64plus-core/pull/333): Multiplayer timing fix for Mario Kart E and J +* [330](https://github.com/mupen64plus/mupen64plus-core/pull/330): Add hack for Rat Attack! +* [329](https://github.com/mupen64plus/mupen64plus-core/pull/329): Print accelerator when GB cart supports it. +* [327](https://github.com/mupen64plus/mupen64plus-core/pull/327): Implement "Audio Signal" +* [326](https://github.com/mupen64plus/mupen64plus-core/pull/326): Updates to DisableExtraMem +* [324](https://github.com/mupen64plus/mupen64plus-core/pull/324): Add CP0_ERROREPC_REG to MTC0. + Fixes https://github.com/mupen64plus/mupen64plus-core/issues/323 +* [322](https://github.com/mupen64plus/mupen64plus-core/pull/322): Fix asm_defines w/ mawk by setting LANG=C +* [320](https://github.com/mupen64plus/mupen64plus-core/pull/320): Revert "Allow using compiler other than gcc" +* [318](https://github.com/mupen64plus/mupen64plus-core/pull/318): Fix random resets in South Park 64. + https://github.com/mupen64plus/mupen64plus-core/issues/316 +* [317](https://github.com/mupen64plus/mupen64plus-core/pull/317): Allow using compiler other than gcc +* [313](https://github.com/mupen64plus/mupen64plus-core/pull/313): Memory refactorings +* [312](https://github.com/mupen64plus/mupen64plus-core/pull/312): Remove memd +* [310](https://github.com/mupen64plus/mupen64plus-core/pull/310): Fix reading CP0_RANDOM_REG. + Fixes https://github.com/mupen64plus/mupen64plus-core/issues/309 +* [308](https://github.com/mupen64plus/mupen64plus-core/pull/308): Add note for NBA Showtime - NBA on NBC +* [307](https://github.com/mupen64plus/mupen64plus-core/pull/307): Trust plugins to trigger SP interrupts +* [306](https://github.com/mupen64plus/mupen64plus-core/pull/306): INI cheat cleanup +* [301](https://github.com/mupen64plus/mupen64plus-core/pull/301): Avoid usage of global variable g_delay_si. + (One less global variable used inside the emulation core.) +* [300](https://github.com/mupen64plus/mupen64plus-core/pull/300): CountPerScanline=1600 for Twisted Edge +* [298](https://github.com/mupen64plus/mupen64plus-core/pull/298): Various Mac-related build fixes +* [297](https://github.com/mupen64plus/mupen64plus-core/pull/297): Fix M64P_GL_SWAP_CONTROL with SDL>=1.3 and no video extension +* [296](https://github.com/mupen64plus/mupen64plus-core/pull/296): Fix undefined behavior in the interpreter +* [295](https://github.com/mupen64plus/mupen64plus-core/pull/295): [WIP] Pass pointer to fcr31 around in fpu.h functions to avoid usage of g_dev. +* [293](https://github.com/mupen64plus/mupen64plus-core/pull/293): Revert "Pass pointer to fcr31 around in fpu.h functions to avoid usage of g_dev." +* [290](https://github.com/mupen64plus/mupen64plus-core/pull/290): Fix audio in RE2 +* [289](https://github.com/mupen64plus/mupen64plus-core/pull/289): Fix Twisted Edge audio. + Fixes #285 +* [282](https://github.com/mupen64plus/mupen64plus-core/pull/282): No g dev in interrupt + (Remove usage of g_dev in interrupt.) +* [281](https://github.com/mupen64plus/mupen64plus-core/pull/281): Pass pointer to fcr31 around in fpu.h functions to avoid usage of g_dev. +* [280](https://github.com/mupen64plus/mupen64plus-core/pull/280): Remove MK64 cheat for E and J +* [278](https://github.com/mupen64plus/mupen64plus-core/pull/278): Avoid usage of g_dev in rdram_deteciton_hack. +* [275](https://github.com/mupen64plus/mupen64plus-core/pull/275): README.md URL fix +* [272](https://github.com/mupen64plus/mupen64plus-core/pull/272): Fix audio in Star Wars Episode I - Racer +* [271](https://github.com/mupen64plus/mupen64plus-core/pull/271): DK64 Bone Displacement fix +* [270](https://github.com/mupen64plus/mupen64plus-core/pull/270): Remove custom CountPerScanline values +* [269](https://github.com/mupen64plus/mupen64plus-core/pull/269): Remove byte and hword accessors +* [263](https://github.com/mupen64plus/mupen64plus-core/pull/263): Fix Indiana Jones +* [258](https://github.com/mupen64plus/mupen64plus-core/pull/258): Reduce usage of g_dev +* [257](https://github.com/mupen64plus/mupen64plus-core/pull/257): Get World Driver Championship booting +* [256](https://github.com/mupen64plus/mupen64plus-core/pull/256): Mips instructions refactorings + (This is just some internal reoganization of mips instructions functions.) +* [255](https://github.com/mupen64plus/mupen64plus-core/pull/255): new_dynarec: Fix crash during shutdown +* [253](https://github.com/mupen64plus/mupen64plus-core/pull/253): Restore old behavior when allocating memory for RDRAM +* [250](https://github.com/mupen64plus/mupen64plus-core/pull/250): Don't reset the speed limiter when falling behind +* [246](https://github.com/mupen64plus/mupen64plus-core/pull/246): CountPerOp=3 for CBFD +* [243](https://github.com/mupen64plus/mupen64plus-core/pull/243): Fix compilation in msvc as per Gillou's comment. +* [242](https://github.com/mupen64plus/mupen64plus-core/pull/242): Fix excessive warnings when compiling using clang. +* [241](https://github.com/mupen64plus/mupen64plus-core/pull/241): Minor refac + (Some minor refactorings to remove some magic constants and rework the si peripherals status command handling.) +* [240](https://github.com/mupen64plus/mupen64plus-core/pull/240): Avoid g_dev in r4300 code + (Small PR which avoids usage of the g_dev global variable inside TLB code.) +* [239](https://github.com/mupen64plus/mupen64plus-core/pull/239): Don't use SDL directly +* [238](https://github.com/mupen64plus/mupen64plus-core/pull/238): Dyna start +* [237](https://github.com/mupen64plus/mupen64plus-core/pull/237): Add ConfigExternalGetParameter +* [235](https://github.com/mupen64plus/mupen64plus-core/pull/235): new_dynarec: Code cleanup +* [233](https://github.com/mupen64plus/mupen64plus-core/pull/233): fix include paths / fixes building on arm/neon +* [232](https://github.com/mupen64plus/mupen64plus-core/pull/232): Disable OSD if OpenGL core context is in use +* [227](https://github.com/mupen64plus/mupen64plus-core/pull/227): Device refactorings +* [220](https://github.com/mupen64plus/mupen64plus-core/pull/220): Fix Kirby 64 save size +* [216](https://github.com/mupen64plus/mupen64plus-core/pull/216): Fix new dynarec interrupt management + (This also fixes soft reset.) +* [215](https://github.com/mupen64plus/mupen64plus-core/pull/215): Fix assertion failure when starting Kuiki Uhabi Suigo +* [213](https://github.com/mupen64plus/mupen64plus-core/pull/213): No globals +* [212](https://github.com/mupen64plus/mupen64plus-core/pull/212): Allow CountPerScan and ViTiming in ROM database to be overridden +* [211](https://github.com/mupen64plus/mupen64plus-core/pull/211): Avoid processing CIC response pif ram as commands +* [208](https://github.com/mupen64plus/mupen64plus-core/pull/208): Various refactorings +* [205](https://github.com/mupen64plus/mupen64plus-core/pull/205): Clean magic constants +* [204](https://github.com/mupen64plus/mupen64plus-core/pull/204): Use custom ViRefresh for some games +* [203](https://github.com/mupen64plus/mupen64plus-core/pull/203): Fix mario kart multiplayer timings +* [202](https://github.com/mupen64plus/mupen64plus-core/pull/202): Make SDL usage optional in some files +* [199](https://github.com/mupen64plus/mupen64plus-core/pull/199): Add using_tlb flag to savestates +* [198](https://github.com/mupen64plus/mupen64plus-core/pull/198): Allow setting a loadstate job before emulator is running. +* [196](https://github.com/mupen64plus/mupen64plus-core/pull/196): Fix Pokemon Puzzle League +* [192](https://github.com/mupen64plus/mupen64plus-core/pull/192): new_dynarec: Allocate memory for original MIPS source code of a recompiled block before do_dirty_stub_ds is being called. +* [182](https://github.com/mupen64plus/mupen64plus-core/pull/182): New dynarec fix +* [180](https://github.com/mupen64plus/mupen64plus-core/pull/180): Use Ubuntu Trusty as base system for Travis CI +* [168](https://github.com/mupen64plus/mupen64plus-core/pull/168): Documentation Update and Formatting +* [155](https://github.com/mupen64plus/mupen64plus-core/pull/155): Fix speed limiter issue +* [150](https://github.com/mupen64plus/mupen64plus-core/pull/150): Improve speed limiter accuracy +* [144](https://github.com/mupen64plus/mupen64plus-core/pull/144): Framelimiter: Discard frames with excessive execution time +* [143](https://github.com/mupen64plus/mupen64plus-core/pull/143): Fixed gfx moveScreen when SDL_VERSION > 1.2 +* [140](https://github.com/mupen64plus/mupen64plus-core/pull/140): Call gfx moveScreen when SDL window moved (Windows only) +* [138](https://github.com/mupen64plus/mupen64plus-core/pull/138): new_dynarec: Reverted printing method when warning about a block compiled at bogus memory address +* [137](https://github.com/mupen64plus/mupen64plus-core/pull/137): Dynamic linker +* [135](https://github.com/mupen64plus/mupen64plus-core/pull/135): new_dynarec: Fixed bomberman 64 +* [133](https://github.com/mupen64plus/mupen64plus-core/pull/133): new_dynarec: Fixed verifier pointer when using trampoline jump +* [132](https://github.com/mupen64plus/mupen64plus-core/pull/132): new_dynarec: Avoid replacing an allocated temporary register. + Fixed crash in castlevania +* [131](https://github.com/mupen64plus/mupen64plus-core/pull/131): Allow building the old dynarec on x64 with VisualStudio2013 +* [128](https://github.com/mupen64plus/mupen64plus-core/pull/128): Rename new_dynarec.txt to new_dynarec.mediawiki +* [125](https://github.com/mupen64plus/mupen64plus-core/pull/125): Remove Zelda OoT Subscreen Delay Fix from mupen64plus.ini +* [124](https://github.com/mupen64plus/mupen64plus-core/pull/124): new_dynarec: Implemented recompiled DMULT instruction on x86 + (This also fix DK going through wall issue on x86) +* [123](https://github.com/mupen64plus/mupen64plus-core/pull/123): Merge back "Avoid .text relocations in PIC shared library" fix + cleanup linkage_arm.S +* [122](https://github.com/mupen64plus/mupen64plus-core/pull/122): Remove unnecessary OpenBSD warning. +* [120](https://github.com/mupen64plus/mupen64plus-core/pull/120): Avoid .text relocations in PIC shared library +* [117](https://github.com/mupen64plus/mupen64plus-core/pull/117): Squelch sscanf warning in arm_cpu_features. +* [98](https://github.com/mupen64plus/mupen64plus-core/pull/98): [WIP/RFC] Transfer Pak emulation support + + +## Closed Issues + +* [245](https://github.com/mupen64plus/mupen64plus-core/issues/245): Mario Kart 64 item not usable bug +* [350](https://github.com/mupen64plus/mupen64plus-core/issues/350): RSP DMA double buffering not implemented +* [438](https://github.com/mupen64plus/mupen64plus-core/issues/438): Mario 64 Star Revenge New dynarec game freezes +* [616](https://github.com/mupen64plus/mupen64plus-core/issues/616): Please add 4K access to resolution in Android. +* [552](https://github.com/mupen64plus/mupen64plus-core/issues/552): Tonic trouble (u) new dynarec crash +* [338](https://github.com/mupen64plus/mupen64plus-core/issues/338): Turok 3 freezes after intro +* [304](https://github.com/mupen64plus/mupen64plus-core/issues/304): Pokemon Puzzle League New Dynarec assert +* [613](https://github.com/mupen64plus/mupen64plus-core/issues/613): Selecting a save state slot sends M64CORE_SAVESTATE_SLOT command twice +* [558](https://github.com/mupen64plus/mupen64plus-core/issues/558): Donkey Kong 64 back to level beginning on vines +* [587](https://github.com/mupen64plus/mupen64plus-core/issues/587): New dynarec CBFD is freezing +* [504](https://github.com/mupen64plus/mupen64plus-core/issues/504): No 64 bit ARM library support with new dynarec +* [603](https://github.com/mupen64plus/mupen64plus-core/issues/603): New dynarec not working on x64 +* [540](https://github.com/mupen64plus/mupen64plus-core/issues/540): Translucent textures in Zelda OOT +* [584](https://github.com/mupen64plus/mupen64plus-core/issues/584): Regression caused by #444 +* [601](https://github.com/mupen64plus/mupen64plus-core/issues/601): Combined joystick actions not working on Windows 10 +* [592](https://github.com/mupen64plus/mupen64plus-core/issues/592): Support for non-SDL mouse injection +* [595](https://github.com/mupen64plus/mupen64plus-core/issues/595): Debugger is broken +* [591](https://github.com/mupen64plus/mupen64plus-core/issues/591): Troubles extracting rom MD5 from save state +* [580](https://github.com/mupen64plus/mupen64plus-core/issues/580): SM64 Last Impact: Game Crashes When jumping inside basket or entering warpipe in hub world +* [539](https://github.com/mupen64plus/mupen64plus-core/issues/539): Is not longer possible to play using the keyboard +* [568](https://github.com/mupen64plus/mupen64plus-core/issues/568): Is "frame advance" working? +* [567](https://github.com/mupen64plus/mupen64plus-core/issues/567): Unable to enter and edit config +* [555](https://github.com/mupen64plus/mupen64plus-core/issues/555): Audio is not working in Windows +* [560](https://github.com/mupen64plus/mupen64plus-core/issues/560): D-pad on iOS +* [536](https://github.com/mupen64plus/mupen64plus-core/issues/536): Bus error when loading savestate on odroid c2 (armhf) +* [261](https://github.com/mupen64plus/mupen64plus-core/issues/261): ARM New dynarec crash +* [522](https://github.com/mupen64plus/mupen64plus-core/issues/522): Building with LTO enabled makes loading save states crash +* [543](https://github.com/mupen64plus/mupen64plus-core/issues/543): Speed limiter is slowing down way too much +* [542](https://github.com/mupen64plus/mupen64plus-core/issues/542): Indiana Jones have bugs in dynarec +* [531](https://github.com/mupen64plus/mupen64plus-core/issues/531): Unplugging controllers using CONTROL_INFO doesn't work +* [528](https://github.com/mupen64plus/mupen64plus-core/issues/528): Log spam when loading save states: Unexpected command format 01 c4 01 +* [222](https://github.com/mupen64plus/mupen64plus-core/issues/222): World Driver Championship | Video Error: Error: Unsupported uCode! crc: 844b55b5 +* [530](https://github.com/mupen64plus/mupen64plus-core/issues/530): Major League Baseball with Ken Griffey Jr. (Messed Up Splash Screens and Start Screen) +* [181](https://github.com/mupen64plus/mupen64plus-core/issues/181): Top Gear Really 2 freezes +* [294](https://github.com/mupen64plus/mupen64plus-core/issues/294): Save states are locking up the emulator +* [321](https://github.com/mupen64plus/mupen64plus-core/issues/321): Crash report +* [527](https://github.com/mupen64plus/mupen64plus-core/issues/527): GLP license text has outdated address +* [484](https://github.com/mupen64plus/mupen64plus-core/issues/484): mupen64plus save/screenshot/config folder change on macOS +* [512](https://github.com/mupen64plus/mupen64plus-core/issues/512): Tetris 64 (J) freezes on boot. +* [489](https://github.com/mupen64plus/mupen64plus-core/issues/489): Reported crashes +* [497](https://github.com/mupen64plus/mupen64plus-core/issues/497): Crash when loading save state +* [344](https://github.com/mupen64plus/mupen64plus-core/issues/344): Possibility of idle loop detection on cached interpreter. +* [447](https://github.com/mupen64plus/mupen64plus-core/issues/447): Removing the GoldenEye TLB hack (Pure Interpreter) +* [486](https://github.com/mupen64plus/mupen64plus-core/issues/486): International super star soccer 2000 keeps stalling +* [482](https://github.com/mupen64plus/mupen64plus-core/issues/482): Compiler log seldomness +* [479](https://github.com/mupen64plus/mupen64plus-core/issues/479): MacOS makefile changes +* [463](https://github.com/mupen64plus/mupen64plus-core/issues/463): Audio cracks in Banjo Kazooie +* [458](https://github.com/mupen64plus/mupen64plus-core/issues/458): FlashRam saving fails in Derby Stallion 64 +* [467](https://github.com/mupen64plus/mupen64plus-core/issues/467): Some devices are failing CoreStartup with M64ERR_NO_MEMORY +* [445](https://github.com/mupen64plus/mupen64plus-core/issues/445): Can't change pak type after game has started using CONTROL.plugin API +* [469](https://github.com/mupen64plus/mupen64plus-core/issues/469): Hey You Pikachu corrupt graphics +* [462](https://github.com/mupen64plus/mupen64plus-core/issues/462): Ide Yousuke no Mahjong Juku - black screen +* [456](https://github.com/mupen64plus/mupen64plus-core/issues/456): Waialae Country Club won't save +* [452](https://github.com/mupen64plus/mupen64plus-core/issues/452): Turok: Dinosaur Hunter not responding to inputs. +* [385](https://github.com/mupen64plus/mupen64plus-core/issues/385): Bomberman 64: black screen using arm/x86 new dynarec +* [283](https://github.com/mupen64plus/mupen64plus-core/issues/283): BattleTanx - Global Assault (U) - Game restarts +* [442](https://github.com/mupen64plus/mupen64plus-core/issues/442): No longer supporting Raspberry Pi? +* [434](https://github.com/mupen64plus/mupen64plus-core/issues/434): Should mupen64plus switch to big-endian format for raw saves? +* [432](https://github.com/mupen64plus/mupen64plus-core/issues/432): Bomberman 64 is freezing as of latest +* [426](https://github.com/mupen64plus/mupen64plus-core/issues/426): x86 new dynarec games are freezing +* [429](https://github.com/mupen64plus/mupen64plus-core/issues/429): UI-Console Error: LoadLibrary('mupen64plus.dll') error: The specified module could not be found. +* [418](https://github.com/mupen64plus/mupen64plus-core/issues/418): Castlevania gets stuck in the intro demo +* [145](https://github.com/mupen64plus/mupen64plus-core/issues/145): Donkey Kong 64 - Bone Displacement +* [288](https://github.com/mupen64plus/mupen64plus-core/issues/288): Indiana Jones x86 new dynarec crash +* [274](https://github.com/mupen64plus/mupen64plus-core/issues/274): New Dynarec: WDC not working +* [416](https://github.com/mupen64plus/mupen64plus-core/issues/416): Interpreter fails CP1ROUND test +* [384](https://github.com/mupen64plus/mupen64plus-core/issues/384): Better COUNT emulation idea/enhancement +* [405](https://github.com/mupen64plus/mupen64plus-core/issues/405): PIF rewrite broke loading save states at startup +* [409](https://github.com/mupen64plus/mupen64plus-core/issues/409): Mischief Makers - Framerate halved +* [402](https://github.com/mupen64plus/mupen64plus-core/issues/402): Custom Robo V2 freeze +* [374](https://github.com/mupen64plus/mupen64plus-core/issues/374): Save paths not correct. +* [398](https://github.com/mupen64plus/mupen64plus-core/issues/398): Vigilante 8: Second Offense Screenshots/Save States don't work. +* [349](https://github.com/mupen64plus/mupen64plus-core/issues/349): Crash GLXBadFBConfig +* [315](https://github.com/mupen64plus/mupen64plus-core/issues/315): SM64 won't launch (several issues I believe) +* [392](https://github.com/mupen64plus/mupen64plus-core/issues/392): Yakouchuu II - Satsujin Kouro does not go to menu +* [154](https://github.com/mupen64plus/mupen64plus-core/issues/154): Paper Mario (U) flash written incorrectly +* [389](https://github.com/mupen64plus/mupen64plus-core/issues/389): OSD=0 causes game to hang after one has been played +* [391](https://github.com/mupen64plus/mupen64plus-core/issues/391): Hydro thunder broke with recent commits +* [127](https://github.com/mupen64plus/mupen64plus-core/issues/127): Several Rare games locking up while using LLE video plugins +* [379](https://github.com/mupen64plus/mupen64plus-core/issues/379): Perfect Dark (Europe) is hanging/freezing at the Perfect Dark logo +* [130](https://github.com/mupen64plus/mupen64plus-core/issues/130): Remove processDList() from code and API +* [335](https://github.com/mupen64plus/mupen64plus-core/issues/335): Compressed ROM support +* [303](https://github.com/mupen64plus/mupen64plus-core/issues/303): No RPS available in Mupen64 0.51 +* [377](https://github.com/mupen64plus/mupen64plus-core/issues/377): Travis CI broken +* [314](https://github.com/mupen64plus/mupen64plus-core/issues/314): New Dynarec: NFL Quarterback Club 2001 corrupt graphics +* [186](https://github.com/mupen64plus/mupen64plus-core/issues/186): Perfect Dark freezes occasionally when attempting to start next level +* [346](https://github.com/mupen64plus/mupen64plus-core/issues/346): Anisotropic Filtering Missing +* [200](https://github.com/mupen64plus/mupen64plus-core/issues/200): Top Gear Rally bad audio +* [316](https://github.com/mupen64plus/mupen64plus-core/issues/316): South park restarts (x86 fix not PIC complaint) +* [366](https://github.com/mupen64plus/mupen64plus-core/issues/366): Doom 64 Mempak saving issue +* [354](https://github.com/mupen64plus/mupen64plus-core/issues/354): [Website] HTTPS does not work +* [345](https://github.com/mupen64plus/mupen64plus-core/issues/345): XbrZ is Broken +* [221](https://github.com/mupen64plus/mupen64plus-core/issues/221): All Star Baseball 2001 not starting (TLB Exception) +* [311](https://github.com/mupen64plus/mupen64plus-core/issues/311): International Track & Field 2000 won't start (TLB Exception) +* [340](https://github.com/mupen64plus/mupen64plus-core/issues/340): Unable to map C buttons +* [331](https://github.com/mupen64plus/mupen64plus-core/issues/331): Indiana Jones freeze at end of level +* [319](https://github.com/mupen64plus/mupen64plus-core/issues/319): Issue with compile-time generation of asm defines +* [328](https://github.com/mupen64plus/mupen64plus-core/issues/328): x86 new dynarec crashing +* [325](https://github.com/mupen64plus/mupen64plus-core/issues/325): DisableExtraMem no longer working? +* [323](https://github.com/mupen64plus/mupen64plus-core/issues/323): NHL Breakaway 98 won't start +* [309](https://github.com/mupen64plus/mupen64plus-core/issues/309): Space Invaders won't start +* [87](https://github.com/mupen64plus/mupen64plus-core/issues/87): Signed integer overflow in core interpreter +* [217](https://github.com/mupen64plus/mupen64plus-core/issues/217): Add custom ini support +* [284](https://github.com/mupen64plus/mupen64plus-core/issues/284): CBFD freezes when entering other stages using a saved state +* [299](https://github.com/mupen64plus/mupen64plus-core/issues/299): RE2 video distorted on ARM dynarec and x64 Windows dynarec +* [287](https://github.com/mupen64plus/mupen64plus-core/issues/287): Banjo tooie crash after latest refactors +* [219](https://github.com/mupen64plus/mupen64plus-core/issues/219): RSP HLE Crash +* [268](https://github.com/mupen64plus/mupen64plus-core/issues/268): new dynarec ARM crash +* [291](https://github.com/mupen64plus/mupen64plus-core/issues/291): macho64 output format not recognized on El Capitan +* [285](https://github.com/mupen64plus/mupen64plus-core/issues/285): Twisted Edge Extreme Snowboarding no audio +* [266](https://github.com/mupen64plus/mupen64plus-core/issues/266): Constant audio buffer underflows and GFX flickering in Pokemon Puzzle League FMV's +* [267](https://github.com/mupen64plus/mupen64plus-core/issues/267): Question: RSP specific instructions and their interpretation +* [226](https://github.com/mupen64plus/mupen64plus-core/issues/226): Input Lag with GFX plugins that have framebuffer emulation (Mario 64) +* [262](https://github.com/mupen64plus/mupen64plus-core/issues/262): Pilot Wings 64 Shadows Graphics Glitch +* [254](https://github.com/mupen64plus/mupen64plus-core/issues/254): Mupen 0.5 Save States (Discussion) +* [251](https://github.com/mupen64plus/mupen64plus-core/issues/251): Compilation fix for SDL2 from homebrew on Mac for plugins +* [247](https://github.com/mupen64plus/mupen64plus-core/issues/247): How to run in Fedora? +* [158](https://github.com/mupen64plus/mupen64plus-core/issues/158): Crash during Mario Party (Coin Block Bash) +* [248](https://github.com/mupen64plus/mupen64plus-core/issues/248): Missing textures and incorrect placement in Star Wars: Shadows of the Empire +* [244](https://github.com/mupen64plus/mupen64plus-core/issues/244): Saving sate cause retroarch to crash +* [193](https://github.com/mupen64plus/mupen64plus-core/issues/193): OSD should support OpenGL core profile +* [195](https://github.com/mupen64plus/mupen64plus-core/issues/195): arm new dynarec doesn't build with clang +* [234](https://github.com/mupen64plus/mupen64plus-core/issues/234): Segfault when closing games +* [225](https://github.com/mupen64plus/mupen64plus-core/issues/225): Ability to set custom RDRAM size +* [230](https://github.com/mupen64plus/mupen64plus-core/issues/230): fails to build since 5aebce38204de4fe3671f78e3cee66838f9a1d87 +* [139](https://github.com/mupen64plus/mupen64plus-core/issues/139): Configurable VI Refresh Rate +* [187](https://github.com/mupen64plus/mupen64plus-core/issues/187): Gamepads are comfortable +* [194](https://github.com/mupen64plus/mupen64plus-core/issues/194): Compiling to custom paths +* [191](https://github.com/mupen64plus/mupen64plus-core/issues/191): Wonder Project J2: Cutoff / misaligned textures +* [183](https://github.com/mupen64plus/mupen64plus-core/issues/183): Building on Windows +* [179](https://github.com/mupen64plus/mupen64plus-core/issues/179): NBA live 2000 crash +* [170](https://github.com/mupen64plus/mupen64plus-core/issues/170): Banjo-Tooie crash +* [160](https://github.com/mupen64plus/mupen64plus-core/issues/160): Vidext SDL2 compatibility overrides context profile mask, making attribute useless +* [119](https://github.com/mupen64plus/mupen64plus-core/issues/119): ARM build creates broken PIC with TEXTREL +* [172](https://github.com/mupen64plus/mupen64plus-core/issues/172): Screenshots broken on raspberry pi +* [166](https://github.com/mupen64plus/mupen64plus-core/issues/166): glide64mk2 requires libboost 1.46.1 +* [173](https://github.com/mupen64plus/mupen64plus-core/issues/173): Core build fails in VS2013 +* [169](https://github.com/mupen64plus/mupen64plus-core/issues/169): With mouse input disabled, mouse still gets captured and ctrl alt does nothing +* [167](https://github.com/mupen64plus/mupen64plus-core/issues/167): N64 from open emu suddenly stopped working. +* [163](https://github.com/mupen64plus/mupen64plus-core/issues/163): Does not work on Windows XP (but works if binaries are patched) +* [161](https://github.com/mupen64plus/mupen64plus-core/issues/161): Disney's Tarzan freezes +* [153](https://github.com/mupen64plus/mupen64plus-core/issues/153): Triggering [CoreEvents] using simultaneous button presses doesn't work +* [152](https://github.com/mupen64plus/mupen64plus-core/issues/152): Matching code for input neglects spaces in controller names +* [149](https://github.com/mupen64plus/mupen64plus-core/issues/149): Bomberman 64: transition and effets are not rightly emulated +* [146](https://github.com/mupen64plus/mupen64plus-core/issues/146): RSP SETTINGS VANISHED +* [141](https://github.com/mupen64plus/mupen64plus-core/issues/141): Fails to build on windows (mingw64) +* [136](https://github.com/mupen64plus/mupen64plus-core/issues/136): si/eeprom.o (symbol from plugin): memset used with constant zero length parameter; +* [102](https://github.com/mupen64plus/mupen64plus-core/issues/102): Google Code shutting down: More organization needed +* [126](https://github.com/mupen64plus/mupen64plus-core/issues/126): Can map buttons with controller, game play doesnt work. Sixaxis +* [107](https://github.com/mupen64plus/mupen64plus-core/issues/107): Next release discussion + Mupen64Plus-core v2.5 - April 26, 2015 -------------------------------------- diff --git a/mupen64plus-core/appveyor.yml b/mupen64plus-core/appveyor.yml index 3c6530e2..c56b5314 100644 --- a/mupen64plus-core/appveyor.yml +++ b/mupen64plus-core/appveyor.yml @@ -1,5 +1,19 @@ version: 1.0.{build} +image: Visual Studio 2022 + +skip_tags: true + +skip_commits: + files: + - '**/*.md' + - .github/ + - data/* + - .gitattributes + - .gitignore + - .travis.yml + - README + configuration: - New_Dynarec_Release - Release @@ -8,6 +22,7 @@ platform: - x64 before_build: + - git tag --delete nightly-build - git clone --depth 1 https://github.com/mupen64plus/mupen64plus-win32-deps.git ..\mupen64plus-win32-deps build_script: diff --git a/mupen64plus-core/data/mupen64plus.ini b/mupen64plus-core/data/mupen64plus.ini index bdeb9940..bc3c27df 100644 --- a/mupen64plus-core/data/mupen64plus.ini +++ b/mupen64plus-core/data/mupen64plus.ini @@ -21,6 +21,11 @@ Status=1 Rumble=Yes Players=4 +[551324F5FD0A2B0BC2B1BFBB0FE29C08] +GoodName=007 - The World is Not Enough (E) [T+Spa1.0.1_IlDucci] +CRC=D8C74049 29CDD8C4 +RefMD5=34AB1DEA3111A233A8B5C5679DE22E83 + [9D58996A8AA91263B5CD45C385F45FE4] GoodName=007 - The World is Not Enough (U) [!] CRC=033F4C13 319EE7A7 @@ -208,9 +213,20 @@ RefMD5=8BEC4EDFA1446859DA896E4EBAE8F925 [0A5ACFEA0C7CF68AE25202040D5AD1EB] GoodName=40 Winks (E) (M3) (Prototype) CRC=ABA51D09 C668BAD9 +Players=2 Mempak=Yes Rumble=Yes +[8C3E4FE5F6B2D37C66A71097E8B1C72A] +GoodName=40 Winks (U) (Prototype) +CRC=AA94493C 3736CC22 +RefMD5=0A5ACFEA0C7CF68AE25202040D5AD1EB + +[B88C172ED14F4ACB43C4A352D71CC640] +GoodName=40 Winks (U) (Unl) +CRC=5EF44642 44D05A25 +RefMD5=0A5ACFEA0C7CF68AE25202040D5AD1EB + [66335F4DC6AB27034398BC26F263B897] GoodName=64 Hanafuda - Tenshi no Yakusoku (J) [!] CRC=36F22FBF 318912F2 @@ -1271,6 +1287,11 @@ Players=1 SaveType=Eeprom 4KB Rumble=Yes +[D0410D2C399CBF216BB756F35E5AC4EC] +GoodName=Banjo-Kazooie (E) [T+Spa1.1_PacoChan] +CRC=8B2FCAD3 361B412C +RefMD5=06A43BACF5C0687F596DF9B018CA6D7F + [5FC8116DAFDEC73DE9C5CCC2D8E14B4F] GoodName=Banjo-Kazooie (E) (M3) [f1] CRC=192C1422 5B8CFF48 @@ -1794,6 +1815,11 @@ SaveType=Eeprom 4KB CountPerOp=1 Rumble=Yes +[8D21DFCDE6534127CBB444B7361B0D99] +GoodName=Body Harvest (U) [T+Spa0.98_jackic] +CRC=810CED2D D7B3F710 +RefMD5=3B8585ED03E8DDB89D7DE456317545E7 + [77554810629D4EF4D93519745FA6066B] GoodName=Body Harvest (U) [b1] CRC=5326696F FE9A99C3 @@ -1890,11 +1916,21 @@ GoodName=Bomberman 64 - Arcade Edition (J) [f2] (PAL-CRC) CRC=E87F1ACE 809F204B RefMD5=08E491F87445C6E5C168D982FC665D5F -[08E491F87445C6E5C168D982FC665D5F] +[D54FD7067BD774E32B57F9C2C0496899] GoodName=Bomberman 64 - Arcade Edition (J) [!] CRC=DF6FF0F4 29D14238 Players=4 +[08E491F87445C6E5C168D982FC665D5F] +GoodName=Bomberman 64 - Arcade Edition (J) [b1] +CRC=DF6FF0F4 29D14238 +RefMD5=D54FD7067BD774E32B57F9C2C0496899 + +[C714806B4CC144A3EABD234C07412109] +GoodName=Bomberman 64 - Arcade Edition (J) [T+Eng1.3_Zoinkity] +CRC=9780C9FB 67CF6B4A +RefMD5=D54FD7067BD774E32B57F9C2C0496899 + [AEC1FDB0F1CAAD86C9F457989A4CE482] GoodName=Bomberman 64 - The Second Attack! (U) [!] CRC=237E73B4 D63B6B37 @@ -2029,6 +2065,7 @@ CRC=D72FD14D 1FED32C4 SaveType=None Mempak=Yes Players=2 +CountPerOp=1 [6E207C47FAAD2A4F64AE721A3A8F6161] GoodName=Bottom of the 9th (U) [b1] @@ -2771,7 +2808,7 @@ GoodName=Charlie Blast's Territory (E) [o1] CRC=FB3C48D0 8D28F69F RefMD5=DD53E1F83E8789D23DF6AF942FFEF236 -[59FA8C6D533D36C0FFC2AAFAB7166E6F] +[747E76D50DC3C06FD35E146129706A60] GoodName=Charlie Blast's Territory (U) [!] CRC=1E0E96E8 4E28826B SaveType=None @@ -2779,15 +2816,20 @@ Players=4 CountPerOp=1 Rumble=Yes +[59FA8C6D533D36C0FFC2AAFAB7166E6F] +GoodName=Charlie Blast's Territory (U) [b1] +CRC=1E0E96E8 4E28826B +RefMD5=747E76D50DC3C06FD35E146129706A60 + [7FFD84FC7D112EAB4EEF15CB10189A38] GoodName=Charlie Blast's Territory (U) [hIR] CRC=1E0E96E8 4E28826B -RefMD5=59FA8C6D533D36C0FFC2AAFAB7166E6F +RefMD5=747E76D50DC3C06FD35E146129706A60 [7CBB43473597F48AA9A8E0B0D38CE685] GoodName=Charlie Blast's Territory (U) [hI] CRC=A6FD0808 6E6B3ECA -RefMD5=59FA8C6D533D36C0FFC2AAFAB7166E6F +RefMD5=747E76D50DC3C06FD35E146129706A60 [8F6BED633BE214CF039DBDAC356231CE] GoodName=Chopper Attack (E) [!] @@ -3131,6 +3173,16 @@ SaveType=Eeprom 16KB Players=4 Rumble=Yes +[256564DAF29311497917BAAA14ED3F6D] +GoodName=Conker's Bad Fur Day (U) [T+Fre1.3_Corrigo] +CRC=30C7AC50 7704072D +RefMD5=00E2920665F2329B95797A7EAABC2390 + +[79A8A007092D1E5B00D0ED94F83620EB] +GoodName=Conker's Bad Fur Day (U) [T+Spa1.1_Blade133bo] +CRC=34AEB154 A4253B86 +RefMD5=00E2920665F2329B95797A7EAABC2390 + [DB7A03B77D44DB81B8A3FCDFC4B72D8C] GoodName=Cruis'n Exotica (U) [!] CRC=46A3F7AF 0F7591D0 @@ -3554,6 +3606,11 @@ SaveType=Eeprom 4KB Mempak=Yes Rumble=Yes +[14ABD7C0FA3B5256FC62824039F06A20] +GoodName=Diddy Kong Racing (E) (M3) (V1.1) [T+Ita1.0_Rulesless] +CRC=568E02F5 E212A80C +RefMD5=6B2BAFE540E0AF052A78E85B992BE999 + [35CF26D4D33717BA730D7978E2F2107D] GoodName=Diddy Kong Racing (E) (M3) (V1.1) [f1] (Z64) CRC=D84C17CA 13F8F651 @@ -4017,7 +4074,7 @@ GoodName=Animal Crossing (Ch) (iQue) [!] CRC=916EB31B F47097A5 [118E9CE360B97A6F8DAB2C9F30660BF5] -GoodName=Donkey Kong 64 (E) [!] +GoodName=Donkey Kong 64 (E) (M4) [!] CRC=11936D8C 6F2C4B43 SaveType=Eeprom 16KB Players=4 @@ -4025,17 +4082,17 @@ Rumble=Yes CountPerOp=1 [428067DC7DB42DFC977A775F0A6E55B1] -GoodName=Donkey Kong 64 (E) [b1] +GoodName=Donkey Kong 64 (E) (M4) [b1] CRC=11936D8C 6F2C4B43 RefMD5=118E9CE360B97A6F8DAB2C9F30660BF5 [C48C46362602766EA758FAAEED88AF50] -GoodName=Donkey Kong 64 (E) [f1] (Boot&Save) +GoodName=Donkey Kong 64 (E) (M4) [f1] (Boot&Save) CRC=1F95CAAA 047FC22A RefMD5=118E9CE360B97A6F8DAB2C9F30660BF5 [7BE2AAB259C437E8BA91FF8E5903837C] -GoodName=Donkey Kong 64 (E) [f1] (Save) +GoodName=Donkey Kong 64 (E) (M4) [f1] (Save) CRC=1F95CAAA 047FC22A RefMD5=118E9CE360B97A6F8DAB2C9F30660BF5 @@ -4163,6 +4220,11 @@ GoodName=Doom 64 (U) (V1.0) [t2] CRC=13D0EE29 D57A41AD RefMD5=B67748B64A2CC7EFD2F3AD4504561E0E +[FE63F970BB7F1D6798878D5835E1026C] +GoodName=Doom 64: Complete Edition (Doom 64 Hack) +CRC=5318786C 994249C6 +RefMD5=1B1378BB9EE819F740550F566745AF73 + [1B1378BB9EE819F740550F566745AF73] GoodName=Doom 64 (U) (V1.1) [!] CRC=423E96F4 CE88F05B @@ -4293,6 +4355,11 @@ CRC=769D4D13 DA233FFE Players=4 SaveType=Eeprom 4KB +[36B3D908FB50E5521DEFBEB2C39F47B0] +GoodName=Dr. Mario 64 (U) [T+Jap1.0_Zoinkity] +CRC=B74388D7 7DBD6DFB +RefMD5=1A7936367413E5D6874ABDA6D623AD32 + [0AD0800D351F77EC31092096D58C25F0] GoodName=Dragon King by CrowTRobo (PD) [b1] CRC=19E0E54C CB721FD2 @@ -5060,6 +5127,11 @@ SaveType=SRAM Players=4 Rumble=Yes +[20F5EEEE849DB44146AAFB1B97A857F6] +GoodName=F-ZERO X (U) [T+Por0.99_byftr] +CRC=485256FF CF6DF912 +RefMD5=753437D0D8ADA1D12F3F9CF0F0A5171F + [3AE32658B839FFA3189E3CA84E0B884A] GoodName=F-ZERO X (U) [f1] (Sex V1.0 Hack) CRC=7E399849 03DAC1CD @@ -5762,6 +5834,11 @@ Players=4 Mempak=Yes Rumble=Yes +[0AC7A16F8AFCC03031835C89D095D7B9] +GoodName=Getter Love!! (J) [T+Eng1.01_ozidual] +CRC=CDDB4BDF 84E5EFAC +RefMD5=5270D98F9E67DC7EF354ECE109C2A18F + [3BE170E7E94A03BD4E36732EB137EE39] GoodName=Getter Love!! (J) [b1] CRC=489C84E6 4C6E49F9 @@ -5914,12 +5991,12 @@ CRC=4690FB1C 4CD56D44 RefMD5=231BAC1AFB3DE138072C2D697783059B [2DB6ED9A6A244CB209FFDF52D9006E4D] -GoodName=Golden Nugget 64 (U) [f1] (PAL) +GoodName=Golden Nugget 64 (U) [b3][f1] (PAL) CRC=438CEBDC 44C1837C RefMD5=231BAC1AFB3DE138072C2D697783059B [F68F02A69829469F208D538D7CF4A215] -GoodName=Golden Nugget 64 (U) [h1C] +GoodName=Golden Nugget 64 (U) [b3][h1C] CRC=4690FB1C 4CD56D44 RefMD5=231BAC1AFB3DE138072C2D697783059B @@ -5931,6 +6008,11 @@ SaveType=Eeprom 4KB Rumble=Yes Status=4 +[6E23E6BAD28F8AC5C07344DCB0C266B8] +GoodName=GoldenEye 007 (E) [T+Spa2.0_Sogun&IlDucci] +CRC=625E0850 1F1B2327 +RefMD5=CFF69B70A8AD674A0EFE5558765855C9 + [ECD4F078DC77F4B2D12C1C0F5CABA323] GoodName=GoldenEye 007 (E) [b1] CRC=0414CA61 2E57B8AA @@ -6003,6 +6085,11 @@ SaveType=Eeprom 4KB Rumble=Yes Status=4 +[78F83525E0CC98641AEACE37E3FF7F16] +GoodName=GoldenEye 007 (U) [T+Spa2.0_Sogun&IlDucci] +CRC=3A03E50F E78D1CA7 +RefMD5=70C525880240C1E838B8B1BE35666C3B + [0B5A50E8D477B3BC53BE997264FE84A5] GoodName=GoldenEye 007 (U) [b1] CRC=DCBC50D1 09FD1AA3 @@ -6042,6 +6129,25 @@ RefMD5=70C525880240C1E838B8B1BE35666C3B GoodName=GoldenEye 007 Intro by SonCrap (PD) CRC=9303DD17 0813B398 +[67F2E18ED18A5F0EA3FE2D913E3F1786] +GoodName=GoldenEye X +; Alternate Title=GoldenEye X (5e Clouds) +CRC=FDF95D15 618546CA +RefMD5=E03B088B6AC9E0080440EFED07C1E40F + +[435D75D07878D305293EC4D591B86303] +GoodName=GoldenEye X +; Alternate Title=GoldenEye X (5e Cloudless) +CRC=E8B3F63D E5A997B1 +RefMD5=E03B088B6AC9E0080440EFED07C1E40F + +[EA0E3E6AEFA58738A12906298373218B] +GoodName=GoldenEye 007 (UE) (Switch Online) [!] +CRC=DCBC50D1 9FD1AA3 +Players=4 +SaveType=Eeprom 4KB +Rumble=Yes + [9B8A7541D0234F4D97004ECBC216D9C2] GoodName=HIPTHRUST by MooglyGuy (PD) CRC=88A12FB3 8A583CBD @@ -6153,6 +6259,11 @@ GoodName=Heiwa Pachinko World 64 (J) [!] CRC=3E70E866 4438BAE8 Players=1 +[78E2ED1DB5F90E2EA9C04771B27685BC] +GoodName=Heiwa Pachinko World 64 (J) [T+Eng1.0_Zoinkity] +CRC=3AE679E2 1A3C42DB +RefMD5=5E8539E037EEA88C5A2746F60E431C8D + [9D0D85E3A0C94B98D6404E8FC28E7B01] GoodName=Heiwa Pachinko World 64 (J) [b1] CRC=3E70E866 4438BAE8 @@ -6292,6 +6403,7 @@ CRC=D3F10E5D 052EA579 Players=1 SaveType=Eeprom 4KB Rumble=Yes +AiDmaModifier=88 [DCC316EFFC4928F5B0AE8D273D8024BF] GoodName=HiRes CFB Demo (PD) @@ -7839,6 +7951,11 @@ Status=4 Rumble=Yes Players=1 +[A9BA7523591C975AC00309ED516FD248] +GoodName=Legend of Zelda, The - Majora's Mask (U) [T+Ita2.0_Rulesless] +CRC=5242DFBC 2FDE6D42 +RefMD5=2A0A8ACB61538235BC1094D297FB6556 + [9C7D1B51ECBC9AC333D00273F96758B1] GoodName=Legend of Zelda, The - Majora's Mask (U) [T+Pol1.0] CRC=5354631C 03A2DEF0 @@ -8033,6 +8150,11 @@ GoodName=Legend of Zelda, The - Ocarina of Time (U) (V1.0) [T+Spa2.0_eduardo_a2j CRC=EC7011B7 7616D72B RefMD5=5BD1FE107BF8106B2AB6650ABECD54D6 +[86A9550AD8CDDACA13813765045A00EE] +GoodName=Legend of Zelda, The - Ocarina of Time (U) (V1.0) [T+Spa2.2_eduardo_a2j] +CRC=EC7011B7 7616D72B +RefMD5=5BD1FE107BF8106B2AB6650ABECD54D6 + [AAEB5ACA09291E8F5FEDFA1F576D9F0A] GoodName=Legend of Zelda, The - Ocarina of Time (U) (V1.0) [T-Pol1.2] CRC=EC7011B7 7616D72B @@ -9247,11 +9369,21 @@ GoodName=Mario Kart 64 (U) [T+Ita0.01_Cattivik66] CRC=3E5055B6 2E92DA52 RefMD5=3A67D9986F54EB282924FCA4CD5F6DFF +[22FACED432B0A9A4EC69A79452D16049] +GoodName=Mario Kart 64 (U) [T+Ita1.0_Rulesless] +CRC=4578A055 3F31815D +RefMD5=3A67D9986F54EB282924FCA4CD5F6DFF + [65CBD8DCEC6B7CE61E1CB233EFA4A317] GoodName=Mario Kart 64 (U) [T+Por1.0_Dr_X] CRC=3E5055B6 2E92DA52 RefMD5=3A67D9986F54EB282924FCA4CD5F6DFF +[79743743904EEA727CB82725BE765EC6] +GoodName=Mario Kart 64 (U) [T+Spa1.1_Blade133bo] +CRC=40CA1EC9 8CC292D4 +RefMD5=3A67D9986F54EB282924FCA4CD5F6DFF + [4A5E85A3E7118A742E3678F8F7E82B0E] GoodName=Mario Kart 64 (U) [b1] CRC=3E5055B6 2E92DA52 @@ -9315,6 +9447,11 @@ SaveType=Eeprom 4KB Rumble=Yes CountPerOp=1 +[1EAC55826A435FD1C0E0FEEFE89FAC15] +GoodName=Mario Party (E) (M3) [T+Spa1.0_PacoChan] +CRC=45C59BC3 D4C62468 +RefMD5=9773150709BD804B8E57E35F1D6B0EED + [2608A4D7A695D0B1A1BBC47695EACE0E] GoodName=Mario Party (E) (M3) [b1] CRC=9C663069 80F24A80 @@ -11885,12 +12022,18 @@ Mempak=Yes Rumble=Yes Transferpak=Yes +[5D72155E00Bf2CAB41B0F4A2f2E09C61] +GoodName=Perfect Dark Plus +CRC=FB453D58 AD565422 +RefMD5=E03B088B6AC9E0080440EFED07C1E40F + [E0BCB2758EDF0AC6AB7DB36D98E1E57C] GoodName=Pikachu Genki Dechu (J) [!] CRC=3F245305 FC0B74AA Players=1 SaveType=Eeprom 4KB Rumble=Yes +AiDmaModifier=88 [D0AE6C07AC0481EBA5FF9CE798A69574] GoodName=Pikachu Genki Dechu (J) [b1] @@ -12224,13 +12367,18 @@ Players=1 SaveType=Flash RAM CountPerOp=1 -[D0453459095F69BE36D675D8F743069B] +[8E1968191DD27655C03BE812CF041A95] GoodName=Pokemon Snap (I) [!] CRC=C0C85046 61051B05 Players=1 SaveType=Flash RAM CountPerOp=1 +[D0453459095F69BE36D675D8F743069B] +GoodName=Pokemon Snap (I) [b1] +CRC=C0C85046 61051B05 +RefMD5=8E1968191DD27655C03BE812CF041A95 + [A45D7115BE5A06FD1567F9F913C3BDF8] GoodName=Pokemon Snap (S) [!] CRC=817D286A EF417416 @@ -12596,6 +12744,16 @@ CRC=94807E6B 60CC62E4 Players=2 Rumble=Yes +[B8267C225583A02ACC3B74B3178B87E9] +GoodName=Puyo Puyo Sun 64 (J) [T+Eng1.1_Zoinkity] +CRC=1C348AFE 5B96ECFA +RefMD5=FAAA2094B04DCA4C287AF9334D22529D + +[9803F144AB702A7C3E16092AA929AB45] +GoodName=Puyo Puyo Sun 64 (J) [T+Kor1.0_Zoinkity&UltimatePenguin] +CRC=7FCBF1D2 4546EB4 +RefMD5=FAAA2094B04DCA4C287AF9334D22529D + [20C7ABF5A4DB8DC1C70725FD213E29A2] GoodName=Puyo Puyo Sun 64 (J) [b1] CRC=94807E6B 60CC62E4 @@ -13074,6 +13232,11 @@ SaveType=None Mempak=Yes Rumble=Yes +[73165CBE4E2421475C5FE9A227DCAFEC] +GoodName=Ready 2 Rumble Boxing (E) [T+Spa2.0_IlDucci] +CRC=56E06922 10F665F6 +RefMD5=ADC95AE01855FA305B13F8B22427E597 + [70F73D01FEAF768758F31B23DE2C2620] GoodName=Ready 2 Rumble Boxing (E) (M3) [t1] (P1 Untouchable) CRC=8638A334 8EBF1508 @@ -13227,47 +13390,47 @@ GoodName=Robotron 64 (E) [h1C] CRC=9FF69D4F 195F0059 RefMD5=2ABCD1AD41B3356FBC1018ECB121283E -[5698757883A6F46FE5B4C9B6E780B480] +[C4A9BBF989DDFAF5F389E4ADC6195DBC] GoodName=Robotron 64 (U) [!] CRC=AC8E4B32 E7B47326 Players=2 SaveType=None Mempak=Yes -[C4A9BBF989DDFAF5F389E4ADC6195DBC] +[5698757883A6F46FE5B4C9B6E780B480] GoodName=Robotron 64 (U) [b1] CRC=AC8E4B32 E7B47326 -RefMD5=5698757883A6F46FE5B4C9B6E780B480 +RefMD5=C4A9BBF989DDFAF5F389E4ADC6195DBC [8555DCF12FD35206451FB415F3FD2EEF] GoodName=Robotron 64 (U) [b2] CRC=8F4C4B32 6D881FCA -RefMD5=5698757883A6F46FE5B4C9B6E780B480 +RefMD5=C4A9BBF989DDFAF5F389E4ADC6195DBC [AB50026DAEA629F554408E5BA3EFA182] GoodName=Robotron 64 (U) [b3] CRC=8F4C4B32 6D881FCA -RefMD5=5698757883A6F46FE5B4C9B6E780B480 +RefMD5=C4A9BBF989DDFAF5F389E4ADC6195DBC [1AA79B5DEFE1F5852D5ADD799383CCCD] GoodName=Robotron 64 (U) [b4] CRC=575CCC2E A8B5ACE0 -RefMD5=5698757883A6F46FE5B4C9B6E780B480 +RefMD5=C4A9BBF989DDFAF5F389E4ADC6195DBC [377B9C40EBE4F4CF845E577A8DC9ACCB] GoodName=Robotron 64 (U) [f1] (PAL) CRC=8F4C4B32 6D881FCA -RefMD5=5698757883A6F46FE5B4C9B6E780B480 +RefMD5=C4A9BBF989DDFAF5F389E4ADC6195DBC [27E4C31540E18E3E8CEA97BFF744CC11] GoodName=Robotron 64 (U) [o1] CRC=AC8E4B32 E7B47326 -RefMD5=5698757883A6F46FE5B4C9B6E780B480 +RefMD5=C4A9BBF989DDFAF5F389E4ADC6195DBC [E151CCE259636C5AE15CA1C8B380EE76] GoodName=Robotron 64 (U) [t1] CRC=575CCC2E A8B5ACE0 -RefMD5=5698757883A6F46FE5B4C9B6E780B480 +RefMD5=C4A9BBF989DDFAF5F389E4ADC6195DBC [427C5457D4A29A222811F0CAA9CCA7B9] GoodName=Rocket - Robot on Wheels (E) (M3) [!] @@ -13928,6 +14091,21 @@ GoodName=SmashRemix1.0.1 CRC=FDFC3328 A0B2BA23 RefMD5=5AAC6E652C5CF1E37A466AC0073E88CA +[D8AB932AFD1CED97ACD33FA984B309ED] +GoodName=SmashRemix1.1.0 +CRC=FB816989 6F442541 +RefMD5=5AAC6E652C5CF1E37A466AC0073E88CA + +[70A61772B2123879367011A5E2900569] +GoodName=SmashRemix1.1.1 +CRC=FB816D35 3FC67AFF +RefMD5=5AAC6E652C5CF1E37A466AC0073E88CA + +[44DE1F9ED1FA35B72D31A1F809285B01] +GoodName=SmashRemix1.2.0 +CRC=A1BFF9C9 8FB192A2 +RefMD5=5AAC6E652C5CF1E37A466AC0073E88CA + [B8D4B92E66A312708626B3216DE07A3A] GoodName=Snobow Kids (J) [!] CRC=84FC04FF B1253CE9 @@ -14629,6 +14807,11 @@ GoodName=Star Wars - Shadows of the Empire (U) (V1.2) [o1] CRC=4DD7ED54 74F9287D RefMD5=C7B40352AAD8D863D88D51672F9A0087 +[4E12B97690897465A27BDF0DDA722826] +GoodName=Star Wars - Shadows of the Empire (U) (Unl) +CRC=1C5EE088 D32A963D +RefMD5=C7B40352AAD8D863D88D51672F9A0087 + [8603B180E70B2A72EF77D46C2BEC2234] GoodName=Star Wars - Shutsugeki! Rogue Chuutai (J) [!] CRC=827E4890 958468DC @@ -14685,6 +14868,11 @@ Players=2 SaveType=Eeprom 16KB Rumble=Yes +[73113AE7AAF0911BA6A1DF1353980D14] +GoodName=Star Wars Episode I - Racer (E) [T+Spa1.0.2_IlDucci] +CRC=E857ED95 6028F9AF +RefMD5=6EF9FED309F28BD59B605F128869AA00 + [8CC7E31925FBFA13A584F529E8912207] GoodName=Star Wars Episode I - Racer (E) (M3) [f1] (Save) CRC=166312CB C30220D2 @@ -14734,6 +14922,11 @@ GoodName=Star Wars Episode I - Racer (U) [t4] CRC=B722133E 5EECEC4D RefMD5=1EE8800A4003E7F9688C5A35F929D01B +[0B2B4D3C38426763059D68AA53B86FDF] +GoodName=Star Wars Episode I - Racer (U) (Unl) +CRC=21CD632F 8BCA2AD4 +RefMD5=1EE8800A4003E7F9688C5A35F929D01B + [3EB732A8D004263AD8EB0DA59A29582A] GoodName=StarCraft 64 (Beta) CRC=BC9B2CC3 4ED04DA5 @@ -14933,6 +15126,11 @@ Players=1 Status=4 SaveType=Eeprom 4KB +[6614298A943234FD30885BD0B1383CBB] +GoodName=Super Mario 64 (J) [T+Kor1.0_Minio] +CRC=B36CF0BE A35F5C7B +RefMD5=85D61F5525AF708C9F1E84DCE6DC10E9 + [DBF7F4D881E1DF604C41367B04233E74] GoodName=Super Mario 64 (J) [h1C] CRC=4EAA3D0E 74757C24 @@ -14975,6 +15173,12 @@ GoodName=Super Mario 64 (U) [T+SpaFinal_Mistergame][a1] CRC=635A42C5 BDC58EDC RefMD5=20B854B239203BAF6C961B850A4A51A2 +[E2FCF7782B622495FABE12FC7244B35B] +GoodName=Super Mario 64 (U) [T+Spa3.9_Blade133bo] +CRC=88EC3675 59CE4583 +RefMD5=20B854B239203BAF6C961B850A4A51A2 +Rumble=Yes + [35BBAF4869D0B0D2147D35F1EC3CF2CA] GoodName=Super Mario 64 (U) [T-Ita1.0final_beta1_Rulesless] CRC=819D5B33 A5096295 @@ -14985,6 +15189,12 @@ GoodName=Super Mario 64 (U) [T+Ita2.0final_beta2_Rulesless] CRC=490A59F2 22BC515E RefMD5=20B854B239203BAF6C961B850A4A51A2 +[E5544B156833120E7B97A05AAB743F98] +GoodName=Super Mario 64 (U) [T+Ita4.2_Rulesless] +CRC=5EDE011C E166686 +RefMD5=20B854B239203BAF6C961B850A4A51A2 +Rumble=Yes + [933C474FC970A0E7D659F32334DB6475] GoodName=Super Mario 64 (U) [b1] CRC=635A2BFF 8B022326 @@ -15030,6 +15240,16 @@ GoodName=Super Mario Magic Plant Adventure 64 (Super Mario 64 Hack) CRC=635A42C5 BDC58EDC RefMD5=20B854B239203BAF6C961B850A4A51A2 +[338FAA6E250F72F1B1D7B873BFAEE918] +GoodName=SM64 Splitscreen Multiplayer Beta +CRC=5C277FBA E8A351C9 +RefMD5=20B854B239203BAF6C961B850A4A51A2 + +[F0595A36EAB6109C5FF2420EB452B6E2] +GoodName=SM64 Splitscreen Multiplayer +CRC=13CD0830 3CCF65E9 +RefMD5=20B854B239203BAF6C961B850A4A51A2 + [2D727C3278AA232D94F2FB45AEC4D303] GoodName=Super Mario 64 - Shindou Edition (J) [!] CRC=D6FBA4A8 6326AA2C @@ -15038,6 +15258,11 @@ Status=4 SaveType=Eeprom 4KB Rumble=Yes +[A00D0D095592113A279A27ACD785CE50] +GoodName=Super Mario 64 - Shindou Edition (J) [T+Kor1.0_Minio] +CRC=C1373DC8 ED88D197 +RefMD5=2D727C3278AA232D94F2FB45AEC4D303 + [23033085561CD331CC81F0026FCB2CE2] GoodName=Super Mario 64 - Shindou Edition (J) [b1] CRC=D6FBA4A8 6326AA2C @@ -16012,6 +16237,16 @@ GoodName=Tsumi to Batsu - Hoshi no Keishousha (J) [T+Eng1.0_Zoinkity] CRC=B73AB6F6 296267DD RefMD5=A0657BC99E169153FD46AECCFDE748F3 +[79BD05A97E50AC63A638E4E90AB9588B] +GoodName=Tsumi to Batsu - Hoshi no Keishousha (J) [T+Spa1.0b_TheFireRed&IlDucci] +CRC=B1C7794B D93DC5F3 +RefMD5=A0657BC99E169153FD46AECCFDE748F3 + +[103E16BF7FCFB4D713D2EBE19DCB5311] +GoodName=Tsumi to Batsu - Hoshi no Keishousha (J) [T+Spa1.0b_TheFireRed&IlDucci] (Fandub) +CRC=B1C7794B D93DC5F3 +RefMD5=A0657BC99E169153FD46AECCFDE748F3 + [13FAA58604597E4EDC608070F8E0AE24] GoodName=Turok - Dinosaur Hunter (E) (V1.0) [!] CRC=2F7009DD FC3BAC53 @@ -16064,6 +16299,11 @@ Players=1 SaveType=None Mempak=Yes +[85F616E54160FFE14E4E817065EA61CE] +GoodName=Turok - Dinosaur Hunter (U) (V1.0) (Analog Movement Controls_1.01 - No Invert) +CRC=2F70F10D 5C4187FF +RefMD5=AE5107EFDD3C210E1EDD4ACD9B3CAC31 + [AE5107EFDD3C210E1EDD4ACD9B3CAC31] GoodName=Turok - Dinosaur Hunter (U) (V1.0) [!] CRC=2F70F10D 5C4187FF @@ -17162,6 +17402,16 @@ SaveType=Eeprom 4KB Mempak=Yes Rumble=Yes +[ECC1692D12FDCB0C3F605E44DD54CE8C] +GoodName=Wave Race 64 - Shindou Edition (J) (V1.2) [T+Eng1.0_Zoinkity] +CRC=57AF893E 884A377C +RefMD5=FF67DF97476C210D158779AE6142F239 + +[D0472173524D6EC4E78CBCA30EFD98FC] +GoodName=Wave Race 64 - Shindou Edition (J) (V1.2) [T+Eng1.1_Zoinkity] +CRC=57AF893E 8EEF209A +RefMD5=FF67DF97476C210D158779AE6142F239 + [AFBC694A3BA5AE83D3CEEF906BF01839] GoodName=Wave Race 64 - Shindou Edition (J) (V1.2) [b1] CRC=535DF3E2 609789F1 @@ -17612,6 +17862,14 @@ GoodName=Xena Warrior Princess - The Talisman of Fate (U) [t1] CRC=BEADC61C 8622D9F6 RefMD5=1AC234649D28F09E82C0D11ABB17F03B +[BC4BCD45FA712ECDD8A85918A9CA5F4B] +GoodName=Xeno Crisis (A) +CRC=470112E9 53C66CB4 +Players=2 +SaveType=None +Mempak=Yes +Rumble=Yes + [9137129A586E1BCAB6AE81BAC6B01275] GoodName=Xplorer64 BIOS V1.067 (G) CRC=656C6561 73652031 @@ -18058,3 +18316,73 @@ GoodName=Zelda no Densetsu - Mujura no Kamen (J) (V1.1) [!] CRC=69AE0438 2C63F3F3 RefMD5=15D1A2217CAD61C39CFECBFFA0703E25 +; 64DD Games +; +[8D3D9F294B6E174BC7B1D2FD1C727530] +GoodName=64DD IPL (JPN) + +[37C36E4286D36892A9FC70EAFE4104BE] +GoodName=64DD IPL (USA) + +[8485643E5830CD67ED4C0A5FD49E2491] +GoodName=Mario Artist Paint Studio (J) + +[88228E990B58A94E9B3460BEFF632304] +GoodName=Mario Artist Talent Studio (J) + +[114AF722029D6386C3BFEA4CC8FA603C] +GoodName=Mario Artist Communication Kit (J) + +[DA23EE561578B7DAD77ED72728B46D30] +GoodName=Mario Artist Polygon Studio (J) + +[EBBA03F20096FC2BC178FC3A1F4EC2B6] +GoodName=Sim City 64 (J) + +[9F797A9C704B5EBD04D6A2B036309AF2] +GoodName=Nihon Pro Golf Tour 64 (J) + +[E8EB810D996E12CD6C47445F87A94C72] +GoodName=Kyojin no Doshin 1 (J) + +[AFE059AE1210751B6C849CFB482C99DD] +GoodName=Kyojin no Doshin 1 (J) (Store Demo) + +[26087E0750FC6DA0F3DE2A431E34BDDF] +GoodName=Randnet Disk (J) [Rev. 00] + +[33CBC59786063285DC3C090E0ED312E3] +GoodName=Randnet Disk (J) [Rev. 01] + +[FCCA9AF8C1174C40B478EA0E02673B16] +GoodName=Kyojin no Doshin - Kaihou Sensen Chibikkochikko Dai Shuugou (J) + +[CB2FB00C3921245AE04BB38BA01ABE92] +GoodName=F-ZERO X Expansion Kit (J) + +[D9EA905727B44F2774926CB03C2300A7] +GoodName=Super Mario 64 Disk Version (J) (Spaceworld 1996 Demo) + +[84706E10026BD2EE9654DA5E9821598D] +GoodName=Dezaemon 3D Expansion Disk (J) (Proto) +SaveType=SRAM + +[7BB3974754A54BA27A93F96691DC4695] +GoodName=Dezaemon 3D Expansion Disk (J) (Proto) +SaveType=SRAM + +[40AA8EC52C5E025D6C6AD94CEBC2218F] +GoodName=Dezaemon 3D Expansion Disk (J) (Proto) +SaveType=SRAM + +[C108A9B1EB024F0D2317C05976BA49F2] +GoodName=Dezaemon 3D Expansion Disk (J) (Proto) [a1] +SaveType=SRAM + +[4D02F5A719BD455F67721C64B16AE0CB] +GoodName=Mario Artist Paint Studio (J) (1999-02-11 Proto) +CountPerOp=1 + +[A76B619EC832A7E2ABCFBDFEB5375E39] +GoodName=Mario Artist Paint Studio (J) (1999-02-11 Proto) +CountPerOp=1 diff --git a/mupen64plus-core/doc/emuwiki-api-doc/Mupen64Plus-v2.0-API-Versioning.mediawiki b/mupen64plus-core/doc/emuwiki-api-doc/Mupen64Plus-v2.0-API-Versioning.mediawiki index 882ecb71..0414ff26 100644 --- a/mupen64plus-core/doc/emuwiki-api-doc/Mupen64Plus-v2.0-API-Versioning.mediawiki +++ b/mupen64plus-core/doc/emuwiki-api-doc/Mupen64Plus-v2.0-API-Versioning.mediawiki @@ -128,4 +128,13 @@ This is the most complicated interface, because it involves 3 components: the vi * '''FRONTEND_API_VERSION''' version 2.1.4: ** added "M64CMD_ROM_SET_SETTINGS" command to allow setting ROM settings for the currently opened ROM until the ROM is closed. * '''CONFIG_API_VERSION''' version 2.3.2: -** add ConfigOverrideUserPaths() function to allow front-ends to override user paths. \ No newline at end of file +** add ConfigOverrideUserPaths() function to allow front-ends to override user paths. +* '''INPUT_API_VERSION''' version 2.1.1: +** add optional functions: SendVRUWord(), SetMicState(), ReadVRUResults(), ClearVRUWords(), SetVRUWordMask(). These functions add support for the VRU (Voice Recognition Unit). Also added a new int to the CONTROL struct: Type. Type can be CONT_TYPE_STANDARD (0) or CONT_TYPE_VRU (1). +* '''FRONTEND_API_VERSION''' version 2.1.5: +** added "M64CMD_DISK_OPEN" and "M64CMD_DISK_CLOSE" commands to allow booting 64DD disks without requiring a cartridge. +* '''FRONTEND_API_VERSION''' version 2.1.6: +** added "m64p_core_param" type: +*** M64CORE_SCREENSHOT_CAPTURED +* '''VIDEXT_API_VERSION''' version 3.3.0: +** add the VidExt_InitWithRenderMode, VidExt_VK_GetSurface and VidExt_VK_GetInstanceExtensions functions, which allows a plugin to use Vulkan and a front-end to support Vulkan diff --git a/mupen64plus-core/doc/emuwiki-api-doc/Mupen64Plus-v2.0-Core-Front-End.mediawiki b/mupen64plus-core/doc/emuwiki-api-doc/Mupen64Plus-v2.0-Core-Front-End.mediawiki index 36a12f41..d4cc30c0 100644 --- a/mupen64plus-core/doc/emuwiki-api-doc/Mupen64Plus-v2.0-Core-Front-End.mediawiki +++ b/mupen64plus-core/doc/emuwiki-api-doc/Mupen64Plus-v2.0-Core-Front-End.mediawiki @@ -89,13 +89,23 @@ Most libmupen64plus functions return an m64p_error return code, which i |M64CMD_ROM_OPEN |This will cause the core to read in a binary ROM image provided by the front-end. |'''ParamPtr''' Pointer to the uncompressed ROM image in memory.
'''ParamInt''' The size in bytes of the ROM image. -|The emulator cannot be currently running. A ROM image must not be currently opened. +|The emulator cannot be currently running. A ROM image or disk must not be currently opened. |- |M64CMD_ROM_CLOSE |This will close any currently open ROM. The current cheat code list will also be deleted. |N/A |The emulator cannot be currently running. A ROM image must have been previously opened. There should be no plugins currently attached. |- +|M64CMD_DISK_OPEN +|This will cause the core to read in a disk using the medialoader API provided by the front-end. +|N/A +|The emulator cannot be currently running. A disk or ROM image must not be currently opened. +|- +|M64CMD_DISK_CLOSE +|This will close any currently open disk. The current cheat code list will also be deleted. +|N/A +|The emulator cannot be currently running. A disk must have been previously opened. There should be no plugins currently attached. +|- |M64CMD_ROM_GET_HEADER |This will retrieve the header data of the currently open ROM. |'''ParamPtr''' Pointer to a rom_header struct to receive the data.
'''ParamInt''' The size in bytes of the rom_header struct. @@ -296,6 +306,12 @@ These core parameters may be read and/or written using the M64CMD_CORE_STATE_QUE |1 if state saving was successful, 0 if state saving failed. |This parameter cannot be read or written. It is only used for callbacks, because the state load/save operations are asynchronous. |} +|M64CORE_SCREENSHOT_CAPTURED +|No +|No +|1 if capturing screenshot was successful, 0 if capturing screenshot failed. +|This parameter cannot be read or written. It is only used for callbacks. +|}
== ROM Handling Functions == diff --git a/mupen64plus-core/doc/emuwiki-api-doc/Mupen64Plus-v2.0-Core-Video-Extension.mediawiki b/mupen64plus-core/doc/emuwiki-api-doc/Mupen64Plus-v2.0-Core-Video-Extension.mediawiki index c9402ddb..9faf0612 100644 --- a/mupen64plus-core/doc/emuwiki-api-doc/Mupen64Plus-v2.0-Core-Video-Extension.mediawiki +++ b/mupen64plus-core/doc/emuwiki-api-doc/Mupen64Plus-v2.0-Core-Video-Extension.mediawiki @@ -23,6 +23,20 @@ All of these functions should only be called from within the video plugin; they
{| border="1" |Prototype +|'''m64p_error VidExt_InitWithRenderMode(m64p_render_mode RenderMode)''' +|- +|Input Parameters +|'''RenderMode''' Render mode, either M64P_RENDER_OPENGL or M64P_RENDER_VULKAN, defined in [[Mupen64Plus v2.0 headers#m64p_types.h|m64p_types.h]]
+|- +|Requirements +|This function should be called before any other Video Extension functions. +|- +|Usage +|This function should be called from within the RomOpen() video plugin function call. The default SDL implementation of this function simply calls SDL_InitSubSystem(SDL_INIT_VIDEO). It does not open a rendering window or switch video modes. +|} +
+{| border="1" +|Prototype |'''m64p_error VidExt_Quit(void)''' |- |Input Parameters @@ -242,3 +256,35 @@ In the Core Video Extension function ResizeWindow, the SDL function SetVideoMode |On some platforms (for instance, iOS) the default framebuffer object depends on the surface being rendered to, and might be different from 0. This function should be called to retrieve the name of the default FBO. Calling this function may have performance implications and it should not be called every time the default FBO is bound. |}
+ +== Vulkan Functions == +{| border="1" +|Prototype +|'''m64p_error VidExt_VK_GetSurface(void** Surface, void* Instance)''' +|- +|Input Parameters +|'''Surface''' Pointer to a VkSurfaceKHR which will be returned when function succeeds
+'''Instance''' VkInstance which can be used as the front-end's Vulkan instance +|- +|Requirements +|The video extension must be initialized before calling this function. +|- +|Usage +|This function is used to retrieve the Vulkan surface +|} +
+{| border="1" +|Prototype +|'''m64p_error VidExt_VK_GetInstanceExtensions(const char** Extensions[], uint32_t* NumExtensions)''' +|- +|Input Parameters +|'''Extensions''' Pointer to an array of strings
+'''NumExtensions''' Pointer to an integer which contains the size of the number of objects stored for output. +|- +|Requirements +|The video extension must be initialized before calling this function. +|- +|Usage +|This function is used to retrieve the supported Vulkan extensions. +|} +
diff --git a/mupen64plus-core/doc/emuwiki-api-doc/Mupen64Plus-v2.0-Plugin-API.mediawiki b/mupen64plus-core/doc/emuwiki-api-doc/Mupen64Plus-v2.0-Plugin-API.mediawiki index 6bfcabe3..99b87cd4 100644 --- a/mupen64plus-core/doc/emuwiki-api-doc/Mupen64Plus-v2.0-Plugin-API.mediawiki +++ b/mupen64plus-core/doc/emuwiki-api-doc/Mupen64Plus-v2.0-Plugin-API.mediawiki @@ -224,6 +224,21 @@ These functions are present in all of the plugins. |- |void SDL_KeyUp(int keymod, int keysym); |Pass a SDL_KEYUP-style message to the input plugin +|- +|void SendVRUWord(uint16_t length, uint16_t *word, uint8_t lang); +|Load a word/phrase into the VRU dictionary. length specifies the length of the buffer. lang is 0 for English and 1 for Japanese. Japanese words are encoding using S-JIS. English words are encoded using a phonetic alphabet explained here: https://pastebin.com/ajLzRLze +|- +|void SetMicState(int state); +|A state of 1 tells the input plugin to enable the microphone. A state of 0 tells the input plugin to disable the microphone +|- +|void ReadVRUResults(uint16_t *error_flags, uint16_t *num_results, uint16_t *mic_level, uint16_t *voice_level, uint16_t *voice_length, uint16_t *matches); +|Refer to https://pastebin.com/6UiErk5h and https://ultra64.ca/files/documentation/online-manuals/man/pro-man/pro26/index26.8.html for an explanation of these variables. This function is called after the player has finished speaking. The purpose is for the VRU/input plugin to tell the core if any matches were found against the dictionary that was provided earlier +|- +|void ClearVRUWords(uint8_t length); +|Clears and initializes the word/phrase dictionary with a max length as provided by the variable +|- +|void SetVRUWordMask(uint8_t length, uint8_t *mask); +|Assign a mask to the words in the dictionary. A bit of 1 means to recognize the word, 0 means to ignore the word. See https://ultra64.ca/files/documentation/online-manuals/man/pro-man/pro26/index26.8.html |} === Remove From Older Input API === diff --git a/mupen64plus-core/doc/emuwiki-api-doc/Mupen64Plus-v2.0-headers.mediawiki b/mupen64plus-core/doc/emuwiki-api-doc/Mupen64Plus-v2.0-headers.mediawiki index 39ada3db..67f853ae 100644 --- a/mupen64plus-core/doc/emuwiki-api-doc/Mupen64Plus-v2.0-headers.mediawiki +++ b/mupen64plus-core/doc/emuwiki-api-doc/Mupen64Plus-v2.0-headers.mediawiki @@ -162,6 +162,12 @@ */ char* (*get_gb_cart_ram)(void* cb_data, int controller_num); + /* Allow the frontend to know what DD IPL ROM region file to load + * cb_data: points to frontend-defined callback data. + * region: a region from m64p_system_type + */ + void (*set_dd_rom_region)(void* cb_data, uint8_t region); + /* Allow the frontend to specify the DD IPL ROM file to load * cb_data: points to frontend-defined callback data. * Returns a NULL-terminated string owned by the core specifying the DD IPL ROM filename to load diff --git a/mupen64plus-core/projects/msvc/mupen64plus-core.vcxproj b/mupen64plus-core/projects/msvc/mupen64plus-core.vcxproj index 782fd395..84d8d304 100644 --- a/mupen64plus-core/projects/msvc/mupen64plus-core.vcxproj +++ b/mupen64plus-core/projects/msvc/mupen64plus-core.vcxproj @@ -136,6 +136,7 @@ + @@ -200,6 +201,7 @@ + @@ -440,6 +442,7 @@ + @@ -479,6 +482,7 @@ + @@ -682,13 +686,13 @@ true false Document - "..\..\..\mupen64plus-win32-deps\nasm-2.14.02\x64\nasm.exe" -i ..\..\src\ -i ..\..\src\asm_defines\ -o $(IntDir)linkage_x64.obj -f win64 -d WIN64 "%(FullPath)" + "..\..\..\mupen64plus-win32-deps\nasm-2.16.01\x64\nasm.exe" -i ..\..\src\ -i ..\..\src\asm_defines\ -o "$(IntDir)linkage_x64.obj" -f win64 -d WIN64 "%(FullPath)" $(IntDir)linkage_x64.obj - "..\..\..\mupen64plus-win32-deps\nasm-2.14.02\x64\nasm.exe" -i ..\..\src\ -i ..\..\src\asm_defines\ -o $(IntDir)linkage_x64.obj -f win64 -d WIN64 "%(FullPath)" + "..\..\..\mupen64plus-win32-deps\nasm-2.16.01\x64\nasm.exe" -i ..\..\src\ -i ..\..\src\asm_defines\ -o "$(IntDir)linkage_x64.obj" -f win64 -d WIN64 "%(FullPath)" $(IntDir)linkage_x64.obj - "..\..\..\mupen64plus-win32-deps\nasm-2.14.02\x64\nasm.exe" -i ..\..\src\ -i ..\..\src\asm_defines\ -o $(IntDir)linkage_x64.obj -f win64 -d WIN64 "%(FullPath)" + "..\..\..\mupen64plus-win32-deps\nasm-2.16.01\x64\nasm.exe" -i ..\..\src\ -i ..\..\src\asm_defines\ -o "$(IntDir)linkage_x64.obj" -f win64 -d WIN64 "%(FullPath)" $(IntDir)linkage_x64.obj - "..\..\..\mupen64plus-win32-deps\nasm-2.14.02\x64\nasm.exe" -i ..\..\src\ -i ..\..\src\asm_defines\ -o $(IntDir)linkage_x64.obj -f win64 -d WIN64 "%(FullPath)" + "..\..\..\mupen64plus-win32-deps\nasm-2.16.01\x64\nasm.exe" -i ..\..\src\ -i ..\..\src\asm_defines\ -o "$(IntDir)linkage_x64.obj" -f win64 -d WIN64 "%(FullPath)" $(IntDir)linkage_x64.obj @@ -734,13 +738,13 @@ false true Document - "..\..\..\mupen64plus-win32-deps\nasm-2.14.02\x86\nasm.exe" -i ..\..\src\ -i ..\..\src\asm_defines\ -o $(IntDir)linkage_x86.obj -f win32 -d LEADING_UNDERSCORE "%(FullPath)" + "..\..\..\mupen64plus-win32-deps\nasm-2.16.01\x86\nasm.exe" -i ..\..\src\ -i ..\..\src\asm_defines\ -o "$(IntDir)linkage_x86.obj" -f win32 -d LEADING_UNDERSCORE "%(FullPath)" $(IntDir)linkage_x86.obj - "..\..\..\mupen64plus-win32-deps\nasm-2.14.02\x86\nasm.exe" -i ..\..\src\ -i ..\..\src\asm_defines\ -o $(IntDir)linkage_x86.obj -f win32 -d LEADING_UNDERSCORE "%(FullPath)" + "..\..\..\mupen64plus-win32-deps\nasm-2.16.01\x86\nasm.exe" -i ..\..\src\ -i ..\..\src\asm_defines\ -o "$(IntDir)linkage_x86.obj" -f win32 -d LEADING_UNDERSCORE "%(FullPath)" $(IntDir)linkage_x86.obj - "..\..\..\mupen64plus-win32-deps\nasm-2.14.02\x86\nasm.exe" -i ..\..\src\ -i ..\..\src\asm_defines\ -o $(IntDir)linkage_x86.obj -f win32 -d LEADING_UNDERSCORE "%(FullPath)" + "..\..\..\mupen64plus-win32-deps\nasm-2.16.01\x86\nasm.exe" -i ..\..\src\ -i ..\..\src\asm_defines\ -o "$(IntDir)linkage_x86.obj" -f win32 -d LEADING_UNDERSCORE "%(FullPath)" $(IntDir)linkage_x86.obj - "..\..\..\mupen64plus-win32-deps\nasm-2.14.02\x86\nasm.exe" -i ..\..\src\ -i ..\..\src\asm_defines\ -o $(IntDir)linkage_x86.obj -f win32 -d LEADING_UNDERSCORE "%(FullPath)" + "..\..\..\mupen64plus-win32-deps\nasm-2.16.01\x86\nasm.exe" -i ..\..\src\ -i ..\..\src\asm_defines\ -o "$(IntDir)linkage_x86.obj" -f win32 -d LEADING_UNDERSCORE "%(FullPath)" $(IntDir)linkage_x86.obj @@ -757,9 +761,9 @@ true true Document - "..\..\..\mupen64plus-win32-deps\nasm-2.14.02\x64\nasm.exe" -i ..\..\src\ -i ..\..\src\asm_defines\ -o $(IntDir)dyna_start.obj -f win64 -d WIN64 "%(FullPath)" + "..\..\..\mupen64plus-win32-deps\nasm-2.16.01\x64\nasm.exe" -i ..\..\src\ -i ..\..\src\asm_defines\ -o "$(IntDir)dyna_start.obj" -f win64 -d WIN64 "%(FullPath)" $(IntDir)dyna_start.obj - "..\..\..\mupen64plus-win32-deps\nasm-2.14.02\x64\nasm.exe" -i ..\..\src\ -i ..\..\src\asm_defines\ -o $(IntDir)dyna_start.obj -f win64 -d WIN64 "%(FullPath)" + "..\..\..\mupen64plus-win32-deps\nasm-2.16.01\x64\nasm.exe" -i ..\..\src\ -i ..\..\src\asm_defines\ -o "$(IntDir)dyna_start.obj" -f win64 -d WIN64 "%(FullPath)" $(IntDir)dyna_start.obj @@ -776,9 +780,9 @@ false true Document - "..\..\..\mupen64plus-win32-deps\nasm-2.14.02\x86\nasm.exe" -i ..\..\src\ -i ..\..\src\asm_defines\ -o $(IntDir)dyna_start.obj -f win32 -d LEADING_UNDERSCORE "%(FullPath)" + "..\..\..\mupen64plus-win32-deps\nasm-2.16.01\x86\nasm.exe" -i ..\..\src\ -i ..\..\src\asm_defines\ -o "$(IntDir)dyna_start.obj" -f win32 -d LEADING_UNDERSCORE "%(FullPath)" $(IntDir)dyna_start.obj - "..\..\..\mupen64plus-win32-deps\nasm-2.14.02\x86\nasm.exe" -i ..\..\src\ -i ..\..\src\asm_defines\ -o $(IntDir)dyna_start.obj -f win32 -d LEADING_UNDERSCORE "%(FullPath)" + "..\..\..\mupen64plus-win32-deps\nasm-2.16.01\x86\nasm.exe" -i ..\..\src\ -i ..\..\src\asm_defines\ -o "$(IntDir)dyna_start.obj" -f win32 -d LEADING_UNDERSCORE "%(FullPath)" $(IntDir)dyna_start.obj @@ -926,14 +930,14 @@ Disabled - ..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\include;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\include;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;%(AdditionalIncludeDirectories) + ..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.26.3\include;..\..\..\mupen64plus-win32-deps\SDL2_net-2.2.0\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.13\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.39\include;..\..\..\mupen64plus-win32-deps\freetype-2.13.0\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;DYNAREC;M64P_OSD;M64P_NETPLAY;M64P_PARALLEL;%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebugDLL Level3 - shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\lib\x86\SDL2.lib;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\lib\x86\SDL2_net.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\lib\x86\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\lib\x86\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\lib\x86\freetype.lib;%(AdditionalDependencies) + shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.26.3\lib\x86\SDL2.lib;..\..\..\mupen64plus-win32-deps\SDL2_net-2.2.0\lib\x86\SDL2_net.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.13\lib\x86\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.39\lib\x86\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.13.0\lib\x86\freetype.lib;%(AdditionalDependencies) true Windows @@ -944,14 +948,14 @@ Disabled - ..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\include;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\include;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;%(AdditionalIncludeDirectories) + ..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.26.3\include;..\..\..\mupen64plus-win32-deps\SDL2_net-2.2.0\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.13\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.39\include;..\..\..\mupen64plus-win32-deps\freetype-2.13.0\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;DYNAREC;M64P_PARALLEL;M64P_OSD;M64P_NETPLAY;__x86_64__;%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebugDLL Level3 - shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\lib\x64\SDL2.lib;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\lib\x64\SDL2_net.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\lib\x64\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\lib\x64\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\lib\x64\freetype.lib;%(AdditionalDependencies) + shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.26.3\lib\x64\SDL2.lib;..\..\..\mupen64plus-win32-deps\SDL2_net-2.2.0\lib\x64\SDL2_net.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.13\lib\x64\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.39\lib\x64\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.13.0\lib\x64\freetype.lib;%(AdditionalDependencies) true Windows @@ -962,14 +966,14 @@ Disabled - ..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\include;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\include;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;%(AdditionalIncludeDirectories) + ..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.26.3\include;..\..\..\mupen64plus-win32-deps\SDL2_net-2.2.0\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.13\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.39\include;..\..\..\mupen64plus-win32-deps\freetype-2.13.0\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;DYNAREC;M64P_OSD;M64P_NETPLAY;M64P_PARALLEL;NEW_DYNAREC=1;%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebugDLL Level3 - shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\lib\x86\SDL2.lib;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\lib\x86\SDL2_net.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\lib\x86\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\lib\x86\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\lib\x86\freetype.lib;%(AdditionalDependencies) + shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.26.3\lib\x86\SDL2.lib;..\..\..\mupen64plus-win32-deps\SDL2_net-2.2.0\lib\x86\SDL2_net.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.13\lib\x86\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.39\lib\x86\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.13.0\lib\x86\freetype.lib;%(AdditionalDependencies) true Windows @@ -980,14 +984,14 @@ Disabled - ..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\include;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\include;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;..\..\..\mupen64plus-win32-deps\capstone\include;%(AdditionalIncludeDirectories) + ..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.26.3\include;..\..\..\mupen64plus-win32-deps\SDL2_net-2.2.0\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.13\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.39\include;..\..\..\mupen64plus-win32-deps\freetype-2.13.0\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;..\..\..\mupen64plus-win32-deps\capstone\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;DYNAREC;M64P_OSD;M64P_NETPLAY;M64P_PARALLEL;NEW_DYNAREC=1;RECOMPILER_DEBUG=1;%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebugDLL Level3 - shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\lib\x86\SDL2.lib;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\lib\x86\SDL2_net.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\lib\x86\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\lib\x86\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\lib\x86\freetype.lib;..\..\..\mupen64plus-win32-deps\capstone\lib\x86\capstone_dll.lib;%(AdditionalDependencies) + shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.26.3\lib\x86\SDL2.lib;..\..\..\mupen64plus-win32-deps\SDL2_net-2.2.0\lib\x86\SDL2_net.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.13\lib\x86\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.39\lib\x86\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.13.0\lib\x86\freetype.lib;..\..\..\mupen64plus-win32-deps\capstone\lib\x86\capstone_dll.lib;%(AdditionalDependencies) true Windows @@ -998,14 +1002,14 @@ Disabled - ..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\include;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\include;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;..\..\..\mupen64plus-win32-deps\capstone\include;%(AdditionalIncludeDirectories) + ..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.26.3\include;..\..\..\mupen64plus-win32-deps\SDL2_net-2.2.0\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.13\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.39\include;..\..\..\mupen64plus-win32-deps\freetype-2.13.0\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;..\..\..\mupen64plus-win32-deps\capstone\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;DYNAREC;M64P_OSD;M64P_NETPLAY;M64P_PARALLEL;NEW_DYNAREC=1;RECOMPILER_DEBUG=3;%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebugDLL Level3 - shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\lib\x86\SDL2.lib;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\lib\x86\SDL2_net.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\lib\x86\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\lib\x86\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\lib\x86\freetype.lib;..\..\..\mupen64plus-win32-deps\capstone\lib\x86\capstone_dll.lib;%(AdditionalDependencies) + shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.26.3\lib\x86\SDL2.lib;..\..\..\mupen64plus-win32-deps\SDL2_net-2.2.0\lib\x86\SDL2_net.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.13\lib\x86\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.39\lib\x86\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.13.0\lib\x86\freetype.lib;..\..\..\mupen64plus-win32-deps\capstone\lib\x86\capstone_dll.lib;%(AdditionalDependencies) true Windows @@ -1016,14 +1020,14 @@ Disabled - ..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\include;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\include;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;%(AdditionalIncludeDirectories) + ..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.26.3\include;..\..\..\mupen64plus-win32-deps\SDL2_net-2.2.0\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.13\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.39\include;..\..\..\mupen64plus-win32-deps\freetype-2.13.0\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;DYNAREC;M64P_PARALLEL;M64P_OSD;M64P_NETPLAY;NEW_DYNAREC=2;__x86_64__;%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebugDLL Level3 - shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\lib\x64\SDL2.lib;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\lib\x64\SDL2_net.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\lib\x64\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\lib\x64\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\lib\x64\freetype.lib;%(AdditionalDependencies) + shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.26.3\lib\x64\SDL2.lib;..\..\..\mupen64plus-win32-deps\SDL2_net-2.2.0\lib\x64\SDL2_net.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.13\lib\x64\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.39\lib\x64\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.13.0\lib\x64\freetype.lib;%(AdditionalDependencies) true Windows @@ -1034,14 +1038,14 @@ Disabled - ..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\include;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\include;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;..\..\..\mupen64plus-win32-deps\capstone\include;%(AdditionalIncludeDirectories) + ..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.26.3\include;..\..\..\mupen64plus-win32-deps\SDL2_net-2.2.0\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.13\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.39\include;..\..\..\mupen64plus-win32-deps\freetype-2.13.0\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;..\..\..\mupen64plus-win32-deps\capstone\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;DYNAREC;M64P_PARALLEL;M64P_OSD;M64P_NETPLAY;NEW_DYNAREC=2;__x86_64__;RECOMPILER_DEBUG=4;%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebugDLL Level3 - shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\lib\x64\SDL2.lib;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\lib\x64\SDL2_net.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\lib\x64\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\lib\x64\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\lib\x64\freetype.lib;..\..\..\mupen64plus-win32-deps\capstone\lib\x64\capstone_dll.lib;%(AdditionalDependencies) + shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.26.3\lib\x64\SDL2.lib;..\..\..\mupen64plus-win32-deps\SDL2_net-2.2.0\lib\x64\SDL2_net.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.13\lib\x64\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.39\lib\x64\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.13.0\lib\x64\freetype.lib;..\..\..\mupen64plus-win32-deps\capstone\lib\x64\capstone_dll.lib;%(AdditionalDependencies) true Windows @@ -1052,14 +1056,14 @@ Disabled - ..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\include;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\include;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;..\..\..\mupen64plus-win32-deps\capstone\include;%(AdditionalIncludeDirectories) + ..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.26.3\include;..\..\..\mupen64plus-win32-deps\SDL2_net-2.2.0\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.13\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.39\include;..\..\..\mupen64plus-win32-deps\freetype-2.13.0\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;..\..\..\mupen64plus-win32-deps\capstone\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;DYNAREC;M64P_PARALLEL;M64P_OSD;M64P_NETPLAY;NEW_DYNAREC=2;__x86_64__;RECOMPILER_DEBUG=2;%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebugDLL Level3 - shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\lib\x64\SDL2.lib;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\lib\x64\SDL2_net.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\lib\x64\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\lib\x64\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\lib\x64\freetype.lib;..\..\..\mupen64plus-win32-deps\capstone\lib\x64\capstone_dll.lib;%(AdditionalDependencies) + shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.26.3\lib\x64\SDL2.lib;..\..\..\mupen64plus-win32-deps\SDL2_net-2.2.0\lib\x64\SDL2_net.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.13\lib\x64\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.39\lib\x64\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.13.0\lib\x64\freetype.lib;..\..\..\mupen64plus-win32-deps\capstone\lib\x64\capstone_dll.lib;%(AdditionalDependencies) true Windows @@ -1069,7 +1073,7 @@ - ..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\include;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\include;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;%(AdditionalIncludeDirectories) + ..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.26.3\include;..\..\..\mupen64plus-win32-deps\SDL2_net-2.2.0\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.13\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.39\include;..\..\..\mupen64plus-win32-deps\freetype-2.13.0\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;DYNAREC;M64P_OSD;M64P_NETPLAY;M64P_PARALLEL;%(PreprocessorDefinitions) MultiThreadedDLL Level3 @@ -1078,7 +1082,7 @@ true - shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\lib\x86\SDL2.lib;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\lib\x86\SDL2_net.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\lib\x86\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\lib\x86\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\lib\x86\freetype.lib;%(AdditionalDependencies) + shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.26.3\lib\x86\SDL2.lib;..\..\..\mupen64plus-win32-deps\SDL2_net-2.2.0\lib\x86\SDL2_net.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.13\lib\x86\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.39\lib\x86\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.13.0\lib\x86\freetype.lib;%(AdditionalDependencies) true Windows true @@ -1090,7 +1094,7 @@ - ..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\include;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\include;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;%(AdditionalIncludeDirectories) + ..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.26.3\include;..\..\..\mupen64plus-win32-deps\SDL2_net-2.2.0\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.13\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.39\include;..\..\..\mupen64plus-win32-deps\freetype-2.13.0\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;DYNAREC;M64P_PARALLEL;M64P_OSD;M64P_NETPLAY;__x86_64__;%(PreprocessorDefinitions) MultiThreadedDLL Level3 @@ -1099,7 +1103,7 @@ true - shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\lib\x64\SDL2.lib;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\lib\x64\SDL2_net.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\lib\x64\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\lib\x64\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\lib\x64\freetype.lib;%(AdditionalDependencies) + shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.26.3\lib\x64\SDL2.lib;..\..\..\mupen64plus-win32-deps\SDL2_net-2.2.0\lib\x64\SDL2_net.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.13\lib\x64\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.39\lib\x64\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.13.0\lib\x64\freetype.lib;%(AdditionalDependencies) true Windows true @@ -1111,7 +1115,7 @@ - ..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\include;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\include;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;%(AdditionalIncludeDirectories) + ..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.26.3\include;..\..\..\mupen64plus-win32-deps\SDL2_net-2.2.0\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.13\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.39\include;..\..\..\mupen64plus-win32-deps\freetype-2.13.0\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;DYNAREC;M64P_OSD;M64P_NETPLAY;M64P_PARALLEL;NEW_DYNAREC=1;%(PreprocessorDefinitions) MultiThreadedDLL Level3 @@ -1120,7 +1124,7 @@ true - shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\lib\x86\SDL2.lib;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\lib\x86\SDL2_net.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\lib\x86\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\lib\x86\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\lib\x86\freetype.lib;%(AdditionalDependencies) + shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.26.3\lib\x86\SDL2.lib;..\..\..\mupen64plus-win32-deps\SDL2_net-2.2.0\lib\x86\SDL2_net.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.13\lib\x86\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.39\lib\x86\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.13.0\lib\x86\freetype.lib;%(AdditionalDependencies) true Windows true @@ -1132,7 +1136,7 @@ - ..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\include;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\include;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;%(AdditionalIncludeDirectories) + ..\..\src;..\..\subprojects\md5;..\..\subprojects\minizip;..\..\subprojects\oglft;..\..\subprojects\xxhash;..\..\..\mupen64plus-win32-deps\SDL2-2.26.3\include;..\..\..\mupen64plus-win32-deps\SDL2_net-2.2.0\include;..\..\..\mupen64plus-win32-deps\zlib-1.2.13\include;..\..\..\mupen64plus-win32-deps\libpng-1.6.39\include;..\..\..\mupen64plus-win32-deps\freetype-2.13.0\include;..\..\..\mupen64plus-win32-deps\opencv-3.0.0\include;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;DYNAREC;M64P_PARALLEL;M64P_OSD;M64P_NETPLAY;NEW_DYNAREC=2;__x86_64__;%(PreprocessorDefinitions) MultiThreadedDLL Level3 @@ -1141,7 +1145,7 @@ true - shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.0.10\lib\x64\SDL2.lib;..\..\..\mupen64plus-win32-deps\SDL2_net-2.0.1\lib\x64\SDL2_net.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.11\lib\x64\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.37\lib\x64\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.10.1\lib\x64\freetype.lib;%(AdditionalDependencies) + shell32.lib;opengl32.lib;glu32.lib;..\..\..\mupen64plus-win32-deps\SDL2-2.26.3\lib\x64\SDL2.lib;..\..\..\mupen64plus-win32-deps\SDL2_net-2.2.0\lib\x64\SDL2_net.lib;..\..\..\mupen64plus-win32-deps\zlib-1.2.13\lib\x64\zlib.lib;..\..\..\mupen64plus-win32-deps\libpng-1.6.39\lib\x64\libpng16.lib;..\..\..\mupen64plus-win32-deps\freetype-2.13.0\lib\x64\freetype.lib;%(AdditionalDependencies) true Windows true diff --git a/mupen64plus-core/projects/msvc/mupen64plus-core.vcxproj.filters b/mupen64plus-core/projects/msvc/mupen64plus-core.vcxproj.filters index 41e53b25..caf7af7f 100644 --- a/mupen64plus-core/projects/msvc/mupen64plus-core.vcxproj.filters +++ b/mupen64plus-core/projects/msvc/mupen64plus-core.vcxproj.filters @@ -360,6 +360,9 @@ device\controllers + + device\controllers + device\rdram @@ -704,6 +707,9 @@ device\controllers + + device\controllers + device\rdram diff --git a/mupen64plus-core/projects/unix/Makefile b/mupen64plus-core/projects/unix/Makefile index ff552a4e..962bc10a 100755 --- a/mupen64plus-core/projects/unix/Makefile +++ b/mupen64plus-core/projects/unix/Makefile @@ -99,25 +99,30 @@ endif ifneq ("$(filter arm%,$(HOST_CPU))","") ifeq ("$(filter arm%b,$(HOST_CPU))","") CPU := ARM - ARCH_DETECTED := 32BITS PIC ?= 1 NEW_DYNAREC := 1 - CFLAGS += -marm - ifneq ("$(filter armv5%,$(HOST_CPU))","") - CFLAGS += -DARMv5_ONLY - $(warning Using ARMv5_ONLY) - endif - ifneq ("$(filter armv6%,$(HOST_CPU))","") - CFLAGS += -DARMv5_ONLY - $(warning Using ARMv5_ONLY) - endif - ifeq ($(NEON), 1) - CFLAGS += -mfpu=neon -mfloat-abi=hard + ifneq ("$(filter arm64,$(HOST_CPU))","") + ARCH_DETECTED := 64BITS + $(warning Architecture "$(HOST_CPU)" not officially supported.) else - ifeq ($(VFP_HARD), 1) - CFLAGS += -mfpu=vfp -mfloat-abi=hard + ARCH_DETECTED := 32BITS + CFLAGS += -marm + ifneq ("$(filter armv5%,$(HOST_CPU))","") + CFLAGS += -DARMv5_ONLY + $(warning Using ARMv5_ONLY) + endif + ifneq ("$(filter armv6%,$(HOST_CPU))","") + CFLAGS += -DARMv5_ONLY + $(warning Using ARMv5_ONLY) + endif + ifeq ($(NEON), 1) + CFLAGS += -mfpu=neon -mfloat-abi=hard else - CFLAGS += -mfpu=vfp -mfloat-abi=softfp + ifeq ($(VFP_HARD), 1) + CFLAGS += -mfpu=vfp -mfloat-abi=hard + else + CFLAGS += -mfpu=vfp -mfloat-abi=softfp + endif endif endif endif @@ -175,9 +180,19 @@ ifeq ($(ARCH_DETECTED), 64BITS_32) ifeq ($(OS), FREEBSD) $(error Do not use the BITS=32 option with FreeBSD, use -m32 and -m elf_i386) endif - ifneq ($(OS),OSX) - CFLAGS += -m32 - LDFLAGS += -Wl,-m,elf_i386 + ifneq ($(OS), OSX) + ifeq ($(OS), MINGW) + LDFLAGS += -Wl,-m,i386pe + else + CFLAGS += -m32 + LDFLAGS += -Wl,-m,elf_i386 + endif + endif +endif + +ifeq ($(ARCH_DETECTED), 64BITS) + ifeq ($(OS), MINGW) + LDFLAGS += -Wl,-m,i386pep endif endif @@ -221,21 +236,30 @@ ifeq ($(OS), OSX) CXXFLAGS += '-stdlib=libc++' ifeq ($(CPU), X86) ifeq ($(ARCH_DETECTED), 64BITS) - CFLAGS += -pipe -arch x86_64 -mmacosx-version-min=10.7 -isysroot $(OSX_SDK_PATH) + CFLAGS += -pipe -arch x86_64 -mmacosx-version-min=10.9 -isysroot $(OSX_SDK_PATH) else - CFLAGS += -pipe -mmmx -msse -arch i686 -mmacosx-version-min=10.7 -isysroot $(OSX_SDK_PATH) + CFLAGS += -pipe -mmmx -msse -arch i686 -mmacosx-version-min=10.9 -isysroot $(OSX_SDK_PATH) ifneq ($(PROFILE), 1) CFLAGS += -fomit-frame-pointer endif LDFLAGS += -read_only_relocs suppress endif endif + ifeq ($(CPU), ARM) + # assembly compilation not yet supported on macOS with Apple Silicon + NO_ASM = 1 + CFLAGS += -pipe -arch arm64 -mmacosx-version-min=10.16 -isysroot $(OSX_SDK_PATH) + endif endif ifeq ($(OS), MINGW) TARGET = mupen64plus$(POSTFIX).dll - LDFLAGS += -Wl,-Bsymbolic -shared -Wl,-export-all-symbols - # only export api symbols - LDFLAGS += -Wl,-version-script,$(SRCDIR)/api/api_export.ver + ifeq ($(CC), clang) + LDFLAGS += -shared -Wl,-export-all-symbols + else + LDFLAGS += -Wl,-Bsymbolic -shared -Wl,-export-all-symbols + # only export api symbols + LDFLAGS += -Wl,-version-script,$(SRCDIR)/api/api_export.ver + endif LDLIBS += -lpthread ifeq ($(ARCH_DETECTED), 64BITS) ASFLAGS = -f win64 -d WIN64 @@ -336,6 +360,8 @@ ifeq ($(origin SDL_CFLAGS) $(origin SDL_LDLIBS), undefined undefined) ifeq ($(NETPLAY), 1) SDL_LDLIBS += -lSDL_net endif + # SDL1 doesn't support vulkan + VULKAN = 0 $(warning Using SDL 1.2 libraries) endif else @@ -409,6 +435,22 @@ ifeq ($(OSD), 1) LDLIBS += $(GLU_LDLIBS) endif +ifneq ($(OS), OSX) + ifneq ($(VULKAN), 0) + CFLAGS += -DVIDEXT_VULKAN + + ifeq ($(origin VULKAN_CFLAGS) $(origin VULKAN_LDLIBS), undefined undefined) + ifeq ($(shell $(PKG_CONFIG) --modversion vulkan 2>/dev/null),) + $(error No Vulkan development libraries found!) + endif + VULKAN_CFLAGS += $(shell $(PKG_CONFIG) --cflags vulkan) + VULKAN_LDLIBS += $(shell $(PKG_CONFIG) --libs vulkan) + endif + CFLAGS += $(VULKAN_CFLAGS) + LDLIBS += $(VULKAN_LDLIBS) + endif +endif + # set base program pointers and flags CC = $(CROSS_COMPILE)gcc CXX = $(CROSS_COMPILE)g++ @@ -514,6 +556,7 @@ SOURCE = \ $(SRCDIR)/device/cart/is_viewer.c \ $(SRCDIR)/device/cart/sram.c \ $(SRCDIR)/device/controllers/game_controller.c \ + $(SRCDIR)/device/controllers/vru_controller.c \ $(SRCDIR)/device/controllers/paks/biopak.c \ $(SRCDIR)/device/controllers/paks/mempak.c \ $(SRCDIR)/device/controllers/paks/rumblepak.c \ @@ -532,6 +575,7 @@ SOURCE = \ $(SRCDIR)/device/r4300/cached_interp.c \ $(SRCDIR)/device/r4300/cp0.c \ $(SRCDIR)/device/r4300/cp1.c \ + $(SRCDIR)/device/r4300/cp2.c \ $(SRCDIR)/device/r4300/idec.c \ $(SRCDIR)/device/r4300/interrupt.c \ $(SRCDIR)/device/r4300/pure_interp.c \ @@ -676,6 +720,10 @@ ifeq ($(KEYBINDINGS), 0) CFLAGS += -DNO_KEYBINDINGS endif +ifeq ($(ACCURATE_FPU), 1) + CFLAGS += -DACCURATE_FPU_BEHAVIOR +endif + ifeq ($(DEBUGGER), 1) SOURCE += \ $(SRCDIR)/debugger/dbg_debugger.c \ @@ -684,15 +732,19 @@ ifeq ($(DEBUGGER), 1) $(SRCDIR)/debugger/dbg_breakpoints.c LDLIBS += -lopcodes -lbfd - # UGLY libopcodes version check (we check for > 2.28) + # UGLY libopcodes/libbfd version check (we check for >= 2.28 and >= 2.39) LIBOPCODES_VERSION := $(shell $(STRINGS) --version | head -n1 | rev | cut -d ' ' -f1 | rev) LIBOPCODES_MAJOR := $(shell echo $(LIBOPCODES_VERSION) | cut -f1 -d.) LIBOPCODES_MINOR := $(shell echo $(LIBOPCODES_VERSION) | cut -f2 -d.) - LIBOPCODES_POINT := $(shell echo $(LIBOPCODES_VERSION) | cut -f3 -d.) - LIBOPCODES_GE_2_29 := $(shell [ $(LIBOPCODES_MAJOR) -gt 2 -o \( $(LIBOPCODES_MAJOR) -eq 2 -a $(LIBOPCODES_MINOR) -ge 29 \) -o \( $(LIBOPCODES_MAJOR) -eq 2 -a $(LIBOPCODES_MINOR) -eq 28 -a $(LIBOPCODES_POINT) -ge 1 \) ] && echo true) + LIBOPCODES_POINT := $(shell echo $(LIBOPCODES_VERSION) | cut -f3 -d. | sed 's/^$$/0/') + LIBOPCODES_GE_2_29 := $(shell [ $(LIBOPCODES_MAJOR) -gt 2 -o \( $(LIBOPCODES_MAJOR) -eq 2 -a $(LIBOPCODES_MINOR) -ge 28 \) -o \( $(LIBOPCODES_MAJOR) -eq 2 -a $(LIBOPCODES_MINOR) -eq 28 -a $(LIBOPCODES_POINT) -ge 1 \) ] && echo true) + LIBBFD_GE_2_39 := $(shell [ $(LIBOPCODES_MAJOR) -gt 2 -o \( $(LIBOPCODES_MAJOR) -eq 2 -a $(LIBOPCODES_MINOR) -ge 39 \) -o \( $(LIBOPCODES_MAJOR) -eq 2 -a $(LIBOPCODES_MINOR) -eq 39 -a $(LIBOPCODES_POINT) -ge 1 \) ] && echo true) ifeq ($(LIBOPCODES_GE_2_29),true) CFLAGS += -DUSE_LIBOPCODES_GE_2_29 endif + ifeq ($(LIBBFD_GE_2_39),true) + CFLAGS += -DUSE_LIBBFD_GE_2_39 + endif endif ifeq ($(OPENCV), 1) @@ -739,10 +791,12 @@ targets: @echo " NETPLAY=1 == Enable netplay functionality, requires SDL2_net" @echo " NEW_DYNAREC=1 == Replace dynamic recompiler with Ari64's experimental dynarec" @echo " KEYBINDINGS=0 == Disables the default keybindings" + @echo " ACCURATE_FPU=1 == Enables accurate FPU behavior (i.e correct cause bits)" @echo " OPENCV=1 == Enable OpenCV support" + @echo " VULKAN=0 == Disable vulkan support for the default video extension implementation" @echo " POSTFIX=name == String added to the name of the the build (default: '')" @echo " Install Options:" - @echo " PREFIX=path == install/uninstall prefix (default: /usr/local/)" + @echo " PREFIX=path == install/uninstall prefix (default: /usr/local)" @echo " SHAREDIR=path == path to install shared data files (default: PREFIX/share/mupen64plus)" @echo " LIBDIR=path == path to install core library (default: PREFIX/lib)" @echo " INCDIR=path == path to install core header files (default: PREFIX/include/mupen64plus)" @@ -779,7 +833,7 @@ uninstall: $(RM) "$(DESTDIR)$(SHAREDIR)/mupencheat.txt" clean: - $(RM) -r $(TARGET) $(SONAME) $(OBJDIR) $(SRCDIR)/asm_defines/asm_defines_* + $(RM) -r _obj $(OBJDIR) $(TARGET) $(SONAME) $(SRCDIR)/asm_defines/asm_defines_*.h # build dependency files CFLAGS += -MD -MP diff --git a/mupen64plus-core/src/api/api_export.ver b/mupen64plus-core/src/api/api_export.ver index 74512429..289adf20 100644 --- a/mupen64plus-core/src/api/api_export.ver +++ b/mupen64plus-core/src/api/api_export.ver @@ -69,6 +69,7 @@ VidExt_GL_SetAttribute; VidExt_GL_GetAttribute; VidExt_GL_SwapBuffers; VidExt_Init; +VidExt_InitWithRenderMode; VidExt_ListFullscreenModes; VidExt_ListFullscreenRates; VidExt_Quit; @@ -78,4 +79,6 @@ VidExt_SetVideoModeWithRate; VidExt_ToggleFullScreen; VidExt_ResizeWindow; VidExt_GL_GetDefaultFramebuffer; +VidExt_VK_GetSurface; +VidExt_VK_GetInstanceExtensions; local: *; }; diff --git a/mupen64plus-core/src/api/frontend.c b/mupen64plus-core/src/api/frontend.c index 61afbe1c..e063c92e 100644 --- a/mupen64plus-core/src/api/frontend.c +++ b/mupen64plus-core/src/api/frontend.c @@ -50,6 +50,7 @@ /* some local state variables */ static int l_CoreInit = 0; static int l_ROMOpen = 0; +static int l_DiskOpen = 0; static int l_CallerUsingSDL = 0; /* functions exported outside of libmupen64plus to front-end application */ @@ -132,9 +133,10 @@ EXPORT m64p_error CALL CoreDoCommand(m64p_command Command, int ParamInt, void *P case M64CMD_NOP: return M64ERR_SUCCESS; case M64CMD_ROM_OPEN: - if (g_EmulatorRunning || l_ROMOpen) + if (g_EmulatorRunning || l_DiskOpen || l_ROMOpen) return M64ERR_INVALID_STATE; - if (ParamPtr == NULL || ParamInt < 4096) + // ROM buffer size must be divisible by 4 to avoid out-of-bounds read in swap_copy_rom (v64/n64 formats) + if (ParamPtr == NULL || ParamInt < 4096 || ParamInt > CART_ROM_MAX_SIZE) return M64ERR_INPUT_ASSERT; rval = open_rom((const unsigned char *) ParamPtr, ParamInt); if (rval == M64ERR_SUCCESS) @@ -150,14 +152,34 @@ EXPORT m64p_error CALL CoreDoCommand(m64p_command Command, int ParamInt, void *P cheat_delete_all(&g_cheat_ctx); cheat_uninit(&g_cheat_ctx); return close_rom(); + case M64CMD_DISK_OPEN: + if (g_EmulatorRunning || l_DiskOpen || l_ROMOpen) + return M64ERR_INVALID_STATE; + if (ParamPtr != NULL) + return M64ERR_INPUT_INVALID; + rval = open_disk(); + if (rval == M64ERR_SUCCESS) + { + l_DiskOpen = 1; + ScreenshotRomOpen(); + cheat_init(&g_cheat_ctx); + } + return rval; + case M64CMD_DISK_CLOSE: + if (g_EmulatorRunning || !l_DiskOpen) + return M64ERR_INVALID_STATE; + l_DiskOpen = 0; + cheat_delete_all(&g_cheat_ctx); + cheat_uninit(&g_cheat_ctx); + return close_disk(); case M64CMD_PIF_OPEN: if (g_EmulatorRunning) return M64ERR_INVALID_STATE; - if (ParamPtr == NULL || ParamInt != 2048) + if (ParamPtr == NULL || ParamInt < 1984 || ParamInt > 2048 || ParamInt % 4 != 0) return M64ERR_INPUT_ASSERT; return open_pif((const unsigned char *) ParamPtr, ParamInt); case M64CMD_ROM_GET_HEADER: - if (!l_ROMOpen) + if (!l_ROMOpen && !l_DiskOpen) return M64ERR_INVALID_STATE; if (ParamPtr == NULL) return M64ERR_INPUT_ASSERT; @@ -173,7 +195,7 @@ EXPORT m64p_error CALL CoreDoCommand(m64p_command Command, int ParamInt, void *P } return M64ERR_SUCCESS; case M64CMD_ROM_GET_SETTINGS: - if (!l_ROMOpen) + if (!l_ROMOpen && !l_DiskOpen) return M64ERR_INVALID_STATE; if (ParamPtr == NULL) return M64ERR_INPUT_ASSERT; @@ -182,7 +204,7 @@ EXPORT m64p_error CALL CoreDoCommand(m64p_command Command, int ParamInt, void *P memcpy(ParamPtr, &ROM_SETTINGS, ParamInt); return M64ERR_SUCCESS; case M64CMD_ROM_SET_SETTINGS: - if (g_EmulatorRunning || !l_ROMOpen) + if (g_EmulatorRunning || (!l_ROMOpen && !l_DiskOpen)) return M64ERR_INVALID_STATE; if (ParamPtr == NULL) return M64ERR_INPUT_ASSERT; @@ -191,7 +213,7 @@ EXPORT m64p_error CALL CoreDoCommand(m64p_command Command, int ParamInt, void *P memcpy(&ROM_SETTINGS, ParamPtr, ParamInt); return M64ERR_SUCCESS; case M64CMD_EXECUTE: - if (g_EmulatorRunning || !l_ROMOpen) + if (g_EmulatorRunning || (!l_ROMOpen && !l_DiskOpen)) return M64ERR_INVALID_STATE; /* the main_run() function will not return until the player has quit the game */ rval = main_run(); @@ -361,6 +383,7 @@ EXPORT m64p_error CALL CoreGetRomSettings(m64p_rom_settings *RomSettings, int Ro RomSettings->countperop = entry->countperop; RomSettings->savetype = entry->savetype; RomSettings->sidmaduration = entry->sidmaduration; + RomSettings->aidmamodifier = entry->aidmamodifier; return M64ERR_SUCCESS; } diff --git a/mupen64plus-core/src/api/m64p_plugin.h b/mupen64plus-core/src/api/m64p_plugin.h index 0f5a95d3..76671fd3 100644 --- a/mupen64plus-core/src/api/m64p_plugin.h +++ b/mupen64plus-core/src/api/m64p_plugin.h @@ -136,10 +136,15 @@ typedef struct { void (*CheckInterrupts)(void); } AUDIO_INFO; +/*** Controller types ****/ +#define CONT_TYPE_STANDARD 0 +#define CONT_TYPE_VRU 1 + typedef struct { int Present; int RawData; int Plugin; + int Type; } CONTROL; typedef union { @@ -260,6 +265,11 @@ typedef void (*ptr_ReadController)(int Control, unsigned char *Command); typedef void (*ptr_SDL_KeyDown)(int keymod, int keysym); typedef void (*ptr_SDL_KeyUp)(int keymod, int keysym); typedef void (*ptr_RenderCallback)(void); +typedef void (*ptr_SendVRUWord)(uint16_t length, uint16_t *word, uint8_t lang); +typedef void (*ptr_SetMicState)(int state); +typedef void (*ptr_ReadVRUResults)(uint16_t *error_flags, uint16_t *num_results, uint16_t *mic_level, uint16_t *voice_level, uint16_t *voice_length, uint16_t *matches); +typedef void (*ptr_ClearVRUWords)(uint8_t length); +typedef void (*ptr_SetVRUWordMask)(uint8_t length, uint8_t *mask); #if defined(M64P_PLUGIN_PROTOTYPES) EXPORT void CALL ControllerCommand(int Control, unsigned char *Command); EXPORT void CALL GetKeys(int Control, BUTTONS *Keys); @@ -268,6 +278,11 @@ EXPORT void CALL ReadController(int Control, unsigned char *Command); EXPORT void CALL SDL_KeyDown(int keymod, int keysym); EXPORT void CALL SDL_KeyUp(int keymod, int keysym); EXPORT void CALL RenderCallback(void); +EXPORT void CALL SendVRUWord(uint16_t length, uint16_t *word, uint8_t lang); +EXPORT void CALL SetMicState(int state); +EXPORT void CALL ReadVRUResults(uint16_t *error_flags, uint16_t *num_results, uint16_t *mic_level, uint16_t *voice_level, uint16_t *voice_length, uint16_t *matches); +EXPORT void CALL ClearVRUWords(uint8_t length); +EXPORT void CALL SetVRUWordMask(uint8_t length, uint8_t *mask); #endif /* RSP plugin function pointers */ diff --git a/mupen64plus-core/src/api/m64p_types.h b/mupen64plus-core/src/api/m64p_types.h index c3a1fc21..088f0117 100644 --- a/mupen64plus-core/src/api/m64p_types.h +++ b/mupen64plus-core/src/api/m64p_types.h @@ -137,7 +137,8 @@ typedef enum { M64CORE_AUDIO_MUTE, M64CORE_INPUT_GAMESHARK, M64CORE_STATE_LOADCOMPLETE, - M64CORE_STATE_SAVECOMPLETE + M64CORE_STATE_SAVECOMPLETE, + M64CORE_SCREENSHOT_CAPTURED, } m64p_core_param; typedef enum { @@ -168,7 +169,9 @@ typedef enum { M64CMD_NETPLAY_GET_VERSION, M64CMD_NETPLAY_CLOSE, M64CMD_PIF_OPEN, - M64CMD_ROM_SET_SETTINGS + M64CMD_ROM_SET_SETTINGS, + M64CMD_DISK_OPEN, + M64CMD_DISK_CLOSE } m64p_command; typedef struct { @@ -196,6 +199,12 @@ typedef struct { */ char* (*get_gb_cart_ram)(void* cb_data, int controller_num); + /* Allow the frontend to know what DD IPL ROM region file to load + * cb_data: points to frontend-defined callback data. + * region: a region from m64p_system_type + */ + void (*set_dd_rom_region)(void* cb_data, uint8_t region); + /* Allow the frontend to specify the DD IPL ROM file to load * cb_data: points to frontend-defined callback data. * Returns a NULL-terminated string owned by the core specifying the DD IPL ROM filename to load @@ -235,6 +244,14 @@ typedef enum SAVETYPE_NONE = 5, } m64p_rom_save_type; +typedef enum +{ + DDREGION_JAPAN = 0, + DDREGION_US = 1, + DDREGION_DEV = 2, + DDREGION_UNKNOWN = 3, +} m64p_disk_region; + typedef struct { uint8_t init_PI_BSB_DOM1_LAT_REG; /* 0x00 */ @@ -251,7 +268,8 @@ typedef struct uint32_t unknown; /* 0x34 */ uint32_t Manufacturer_ID; /* 0x38 */ uint16_t Cartridge_ID; /* 0x3C - Game serial number */ - uint16_t Country_code; /* 0x3E */ + uint8_t Country_code; /* 0x3E */ + uint8_t Version; /* 0x3F */ } m64p_rom_header; typedef struct @@ -268,6 +286,7 @@ typedef struct unsigned char disableextramem; /* 0 - No, 1 - Yes boolean for disabling 4MB expansion RAM pack */ unsigned int countperop; /* Number of CPU cycles per instruction. */ unsigned int sidmaduration; /* Default SI DMA duration */ + unsigned int aidmamodifier; /* Percentage modifier for AI DMA duration */ } m64p_rom_settings; /* ----------------------------------------- */ @@ -413,6 +432,11 @@ typedef enum { M64P_GL_CONTEXT_PROFILE_ES } m64p_GLContextType; +typedef enum { + M64P_RENDER_OPENGL = 0, + M64P_RENDER_VULKAN +} m64p_render_mode; + typedef struct { unsigned int Functions; m64p_error (*VidExtFuncInit)(void); @@ -429,6 +453,9 @@ typedef struct { m64p_error (*VidExtFuncToggleFS)(void); m64p_error (*VidExtFuncResizeWindow)(int, int); uint32_t (*VidExtFuncGLGetDefaultFramebuffer)(void); + m64p_error (*VidExtFuncInitWithRenderMode)(m64p_render_mode); + m64p_error (*VidExtFuncVKGetSurface)(void**, void*); + m64p_error (*VidExtFuncVKGetInstanceExtensions)(const char**[], uint32_t*); } m64p_video_extension_functions; #endif /* define M64P_TYPES_H */ diff --git a/mupen64plus-core/src/api/m64p_vidext.h b/mupen64plus-core/src/api/m64p_vidext.h index abc930bd..4ab5a96e 100644 --- a/mupen64plus-core/src/api/m64p_vidext.h +++ b/mupen64plus-core/src/api/m64p_vidext.h @@ -44,6 +44,19 @@ typedef m64p_error (*ptr_VidExt_Init)(void); EXPORT m64p_error CALL VidExt_Init(void); #endif +/* VidExt_InitWithRenderMode() + * + * This function should be called from within the InitiateGFX() video plugin + * function call with the specified rendering mode (OpenGL or Vulkan). + * The default SDL implementation of this function simply calls + * SDL_InitSubSystem(SDL_INIT_VIDEO). It does not open a rendering window or + * switch video modes. + */ +typedef m64p_error (*ptr_VidExt_InitWithRenderMode)(m64p_render_mode); +#if defined(M64P_CORE_PROTOTYPES) +EXPORT m64p_error CALL VidExt_InitWithRenderMode(m64p_render_mode); +#endif + /* VidExt_Quit() * * This function closes any open rendering window and shuts down the video @@ -181,6 +194,26 @@ typedef uint32_t (*ptr_VidExt_GL_GetDefaultFramebuffer)(void); EXPORT uint32_t CALL VidExt_GL_GetDefaultFramebuffer(void); #endif +/* VidExt_VK_GetSurface() + * + * This functions gives out a vulkan surface + */ +typedef m64p_error (*ptr_VidExt_VK_GetSurface)(void**, void*); +#if defined(M64P_CORE_PROTOTYPES) +EXPORT m64p_error CALL VidExt_VK_GetSurface(void**, void*); +#endif + +/* VidExt_VK_GetInstanceExtensions() + * + * This functions gives out a list of supported vulkan + * extensions, and the caller will be given the amount + * of strings stored in the list + */ +typedef m64p_error (*ptr_VidExt_VK_GetInstanceExtensions)(const char**[], uint32_t*); +#if defined(M64P_CORE_PROTOTYPES) +EXPORT m64p_error CALL VidExt_VK_GetInstanceExtensions(const char**[], uint32_t*); +#endif + #ifdef __cplusplus } #endif diff --git a/mupen64plus-core/src/api/vidext.c b/mupen64plus-core/src/api/vidext.c index 66293a2e..1bc4a331 100644 --- a/mupen64plus-core/src/api/vidext.c +++ b/mupen64plus-core/src/api/vidext.c @@ -42,12 +42,16 @@ #endif /* local variables */ -static m64p_video_extension_functions l_ExternalVideoFuncTable = {14, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; +static m64p_video_extension_functions l_ExternalVideoFuncTable = {17, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; static int l_VideoExtensionActive = 0; static int l_VideoOutputActive = 0; static int l_Fullscreen = 0; static int l_SwapControl = 0; +static m64p_render_mode l_RenderMode = M64P_RENDER_OPENGL; static SDL_Surface *l_pScreen = NULL; +#ifdef VIDEXT_VULKAN +static const char** l_VulkanExtensionNames = NULL; +#endif /* global function for use by frontend.c */ m64p_error OverrideVideoFunctions(m64p_video_extension_functions *VideoFunctionStruct) @@ -55,11 +59,12 @@ m64p_error OverrideVideoFunctions(m64p_video_extension_functions *VideoFunctionS /* check input data */ if (VideoFunctionStruct == NULL) return M64ERR_INPUT_ASSERT; - if (VideoFunctionStruct->Functions < 14) + if (VideoFunctionStruct->Functions < 17) return M64ERR_INPUT_INVALID; /* disable video extension if any of the function pointers are NULL */ if (VideoFunctionStruct->VidExtFuncInit == NULL || + VideoFunctionStruct->VidExtFuncInitWithRenderMode == NULL || VideoFunctionStruct->VidExtFuncQuit == NULL || VideoFunctionStruct->VidExtFuncListModes == NULL || VideoFunctionStruct->VidExtFuncListRates == NULL || @@ -72,10 +77,12 @@ m64p_error OverrideVideoFunctions(m64p_video_extension_functions *VideoFunctionS VideoFunctionStruct->VidExtFuncSetCaption == NULL || VideoFunctionStruct->VidExtFuncToggleFS == NULL || VideoFunctionStruct->VidExtFuncResizeWindow == NULL || - VideoFunctionStruct->VidExtFuncGLGetDefaultFramebuffer == NULL) + VideoFunctionStruct->VidExtFuncGLGetDefaultFramebuffer == NULL || + VideoFunctionStruct->VidExtFuncVKGetSurface == NULL || + VideoFunctionStruct->VidExtFuncVKGetInstanceExtensions == NULL) { - l_ExternalVideoFuncTable.Functions = 14; - memset(&l_ExternalVideoFuncTable.VidExtFuncInit, 0, 14 * sizeof(void *)); + l_ExternalVideoFuncTable.Functions = 17; + memset(&l_ExternalVideoFuncTable.VidExtFuncInit, 0, 17 * sizeof(void *)); l_VideoExtensionActive = 0; return M64ERR_SUCCESS; } @@ -103,10 +110,32 @@ EXPORT m64p_error CALL VidExt_Init(void) if (l_VideoExtensionActive) return (*l_ExternalVideoFuncTable.VidExtFuncInit)(); + /* redirect to VidExt_InitWithRenderMode with OpenGL render mode */ + return VidExt_InitWithRenderMode(M64P_RENDER_OPENGL); +} + +EXPORT m64p_error CALL VidExt_InitWithRenderMode(m64p_render_mode RenderMode) +{ + /* call video extension override if necessary */ + if (l_VideoExtensionActive) + return (*l_ExternalVideoFuncTable.VidExtFuncInitWithRenderMode)(RenderMode); + + /* set global render mode */ +#ifdef VIDEXT_VULKAN + l_RenderMode = RenderMode; +#endif + #if SDL_VERSION_ATLEAST(2,0,0) SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); - /* retrieve default swap interval/VSync */ - l_SwapControl = SDL_GL_GetSwapInterval(); + /* retrieve default swap interval/VSync */ + if (RenderMode == M64P_RENDER_OPENGL) { + l_SwapControl = SDL_GL_GetSwapInterval(); + } +#endif + +#if SDL_VERSION_ATLEAST(2,24,0) + /* fix DPI scaling issues on Windows */ + SDL_SetHint(SDL_HINT_WINDOWS_DPI_AWARENESS, "permonitorv2"); #endif if (SDL_InitSubSystem(SDL_INIT_VIDEO) == -1) @@ -115,6 +144,20 @@ EXPORT m64p_error CALL VidExt_Init(void) return M64ERR_SYSTEM_FAIL; } + /* attempt to load vulkan library */ + if (RenderMode == M64P_RENDER_VULKAN) + { +#ifdef VIDEXT_VULKAN + if (SDL_Vulkan_LoadLibrary(NULL) == -1) + { + DebugMessage(M64MSG_ERROR, "SDL_Vulkan_LoadLibrary failed: %s", SDL_GetError()); + return M64ERR_SYSTEM_FAIL; + } +#else + return M64ERR_UNSUPPORTED; +#endif + } + return M64ERR_SUCCESS; } @@ -138,6 +181,15 @@ EXPORT m64p_error CALL VidExt_Quit(void) SDL_ShowCursor(SDL_ENABLE); #if SDL_VERSION_ATLEAST(2,0,0) SDL2_DestroyWindow(); +#endif +#ifdef VIDEXT_VULKAN + if (l_RenderMode == M64P_RENDER_VULKAN) { + SDL_Vulkan_UnloadLibrary(); + } + if (l_VulkanExtensionNames != NULL) { + free(l_VulkanExtensionNames); + l_VulkanExtensionNames = NULL; + } #endif SDL_QuitSubSystem(SDL_INIT_VIDEO); l_pScreen = NULL; @@ -267,15 +319,24 @@ EXPORT m64p_error CALL VidExt_SetVideoMode(int Width, int Height, int BitsPerPix return M64ERR_NOT_INIT; /* Get SDL video flags to use */ - if (ScreenMode == M64VIDEO_WINDOWED) + if (l_RenderMode == M64P_RENDER_OPENGL) { videoFlags = SDL_OPENGL; + } +#ifdef VIDEXT_VULKAN + else + { + videoFlags = SDL_VULKAN; + } +#endif + if (ScreenMode == M64VIDEO_WINDOWED) + { if (Flags & M64VIDEOFLAG_SUPPORT_RESIZING) videoFlags |= SDL_RESIZABLE; } else if (ScreenMode == M64VIDEO_FULLSCREEN) { - videoFlags = SDL_OPENGL | SDL_FULLSCREEN; + videoFlags |= SDL_FULLSCREEN; } else { @@ -309,7 +370,8 @@ EXPORT m64p_error CALL VidExt_SetVideoMode(int Width, int Height, int BitsPerPix #if SDL_VERSION_ATLEAST(2,0,0) /* set swap interval/VSync */ - if (SDL_GL_SetSwapInterval(l_SwapControl) != 0) + if (l_RenderMode == M64P_RENDER_OPENGL && + SDL_GL_SetSwapInterval(l_SwapControl) != 0) { DebugMessage(M64MSG_ERROR, "SDL swap interval (VSync) set failed: %s", SDL_GetError()); } @@ -341,7 +403,7 @@ EXPORT m64p_error CALL VidExt_SetVideoModeWithRate(int Width, int Height, int Re #if SDL_VERSION_ATLEAST(2,0,0) if (!SDL_WasInit(SDL_INIT_VIDEO) || !SDL_VideoWindow) return M64ERR_NOT_INIT; - + int videoFlags = 0; int display = GetVideoDisplay(); int modeCount = SDL_GetNumDisplayModes(display); @@ -453,7 +515,13 @@ EXPORT m64p_error CALL VidExt_ResizeWindow(int Width, int Height) } /* Get SDL video flags to use */ - videoFlags = SDL_OPENGL | SDL_RESIZABLE; + if (l_RenderMode == M64P_RENDER_OPENGL) + videoFlags = SDL_OPENGL; +#ifdef VIDEXT_VULKAN + else + videoFlags = SDL_VULKAN; +#endif + videoFlags |= SDL_RESIZABLE; if ((videoInfo = SDL_GetVideoInfo()) == NULL) { DebugMessage(M64MSG_ERROR, "SDL_GetVideoInfo query failed: %s", SDL_GetError()); @@ -526,6 +594,10 @@ EXPORT m64p_error CALL VidExt_ToggleFullScreen(void) StateChanged(M64CORE_VIDEO_MODE, l_Fullscreen ? M64VIDEO_FULLSCREEN : M64VIDEO_WINDOWED); return M64ERR_SUCCESS; } + else + { + DebugMessage(M64MSG_ERROR, "SDL_WM_ToggleFullScreen failed: %s", SDL_GetError()); + } return M64ERR_SYSTEM_FAIL; } @@ -536,6 +608,9 @@ EXPORT m64p_function CALL VidExt_GL_GetProcAddress(const char* Proc) if (l_VideoExtensionActive) return (*l_ExternalVideoFuncTable.VidExtFuncGLGetProc)(Proc); + if (l_RenderMode != M64P_RENDER_OPENGL) + return NULL; + if (!SDL_WasInit(SDL_INIT_VIDEO)) return NULL; @@ -580,6 +655,9 @@ EXPORT m64p_error CALL VidExt_GL_SetAttribute(m64p_GLattr Attr, int Value) if (l_VideoExtensionActive) return (*l_ExternalVideoFuncTable.VidExtFuncGLSetAttr)(Attr, Value); + if (l_RenderMode != M64P_RENDER_OPENGL) + return M64ERR_INVALID_STATE; + if (!SDL_WasInit(SDL_INIT_VIDEO)) return M64ERR_NOT_INIT; @@ -634,6 +712,9 @@ EXPORT m64p_error CALL VidExt_GL_GetAttribute(m64p_GLattr Attr, int *pValue) if (l_VideoExtensionActive) return (*l_ExternalVideoFuncTable.VidExtFuncGLGetAttr)(Attr, pValue); + if (l_RenderMode != M64P_RENDER_OPENGL) + return M64ERR_INVALID_STATE; + if (!SDL_WasInit(SDL_INIT_VIDEO)) return M64ERR_NOT_INIT; @@ -686,6 +767,9 @@ EXPORT m64p_error CALL VidExt_GL_SwapBuffers(void) if (l_VideoExtensionActive) return (*l_ExternalVideoFuncTable.VidExtFuncGLSwapBuf)(); + if (l_RenderMode != M64P_RENDER_OPENGL) + return M64ERR_INVALID_STATE; + if (!SDL_WasInit(SDL_INIT_VIDEO)) return M64ERR_NOT_INIT; @@ -700,3 +784,72 @@ EXPORT uint32_t CALL VidExt_GL_GetDefaultFramebuffer(void) return 0; } + +EXPORT m64p_error CALL VidExt_VK_GetSurface(void** Surface, void* Instance) +{ + if (l_VideoExtensionActive) + return (*l_ExternalVideoFuncTable.VidExtFuncVKGetSurface)(Surface, Instance); + +#ifdef VIDEXT_VULKAN + VkSurfaceKHR vulkanSurface = VK_NULL_HANDLE; + + if (l_RenderMode != M64P_RENDER_VULKAN) + return M64ERR_INVALID_STATE; + + if (!SDL_WasInit(SDL_INIT_VIDEO) || !SDL_VideoWindow) + return M64ERR_NOT_INIT; + + if (SDL_Vulkan_CreateSurface(SDL_VideoWindow, (VkInstance)Instance, &vulkanSurface) == SDL_FALSE) { + DebugMessage(M64MSG_ERROR, "SDL_Vulkan_CreateSurface failed: %s", SDL_GetError()); + return M64ERR_SYSTEM_FAIL; + } + + *Surface = (void*)vulkanSurface; + return M64ERR_SUCCESS; +#else + return M64ERR_UNSUPPORTED; +#endif +} + +EXPORT m64p_error CALL VidExt_VK_GetInstanceExtensions(const char** Extensions[], uint32_t* NumExtensions) +{ + if (l_VideoExtensionActive) + return (*l_ExternalVideoFuncTable.VidExtFuncVKGetInstanceExtensions)(Extensions, NumExtensions); + +#ifdef VIDEXT_VULKAN + if (l_RenderMode != M64P_RENDER_VULKAN) + return M64ERR_INVALID_STATE; + + if (!SDL_WasInit(SDL_INIT_VIDEO)) + return M64ERR_NOT_INIT; + + unsigned int extensionCount = 0; + if (SDL_Vulkan_GetInstanceExtensions(NULL, &extensionCount, NULL) == SDL_FALSE) { + DebugMessage(M64MSG_ERROR, "SDL_Vulkan_GetInstanceExtensions failed: %s", SDL_GetError()); + return M64ERR_SYSTEM_FAIL; + } + + /* ensure names have been freed before allocating it again */ + if (l_VulkanExtensionNames != NULL) { + free(l_VulkanExtensionNames); + l_VulkanExtensionNames = NULL; + } + + l_VulkanExtensionNames = malloc(sizeof(const char*) * extensionCount); + if (l_VulkanExtensionNames == NULL) { + DebugMessage(M64MSG_ERROR, "malloc failed"); + return M64ERR_SYSTEM_FAIL; + } + + if (SDL_Vulkan_GetInstanceExtensions(NULL, &extensionCount, l_VulkanExtensionNames) == SDL_FALSE) { + DebugMessage(M64MSG_ERROR, "SDL_Vulkan_GetInstanceExtensions failed: %s", SDL_GetError()); + return M64ERR_SYSTEM_FAIL; + } + + *NumExtensions = extensionCount; + *Extensions = l_VulkanExtensionNames; + return M64ERR_SUCCESS; +#else + return M64ERR_UNSUPPORTED; +#endif +} diff --git a/mupen64plus-core/src/api/vidext_sdl2_compat.h b/mupen64plus-core/src/api/vidext_sdl2_compat.h index 06d778dd..20e2959b 100644 --- a/mupen64plus-core/src/api/vidext_sdl2_compat.h +++ b/mupen64plus-core/src/api/vidext_sdl2_compat.h @@ -62,6 +62,7 @@ typedef struct SDL_VideoInfo #define SDL_RESIZABLE 0x01000000 #define SDL_NOFRAME 0x02000000 #define SDL_OPENGL 0x04000000 +#define SDL_VULKAN 0x10000000 #define SDL_HWSURFACE 0x08000001 /**< \note Not used */ #define SDL_BUTTON_WHEELUP 4 @@ -198,7 +199,11 @@ SDL_WM_ToggleFullScreen(SDL_Surface * surface) int window_w; int window_h; - if (!SDL_PublicSurface) { + if ( +#ifdef VIDEXT_VULKAN + (SDL_VideoFlags & SDL_VULKAN && !SDL_VideoWindow) || +#endif + (SDL_VideoFlags & SDL_OPENGL && !SDL_PublicSurface)) { SDL_SetError("SDL_SetVideoMode() hasn't been called"); return 0; } @@ -208,12 +213,16 @@ SDL_WM_ToggleFullScreen(SDL_Surface * surface) if (SDL_SetWindowFullscreen(SDL_VideoWindow, 0) < 0) { return 0; } - SDL_PublicSurface->flags &= ~SDL_FULLSCREEN; + if (SDL_VideoFlags & SDL_OPENGL) { + SDL_PublicSurface->flags &= ~SDL_FULLSCREEN; + } } else { if (SDL_SetWindowFullscreen(SDL_VideoWindow, 1) < 0) { return 0; } - SDL_PublicSurface->flags |= SDL_FULLSCREEN; + if (SDL_VideoFlags & SDL_OPENGL) { + SDL_PublicSurface->flags |= SDL_FULLSCREEN; + } } /* Center the public surface in the window surface */ @@ -233,7 +242,11 @@ SDL_ResizeVideoMode(int width, int height, int bpp, Uint32 flags) int w, h; /* We can't resize something we don't have... */ - if (!SDL_PublicSurface) { + if ( +#ifdef VIDEXT_VULKAN + (SDL_VideoFlags & SDL_VULKAN && !SDL_VideoWindow) || +#endif + (SDL_VideoFlags & SDL_OPENGL && !SDL_PublicSurface)) { return -1; } @@ -415,14 +428,16 @@ SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags) } #ifndef USE_GLES - if (l_ForceCompatibilityContext) + if (flags & SDL_OPENGL && l_ForceCompatibilityContext) { SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); } #else // !USE_GLES - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + if (flags & SDL_OPENGL) { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + } #endif // !USE_GLES /* Create a new window */ @@ -433,6 +448,11 @@ SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags) if (flags & SDL_OPENGL) { window_flags |= SDL_WINDOW_OPENGL; } +#ifdef VIDEXT_VULKAN + if (flags & SDL_VULKAN) { + window_flags |= SDL_WINDOW_VULKAN; + } +#endif if (flags & SDL_RESIZABLE) { window_flags |= SDL_WINDOW_RESIZABLE; } @@ -456,6 +476,11 @@ SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags) if ((window_flags & SDL_WINDOW_OPENGL) && (flags & SDL_OPENGL)) { surface_flags |= SDL_OPENGL; } +#ifdef VIDEXT_VULKAN + if ((window_flags & SDL_WINDOW_VULKAN) && (flags & SDL_VULKAN)) { + surface_flags |= SDL_VULKAN; + } +#endif if (window_flags & SDL_WINDOW_RESIZABLE) { surface_flags |= SDL_RESIZABLE; } @@ -474,8 +499,14 @@ SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags) if (SDL_GL_MakeCurrent(SDL_VideoWindow, SDL_VideoContext) < 0) { return NULL; } + + /* Pitch: size of of line in bytes */ + /* Add 7 to bpp before division, to ensure correct rounding towards infinity + * in cases where bits per pixel do not cleanly divide by 8 (such as 15) + */ + int pitch = (bpp + 7) / 8 * width; SDL_VideoSurface = - SDL_CreateRGBSurfaceFrom(NULL, width, height, bpp, 0, 0, 0, 0, 0); + SDL_CreateRGBSurfaceFrom(NULL, width, height, bpp, pitch, 0, 0, 0, 0); if (!SDL_VideoSurface) { return NULL; } @@ -483,6 +514,13 @@ SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags) SDL_PublicSurface = SDL_VideoSurface; return SDL_PublicSurface; } +#ifdef VIDEXT_VULKAN + else if (flags & SDL_VULKAN) { + /* Vulkan doesn't have a video surface, + * so just return a stub */ + return (SDL_Surface*)0x1; + } +#endif /* We're finally done! */ return NULL; diff --git a/mupen64plus-core/src/asm_defines/asm_defines.c b/mupen64plus-core/src/asm_defines/asm_defines.c index 6ac22805..4ea4fd12 100644 --- a/mupen64plus-core/src/asm_defines/asm_defines.c +++ b/mupen64plus-core/src/asm_defines/asm_defines.c @@ -132,8 +132,8 @@ DEFINE(new_dynarec_hot_state, pending_exception); DEFINE(new_dynarec_hot_state, pcaddr); DEFINE(new_dynarec_hot_state, stop); DEFINE(new_dynarec_hot_state, invc_ptr); -DEFINE(new_dynarec_hot_state, fcr0); -DEFINE(new_dynarec_hot_state, fcr31); +DEFINE(new_dynarec_hot_state, cp1_fcr0); +DEFINE(new_dynarec_hot_state, cp1_fcr31); DEFINE(new_dynarec_hot_state, regs); DEFINE(new_dynarec_hot_state, hi); DEFINE(new_dynarec_hot_state, lo); diff --git a/mupen64plus-core/src/backends/api/joybus.h b/mupen64plus-core/src/backends/api/joybus.h index 8528a11d..42b2e16f 100644 --- a/mupen64plus-core/src/backends/api/joybus.h +++ b/mupen64plus-core/src/backends/api/joybus.h @@ -35,6 +35,11 @@ enum joybus_commands JCMD_AF_RTC_STATUS = 0x06, JCMD_AF_RTC_READ = 0x07, JCMD_AF_RTC_WRITE = 0x08, + JCMD_VRU_READ = 0x09, + JCMD_VRU_WRITE = 0x0A, + JCMD_VRU_READ_STATUS = 0x0B, + JCMD_VRU_WRITE_CONFIG = 0x0C, + JCMD_VRU_WRITE_INIT = 0x0D, JCMD_RESET = 0xff, }; @@ -44,6 +49,7 @@ enum joybus_device_types JDT_JOY_ABS_COUNTERS = 0x0001, /* joystick with absolute coordinates */ JDT_JOY_REL_COUNTERS = 0x0002, /* joystick with relative coordinates (= mouse) */ JDT_JOY_PORT = 0x0004, /* has port for external paks */ + JDT_VRU = 0x0100, /* VRU */ JDT_AF_RTC = 0x1000, /* RTC */ JDT_EEPROM_4K = 0x8000, /* 4k EEPROM */ JDT_EEPROM_16K = 0xc000, /* 16k EEPROM */ diff --git a/mupen64plus-core/src/backends/plugins_compat/input_plugin_compat.c b/mupen64plus-core/src/backends/plugins_compat/input_plugin_compat.c index 07088856..8ebdd059 100644 --- a/mupen64plus-core/src/backends/plugins_compat/input_plugin_compat.c +++ b/mupen64plus-core/src/backends/plugins_compat/input_plugin_compat.c @@ -79,11 +79,11 @@ static m64p_error input_plugin_get_input(void* opaque, uint32_t* input_) if (input.getKeys) input.getKeys(netplay_controller, &keys); - netplay_set_plugin(cin_compat->control_id, Controls[netplay_controller].Plugin); Controls[netplay_controller].Plugin = plugin; Controls[netplay_controller].Present = present; - cin_compat->last_input = keys.Value; //disable pak switching for netplay } + cin_compat->last_input = keys.Value; //disable pak switching for netplay + cin_compat->last_pak_type = Controls[cin_compat->control_id].Plugin; //disable pak switching for netplay } /* return an error if controller is not plugged */ diff --git a/mupen64plus-core/src/debugger/dbg_memory.c b/mupen64plus-core/src/debugger/dbg_memory.c index e98bf081..39ec9f82 100644 --- a/mupen64plus-core/src/debugger/dbg_memory.c +++ b/mupen64plus-core/src/debugger/dbg_memory.c @@ -97,9 +97,23 @@ static int read_memory_func(bfd_vma memaddr, bfd_byte *myaddr, unsigned int leng return (0); } +#ifdef USE_LIBBFD_GE_2_39 +static int fprintf_styled_nop(void *out __attribute__((unused)), + enum disassembler_style s __attribute__((unused)), + const char *fmt __attribute__((unused)), + ...) +{ + return 0; +} +#endif + void init_host_disassembler(void) { +#ifdef USE_LIBBFD_GE_2_39 + INIT_DISASSEMBLE_INFO(dis_info, stderr, process_opcode_out, fprintf_styled_nop); +#else INIT_DISASSEMBLE_INFO(dis_info, stderr, process_opcode_out); +#endif dis_info.fprintf_func = (fprintf_ftype) process_opcode_out; dis_info.stream = stderr; dis_info.bytes_per_line=1; diff --git a/mupen64plus-core/src/device/cart/cart.c b/mupen64plus-core/src/device/cart/cart.c index b28e6c4a..65d18cdf 100644 --- a/mupen64plus-core/src/device/cart/cart.c +++ b/mupen64plus-core/src/device/cart/cart.c @@ -51,6 +51,9 @@ static void process_cart_command(void* jbd, rx_buf[1] = (uint8_t)(cart->eeprom.type >> 8); rx_buf[2] = 0x00; } + else { + *rx |= 0x80; + } } break; case JCMD_EEPROM_READ: { diff --git a/mupen64plus-core/src/device/cart/is_viewer.c b/mupen64plus-core/src/device/cart/is_viewer.c index e90a6c56..d3522401 100644 --- a/mupen64plus-core/src/device/cart/is_viewer.c +++ b/mupen64plus-core/src/device/cart/is_viewer.c @@ -43,7 +43,7 @@ void read_is_viewer(void* opaque, uint32_t address, uint32_t* value) struct is_viewer* is_viewer = (struct is_viewer*)opaque; address &= IS_ADDR_MASK; memcpy(value, &is_viewer->data[address], 4); - *value = m64p_swap32(*value); + *value = big32(*value); } void write_is_viewer(void* opaque, uint32_t address, uint32_t value, uint32_t mask) @@ -67,19 +67,23 @@ void write_is_viewer(void* opaque, uint32_t address, uint32_t value, uint32_t ma memcpy(&is_viewer->output_buffer[is_viewer->buffer_pos], &is_viewer->data[0x20], word); is_viewer->buffer_pos += word; + + /* process new lines in buffer to prevent empty debug messages without losing data */ char* newline = memchr(is_viewer->output_buffer, '\n', is_viewer->buffer_pos); - if (newline) + while (newline) { + size_t index = (newline - is_viewer->output_buffer) + 1; *newline = '\0'; DebugMessage(M64MSG_INFO, "IS64: %s", is_viewer->output_buffer); - memset(is_viewer->output_buffer, 0, is_viewer->buffer_pos); - is_viewer->buffer_pos = 0; + memcpy(&is_viewer->output_buffer, &is_viewer->output_buffer[index], IS_BUFFER_SIZE - index); + is_viewer->buffer_pos -= index; + newline = memchr(is_viewer->output_buffer, '\n', is_viewer->buffer_pos); } } } else { - word = m64p_swap32(word); + word = big32(word); memcpy(&is_viewer->data[address], &word, sizeof(word)); } } diff --git a/mupen64plus-core/src/device/controllers/game_controller.c b/mupen64plus-core/src/device/controllers/game_controller.c index 6d4a7002..4a516a1c 100644 --- a/mupen64plus-core/src/device/controllers/game_controller.c +++ b/mupen64plus-core/src/device/controllers/game_controller.c @@ -155,7 +155,6 @@ const struct game_controller_flavor g_mouse_controller_flavor = mouse_controller_reset }; - void init_game_controller(struct game_controller* cont, const struct game_controller_flavor* flavor, void* cin, const struct controller_input_backend_interface* icin, diff --git a/mupen64plus-core/src/device/controllers/game_controller.h b/mupen64plus-core/src/device/controllers/game_controller.h index 14fdee6a..893f1bd8 100644 --- a/mupen64plus-core/src/device/controllers/game_controller.h +++ b/mupen64plus-core/src/device/controllers/game_controller.h @@ -59,6 +59,12 @@ struct game_controller void* pak; const struct pak_interface* ipak; + + /* VRU */ + uint8_t voice_state; + uint8_t load_offset; + uint8_t voice_init; + uint16_t word[40]; }; void init_game_controller(struct game_controller* cont, diff --git a/mupen64plus-core/src/device/controllers/paks/mempak.c b/mupen64plus-core/src/device/controllers/paks/mempak.c index a0536b5a..e57dc5ff 100644 --- a/mupen64plus-core/src/device/controllers/paks/mempak.c +++ b/mupen64plus-core/src/device/controllers/paks/mempak.c @@ -23,61 +23,131 @@ #include "backends/api/storage_backend.h" #include "device/controllers/game_controller.h" +#include "main/util.h" +#include +#include #include #include +/* Serialized representation of ID Block + * Only used to ease offsets/pointers computation + * DO NOT DEREFERENCE + */ +#pragma pack(push, 1) +struct id_block_serialized { + uint32_t serial[6]; + uint16_t device_id; + uint8_t banks; + uint8_t version; + uint16_t sum; + uint16_t isum; +}; +#pragma pack(pop) +#if defined(static_assert) +static_assert(sizeof(struct id_block_serialized) == 32, "id_block_serialized must have a size of 32 bytes"); +#endif -void format_mempak(uint8_t* mem) +static void checksum_id_block(unsigned char* ptr, + uint16_t* sum, uint16_t* isum) { - enum { MPK_PAGE_SIZE = 256 }; size_t i; + uint16_t accu = 0; + for (i = 0; i < offsetof(struct id_block_serialized, sum); i += 2) { + accu += load_beu16((void*)&ptr[i]); + } + *sum = accu; + *isum = UINT16_C(0xfff2) - accu; +} - static const uint8_t page_0[MPK_PAGE_SIZE] = - { - /* Label area */ - 0x81,0x01,0x02,0x03, 0x04,0x05,0x06,0x07, 0x08,0x09,0x0a,0x0b, 0x0c,0x0d,0x0e,0x0f, - 0x10,0x11,0x12,0x13, 0x14,0x15,0x16,0x17, 0x18,0x19,0x1a,0x1b, 0x1c,0x1d,0x1e,0x1f, - /* Main ID area */ - 0xff,0xff,0xff,0xff, 0x05,0x1a,0x5f,0x13, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, - 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0x01,0xff, 0x66,0x25,0x99,0xcd, - /* Unused */ - 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, - /* ID area backup #1 */ - 0xff,0xff,0xff,0xff, 0x05,0x1a,0x5f,0x13, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, - 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0x01,0xff, 0x66,0x25,0x99,0xcd, - /* ID area backup #2 */ - 0xff,0xff,0xff,0xff, 0x05,0x1a,0x5f,0x13, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, - 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0x01,0xff, 0x66,0x25,0x99,0xcd, - /* Unused */ - 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, - /* ID area backup #3 */ - 0xff,0xff,0xff,0xff, 0x05,0x1a,0x5f,0x13, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, - 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0x01,0xff, 0x66,0x25,0x99,0xcd, - /* Unused */ - 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00 - }; - - /* Fill Page 0 with pre-initialized content */ - memcpy(mem, page_0, MPK_PAGE_SIZE); - - /* Fill INODE page 1 and update it's checkum */ +static uint8_t checksum_index_table(size_t count, unsigned char* ptr) +{ + unsigned sum = 0; + while (count != 0) { + sum += load_beu8((void*)ptr); + ++ptr; + --count; + } + + return (uint8_t)sum; +} + +static void serialize_id_block(unsigned char *ptr, const uint32_t serial[6], uint16_t device_id, uint8_t banks, uint8_t version) { + + size_t i; + /* _ should never be dereferenced - it is only used to ease pointer/offsets computation */ + struct id_block_serialized* const _ = (struct id_block_serialized*)ptr; + + + for (i = 0; i < 6; ++i) { + store_beu32(serial[i], (void*)&_->serial[i]); + } + store_beu16(device_id, (void*)&_->device_id); + store_beu8(banks, (void*)&_->banks); + store_beu8(version, (void*)&_->version); + + uint16_t sum, isum; + checksum_id_block(ptr, &sum, &isum); + + store_beu16(sum, (void*)&_->sum); + store_beu16(isum, (void*)&_->isum); +} + + + +void format_mempak(uint8_t* mem, + const uint32_t serial[6], + uint16_t device_id, + uint8_t banks, + uint8_t version) +{ + enum { MPK_PAGE_SIZE = 256 }; + + uint8_t* const page_0 = mem + 0*MPK_PAGE_SIZE; + uint8_t* const page_1 = mem + 1*MPK_PAGE_SIZE; + uint8_t* const page_2 = mem + 2*MPK_PAGE_SIZE; + uint8_t* const page_3 = mem + 3*MPK_PAGE_SIZE; + + /* Page 0 is divided in 8 x 32-byte blocks: + * 0. reserved + * 1. ID + * 2. reserved + * 3. ID backup #1 + * 4. ID backup #2 + * 5. reserved + * 6. ID backup #3 + * 7. reserved + */ + serialize_id_block(page_0 + 1*32, serial, device_id, banks, version); + + memset(page_0 + 0*32, 0, 32); + memset(page_0 + 2*32, 0, 32); + memset(page_0 + 5*32, 0, 32); + memset(page_0 + 7*32, 0, 32); + + memcpy(page_0 + 3*32, page_0 + 1*32, 32); + memcpy(page_0 + 4*32, page_0 + 1*32, 32); + memcpy(page_0 + 6*32, page_0 + 1*32, 32); + + /* Page 1 holds the index table. + * The first 5 inodes are reserved because the first 5 pages are reserved. + * The first inode page index holds the checksum of the 123 normal nodes. + * The remaining 123 pages are marked empty. + */ size_t start_page = 5; - memset(mem + 1*MPK_PAGE_SIZE, 0, 2*start_page); - for (i = 1*MPK_PAGE_SIZE+2*start_page; i < 2*MPK_PAGE_SIZE; i += 2) { - mem[i+0] = 0x00; - mem[i+1] = 0x03; + size_t last_page = 128; + size_t i; + memset(page_1, 0, 2*start_page); + for(i = start_page; i < last_page; ++i) { + store_beu16(UINT16_C(0x0003), page_1 + 2*i); } - mem[1*MPK_PAGE_SIZE + 1] = 0x71; + page_1[1] = checksum_index_table(2*(last_page-start_page), page_1 + 2*start_page); - /* Page 2 is identical to page 1 */ - memcpy(mem + 2*MPK_PAGE_SIZE, mem + 1*MPK_PAGE_SIZE, MPK_PAGE_SIZE); + /* Page 2 is a backup of Page 1 */ + memcpy(page_2, page_1, MPK_PAGE_SIZE); /* Remaining pages DIR+DATA (3...) are initialized with 0x00 */ - memset(mem + 3*MPK_PAGE_SIZE, 0, MEMPAK_SIZE - 3*MPK_PAGE_SIZE); + memset(page_3, 0, MEMPAK_SIZE - 3*MPK_PAGE_SIZE); } diff --git a/mupen64plus-core/src/device/controllers/paks/mempak.h b/mupen64plus-core/src/device/controllers/paks/mempak.h index 39159f08..1cbad59a 100644 --- a/mupen64plus-core/src/device/controllers/paks/mempak.h +++ b/mupen64plus-core/src/device/controllers/paks/mempak.h @@ -27,6 +27,10 @@ struct storage_backend_interface; +#define DEFAULT_MEMPAK_DEVICEID UINT16_C(0x0001) +#define DEFAULT_MEMPAK_BANKS UINT8_C(0x01) +#define DEFAULT_MEMPAK_VERSION UINT8_C(0x00) + struct mempak { void* storage; @@ -35,7 +39,11 @@ struct mempak enum { MEMPAK_SIZE = 0x8000 }; -void format_mempak(uint8_t* mem); +void format_mempak(uint8_t* mem, + const uint32_t serial[6], + uint16_t device_id, + uint8_t banks, + uint8_t version); void init_mempak(struct mempak* mpk, void* storage, diff --git a/mupen64plus-core/src/device/controllers/vru_controller.c b/mupen64plus-core/src/device/controllers/vru_controller.c new file mode 100644 index 00000000..f577e019 --- /dev/null +++ b/mupen64plus-core/src/device/controllers/vru_controller.c @@ -0,0 +1,254 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus - vru_controller.c * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2014 Bobby Smiles * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "game_controller.h" +#include "vru_controller.h" + +#include "api/callbacks.h" +#include "api/m64p_types.h" +#include "backends/api/controller_input_backend.h" +#include "backends/api/joybus.h" +#include "plugin/plugin.h" +#include "main/rom.h" + +#ifdef COMPARE_CORE +#include "api/debugger.h" +#endif + +#include +#include + +enum voice_status +{ + VOICE_STATUS_READY = 0x00, + VOICE_STATUS_START = 0x01, + VOICE_STATUS_CANCEL = 0x03, + VOICE_STATUS_BUSY = 0x05, + VOICE_STATUS_END = 0x07 +}; + +static uint8_t vru_data_crc(const uint8_t* data, size_t size) +{ + size_t i; + uint8_t crc = 0; + + for(i = 0; i <= size; ++i) + { + int mask; + for (mask = 0x80; mask >= 1; mask >>= 1) + { + uint8_t xor_tap = (crc & 0x80) ? 0x85 : 0x00; + crc <<= 1; + if (i != size && (data[i] & mask)) crc |= 1; + crc ^= xor_tap; + } + } + return crc; +} + +/* VRU controller */ +static void vru_controller_reset(struct game_controller* cont) +{ + cont->status = 0x00; + if (ROM_HEADER.Country_code == 0x4A /* Japan */ || ROM_HEADER.Country_code == 0x00 /* Demo */) + cont->voice_state = VOICE_STATUS_READY; + else + cont->voice_state = VOICE_STATUS_START; + cont->load_offset = 0; + cont->voice_init = 1; + memset(cont->word, 0, 80); +} + +const struct game_controller_flavor g_vru_controller_flavor = +{ + "VRU controller", + JDT_VRU, + vru_controller_reset +}; + +static void poweron_vru_controller(void* jbd) +{ + struct game_controller* cont = (struct game_controller*)jbd; + + cont->flavor->reset(cont); +} + +static void process_vru_command(void* jbd, + const uint8_t* tx, const uint8_t* tx_buf, + uint8_t* rx, uint8_t* rx_buf) +{ + struct game_controller* cont = (struct game_controller*)jbd; + uint32_t input_ = 0; + uint8_t cmd = tx_buf[0]; + + /* if controller can't successfully be polled, consider it to be absent */ + if (cont->icin->get_input(cont->cin, &input_) != M64ERR_SUCCESS) { + *rx |= 0x80; + return; + } + + switch (cmd) + { + case JCMD_RESET: + cont->flavor->reset(cont); + /* fall through */ + case JCMD_STATUS: { + JOYBUS_CHECK_COMMAND_FORMAT(1, 3) + + if (cont->voice_init == 2) + { + /* words have been loaded, we can change the state from READY to START */ + cont->voice_state = VOICE_STATUS_START; + cont->voice_init = 1; + } + else if ((input_ & 0x0020) && (cont->voice_state == VOICE_STATUS_START)) + { + /* HACK: The Z input on the VRU controller is used to indicate that someone is talking */ + /* On Densha de Go, if the player is talking for more than ~2.5 seconds, the input is ignored */ + cont->voice_state = VOICE_STATUS_BUSY; + cont->status = 0; /* setting the status to 0 tells the game to check the voice_status */ + } + else if (!(input_ & 0x0020) && (cont->voice_state == VOICE_STATUS_BUSY)) + { + cont->voice_state = VOICE_STATUS_READY; + cont->status = 0; /* setting the status to 0 tells the game to check the voice_status */ + } + + rx_buf[0] = (uint8_t)(cont->flavor->type >> 0); + rx_buf[1] = (uint8_t)(cont->flavor->type >> 8); + rx_buf[2] = cont->status; + } break; + + case JCMD_CONTROLLER_READ: { + JOYBUS_CHECK_COMMAND_FORMAT(1, 4) +#ifdef COMPARE_CORE + CoreCompareDataSync(4, rx_buf); +#endif + } break; + + case JCMD_VRU_READ_STATUS: { + JOYBUS_CHECK_COMMAND_FORMAT(3, 3) + rx_buf[0] = cont->voice_init ? cont->voice_state : 0; + rx_buf[1] = 0; + rx_buf[2] = vru_data_crc(&rx_buf[0], 2); + if (cont->load_offset > 0) + { + uint8_t offset = 0; + while (cont->word[offset] == 0 && offset < 40) + ++offset; + if (offset == 40) + { + DebugMessage(M64MSG_WARNING, "Empty JCMD_VRU_WRITE."); + } + else if (cont->word[offset] == 3) + { + offset += 3; + uint16_t length = cont->word[offset]; + if (ROM_HEADER.Country_code == 0x4A /* Japan */ || ROM_HEADER.Country_code == 0x00 /* Demo */) + { + offset -= 1; + length = 0; + while (cont->word[offset + length] != 0) + { + ++length; + } + input.sendVRUWord(length, &cont->word[offset], 1); + } + else + { + ++offset; + input.sendVRUWord(length, &cont->word[offset], 0); + } + } + else + { + /* Unhandled command, could be a string/word mask. + For a mask: + "Data is right-aligned and padded with zeroes to an even length, followed with command 0004. Set bits allow strings, unset ignores." + I haven't seen Hey You Pikachu or Densha de GO use the mask command, so I wasn't able to test. + TODO: Call input.SetVRUWordMask() to tell the input plugin about the mask settings */ + DebugMessage(M64MSG_WARNING, "Unknown command in JCMD_VRU_WRITE."); + } + cont->load_offset = 0; + } + cont->status = 1; + } break; + + case JCMD_VRU_WRITE_CONFIG: { + JOYBUS_CHECK_COMMAND_FORMAT(7, 1) + rx_buf[0] = vru_data_crc(&tx_buf[3], 4); + if (rx_buf[0] == 0x4E) + { + input.setMicState(1); + cont->voice_init = 2; + } + else if (rx_buf[0] == 0xEF) + { + input.setMicState(0); + } + else if (tx_buf[3] == 0x2) + { + cont->voice_init = 0; + input.clearVRUWords(tx_buf[5]); + } + cont->status = 0; /* status is always set to 0 after a write */ + } break; + + case JCMD_VRU_WRITE_INIT: { + JOYBUS_CHECK_COMMAND_FORMAT(3, 1) + if (*((uint16_t*)(&tx_buf[1])) == 0) + input.setMicState(0); + rx_buf[0] = 0; + } break; + + case JCMD_VRU_READ: { + JOYBUS_CHECK_COMMAND_FORMAT(3, 37) + *((uint16_t*)(&rx_buf[0])) = 0x8000; /* as per zoinkity https://pastebin.com/6UiErk5h */ + *((uint16_t*)(&rx_buf[2])) = 0x0F00; /* as per zoinkity https://pastebin.com/6UiErk5h */ + *((uint16_t*)(&rx_buf[34])) = 0x0040; /* as per zoinkity https://pastebin.com/6UiErk5h */ + input.readVRUResults((uint16_t*)&rx_buf[4] /*error flags*/, (uint16_t*)&rx_buf[6] /*number of results*/, (uint16_t*)&rx_buf[8] /*mic level*/, \ + (uint16_t*)&rx_buf[10] /*voice level*/, (uint16_t*)&rx_buf[12] /*voice length*/, (uint16_t*)&rx_buf[14] /*matches*/); + rx_buf[36] = vru_data_crc(&rx_buf[0], 36); + cont->voice_state = VOICE_STATUS_START; + } break; + + case JCMD_VRU_WRITE: { + JOYBUS_CHECK_COMMAND_FORMAT(23, 1) + rx_buf[0] = vru_data_crc(&tx_buf[3], 20); + if (cont->load_offset == 0) + memset(cont->word, 0, 80); + memcpy(&cont->word[cont->load_offset], &tx_buf[3], 20); + cont->load_offset += 10; + cont->status = 0; /* status is always set to 0 after a write */ + } break; + + default: + DebugMessage(M64MSG_WARNING, "cont: Unknown command %02x %02x %02x", + *tx, *rx, cmd); + } +} + +const struct joybus_device_interface g_ijoybus_vru_controller = +{ + poweron_vru_controller, + process_vru_command, + NULL +}; diff --git a/mupen64plus-core/src/device/controllers/vru_controller.h b/mupen64plus-core/src/device/controllers/vru_controller.h new file mode 100644 index 00000000..c8113859 --- /dev/null +++ b/mupen64plus-core/src/device/controllers/vru_controller.h @@ -0,0 +1,37 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus - vru_controller.h * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2014 Bobby Smiles * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef M64P_DEVICE_SI_VRU_CONTROLLER_H +#define M64P_DEVICE_SI_VRU_CONTROLLER_H + +#include "backends/api/joybus.h" + +#include +#include + +/* Controller Joybus interface */ +extern const struct joybus_device_interface + g_ijoybus_vru_controller; + +/* Controller flavors */ +extern const struct game_controller_flavor g_vru_controller_flavor; + +#endif diff --git a/mupen64plus-core/src/device/dd/dd_controller.c b/mupen64plus-core/src/device/dd/dd_controller.c index 981b013d..1e388ca7 100644 --- a/mupen64plus-core/src/device/dd/dd_controller.c +++ b/mupen64plus-core/src/device/dd/dd_controller.c @@ -21,6 +21,7 @@ #include "dd_controller.h" +#include #include #include #include @@ -129,6 +130,71 @@ static void clear_dd_interrupt(struct dd_controller* dd, uint32_t bm_int) r4300_check_interrupt(dd->r4300, CP0_CAUSE_IP3, 0); } +void dd_mecha_int_handler(void* opaque) +{ + struct dd_controller* dd = (struct dd_controller*)opaque; + /* clear busy state flag */ + dd->regs[DD_ASIC_CMD_STATUS] &= ~DD_STATUS_BUSY_STATE; + signal_dd_interrupt(dd, DD_STATUS_MECHA_INT); +} + +void dd_bm_int_handler(void* opaque) +{ + struct dd_controller* dd = (struct dd_controller*)opaque; + dd_update_bm(dd); +} + +void dd_dv_active(void* opaque) +{ + struct dd_controller* dd = (struct dd_controller*)opaque; + /* make motor active and prep standby */ + dd->regs[DD_ASIC_CMD_STATUS] &= ~(DD_STATUS_MTR_N_SPIN | DD_STATUS_HEAD_RTRCT); + remove_event(&dd->r4300->cp0.q, DD_DV_INT); + if (dd->timer_standby >= 0) { + add_interrupt_event(&dd->r4300->cp0, DD_DV_INT, 46875000 * dd->timer_standby); + } +} + +void dd_dv_standby(void* opaque) +{ + struct dd_controller* dd = (struct dd_controller*)opaque; + /* make motor standby and prep sleep */ + dd->regs[DD_ASIC_CMD_STATUS] &= ~DD_STATUS_MTR_N_SPIN; + dd->regs[DD_ASIC_CMD_STATUS] |= DD_STATUS_HEAD_RTRCT; + remove_event(&dd->r4300->cp0.q, DD_DV_INT); + if (dd->timer_sleep >= 0) { + add_interrupt_event(&dd->r4300->cp0, DD_DV_INT, 46875000 * dd->timer_sleep); + } +} + +void dd_dv_sleep(void* opaque) +{ + struct dd_controller* dd = (struct dd_controller*)opaque; + /* make motor sleep */ + dd->regs[DD_ASIC_CMD_STATUS] |= DD_STATUS_MTR_N_SPIN | DD_STATUS_HEAD_RTRCT; + remove_event(&dd->r4300->cp0.q, DD_DV_INT); +} + +void dd_dv_int_handler(void* opaque) +{ + struct dd_controller* dd = (struct dd_controller*)opaque; + /* manage drive motor modes (active, standby, sleep) */ + int motorNotSpinning = (dd->regs[DD_ASIC_CMD_STATUS] & DD_STATUS_MTR_N_SPIN) != 0; + int headRetracted = (dd->regs[DD_ASIC_CMD_STATUS] & DD_STATUS_HEAD_RTRCT) != 0; + + if (!motorNotSpinning && headRetracted) { + /* standby to sleep */ + dd_dv_sleep(dd); + DebugMessage(M64MSG_VERBOSE, "Disk drive motor put to sleep mode (auto)"); + } + + if (!motorNotSpinning && !headRetracted) { + /* active to standby, prep time to sleep */ + dd_dv_standby(dd); + DebugMessage(M64MSG_VERBOSE, "Disk drive motor put to standby mode (auto)"); + } +} + static void read_C2(struct dd_controller* dd) { size_t i; @@ -204,14 +270,24 @@ void dd_update_bm(void* opaque) return; } + /* clear flags */ + dd->regs[DD_ASIC_CMD_STATUS] &= ~(DD_STATUS_DATA_RQ | DD_STATUS_C2_XFER); + + /* calculate sector and block info for use later */ unsigned int sector = (dd->regs[DD_ASIC_CUR_SECTOR] >> 16) & 0xff; unsigned int block = sector / 90; sector %= 90; /* handle writes (BM mode 0) */ if (dd->bm_write) { + /* do not write anything and stop BM if the track being written is write protected */ + if (dd->regs[DD_ASIC_CMD_STATUS] & DD_STATUS_WR_PR_ERR) { + dd->regs[DD_ASIC_CMD_STATUS] |= DD_STATUS_BM_ERR; + dd->regs[DD_ASIC_BM_STATUS_CTL] |= DD_BM_STATUS_MICRO; + dd->regs[DD_ASIC_BM_STATUS_CTL] &= ~DD_BM_STATUS_RUNNING; + } /* first sector : just issue a BM interrupt to get things going */ - if (sector == 0) { + else if (sector == 0) { dd->regs[DD_ASIC_CUR_SECTOR] += 0x10000; dd->regs[DD_ASIC_CMD_STATUS] |= DD_STATUS_DATA_RQ; } @@ -256,15 +332,14 @@ void dd_update_bm(void* opaque) dd->regs[DD_ASIC_CMD_STATUS] |= DD_STATUS_DATA_RQ; } /* C2 sectors: do nothing since they're loaded with zeros */ - else if (sector < SECTORS_PER_BLOCK + 4) { + else if (sector < SECTORS_PER_BLOCK + 3) { read_C2(dd); dd->regs[DD_ASIC_CUR_SECTOR] += 0x10000; - if ((sector + 1) == SECTORS_PER_BLOCK + 4) { - dd->regs[DD_ASIC_CMD_STATUS] |= DD_STATUS_C2_XFER; - } } - /* Gap sector: continue to next block, quit after second block */ - else if (sector == SECTORS_PER_BLOCK + 4) { + /* Last C2 sector: continue to next block, quit after second block */ + else if (sector == SECTORS_PER_BLOCK + 3) { + read_C2(dd); + dd->regs[DD_ASIC_CMD_STATUS] |= DD_STATUS_C2_XFER; if (dd->regs[DD_ASIC_BM_STATUS_CTL] & DD_BM_STATUS_BLOCK) { // Start at next block sector 0. dd->regs[DD_ASIC_CUR_SECTOR] = ((1 - block) * 90 + 0) << 16; @@ -279,6 +354,9 @@ void dd_update_bm(void* opaque) } } + /* Make sure motor is still considered active */ + dd_dv_active(dd); + /* Signal a BM interrupt */ signal_dd_interrupt(dd, DD_STATUS_BM_INT); } @@ -317,6 +395,10 @@ void poweron_dd(struct dd_controller* dd) dd->rtc.now = 0; dd->rtc.last_update_rtc = 0; + dd->timer_sleep = 1; + dd->timer_standby = 3; + dd_dv_sleep(dd); + dd->regs[DD_ASIC_ID_REG] = 0x00030000; dd->regs[DD_ASIC_CMD_STATUS] |= DD_STATUS_RST_STATE; if (dd->idisk != NULL) { @@ -355,12 +437,10 @@ void read_dd_regs(void* opaque, uint32_t address, uint32_t* value) switch(reg) { case DD_ASIC_CMD_STATUS: { - /* clear BM interrupt when reading gap */ - unsigned int sector = ((dd->regs[DD_ASIC_CUR_SECTOR] >> 16) & 0xff); - sector %= 90; - if ((dd->regs[DD_ASIC_CMD_STATUS] & DD_STATUS_BM_INT) && (sector > SECTORS_PER_BLOCK)) { + /* acknowledge BM interrupt */ + if (dd->regs[DD_ASIC_CMD_STATUS] & DD_STATUS_BM_INT) { clear_dd_interrupt(dd, DD_STATUS_BM_INT); - dd_update_bm(dd); + add_interrupt_event(&dd->r4300->cp0, DD_BM_INT, 8020 + (((dd->regs[DD_ASIC_CUR_TK] & 0x0fff0000) >> 16) / 56)); } } break; } @@ -368,7 +448,8 @@ void read_dd_regs(void* opaque, uint32_t address, uint32_t* value) void write_dd_regs(void* opaque, uint32_t address, uint32_t value, uint32_t mask) { - unsigned int head, track; + unsigned int head, track, old_track, cycles; + const uint16_t startTrackZones[9] = { 0x000, 0x09E, 0x13C, 0x1D1, 0x266, 0x2FB, 0x390, 0x425, 0x497 }; struct dd_controller* dd = (struct dd_controller*)opaque; if (address < MM_DD_REGS || address >= MM_DD_MS_RAM) { @@ -392,6 +473,12 @@ void write_dd_regs(void* opaque, uint32_t address, uint32_t value, uint32_t mask update_rtc(&dd->rtc); const struct tm* tm = localtime(&dd->rtc.now); + /* base cycle count */ + cycles = 2000; + + /* say the drive is busy while processing the command */ + dd->regs[DD_ASIC_CMD_STATUS] |= DD_STATUS_BUSY_STATE; + switch ((value >> 16) & 0xff) { /* No-op */ @@ -401,6 +488,18 @@ void write_dd_regs(void* opaque, uint32_t address, uint32_t value, uint32_t mask /* Seek track */ case 0x01: case 0x02: + /* base timing cycle count for Seek track CMD */ + cycles = 248250; + /* check if motor is active or not, if not, add more cycles */ + if ((dd->regs[DD_ASIC_CMD_STATUS] & (DD_STATUS_MTR_N_SPIN | DD_STATUS_HEAD_RTRCT)) != 0) { + //divided by 100 because F-Zero X Expansion Kit really dislikes anything higher + cycles += 501750; + } + /* make motor active */ + dd_dv_active(dd); + /* get old track for calculating extra cycles */ + old_track = (dd->regs[DD_ASIC_CUR_TK] & 0x0fff0000) >> 16; + /* update track */ dd->regs[DD_ASIC_CUR_TK] = dd->regs[DD_ASIC_DATA]; /* lock track */ dd->regs[DD_ASIC_CUR_TK] |= DD_TRACK_LOCK; @@ -409,6 +508,82 @@ void write_dd_regs(void* opaque, uint32_t address, uint32_t value, uint32_t mask head = (dd->regs[DD_ASIC_CUR_TK] & 0x10000000) >> 28; track = (dd->regs[DD_ASIC_CUR_TK] & 0x0fff0000) >> 16; dd->bm_zone = (get_zone_from_head_track(head, track) - head) + 8*head; + /* calculate track to track head movement timing */ + cycles += 4825 * abs(track - old_track); + /* if write seek command, check if the track is writable */ + dd->regs[DD_ASIC_CMD_STATUS] &= ~DD_STATUS_WR_PR_ERR; + if (dd->bm_write) { + if (track < startTrackZones[(dd->disk_type & 0xf) - head + 3]) { + dd->regs[DD_ASIC_CMD_STATUS] |= DD_STATUS_WR_PR_ERR; + } + } + break; + + /* Rezero / Start (Seek to track 0) */ + case 0x03: + case 0x05: + /* both commands do the exact same thing */ + /* base timing cycle count for Seek track CMD */ + cycles = 248250; + /* check if motor is active or not, if not, add more cycles */ + if ((dd->regs[DD_ASIC_CMD_STATUS] & (DD_STATUS_MTR_N_SPIN | DD_STATUS_HEAD_RTRCT)) != 0) { + //divided by 100 because F-Zero X Expansion Kit really dislikes anything higher + cycles += 501750; + } + /* make motor active */ + dd_dv_active(dd); + /* get old track for calculating extra cycles */ + old_track = (dd->regs[DD_ASIC_CUR_TK] & 0x0fff0000) >> 16; + /* update track to 0 */ + dd->regs[DD_ASIC_CUR_TK] = 0; + /* lock track */ + dd->regs[DD_ASIC_CUR_TK] |= DD_TRACK_LOCK; + dd->bm_write = 1; + /* update bm_zone */ + head = (dd->regs[DD_ASIC_CUR_TK] & 0x10000000) >> 28; + track = (dd->regs[DD_ASIC_CUR_TK] & 0x0fff0000) >> 16; + dd->bm_zone = (get_zone_from_head_track(head, track) - head) + 8 * head; + /* calculate track to track head movement timing */ + cycles += 4825 * abs(track - old_track); + break; + + /* Sleep / Brake */ + case 0x04: + if ((dd->regs[DD_ASIC_CMD_STATUS] & (DD_STATUS_MTR_N_SPIN | DD_STATUS_HEAD_RTRCT)) != 0) { + //divided by 100 because F-Zero X Expansion Kit really dislikes anything higher + cycles = 207500; + } + dd_dv_sleep(dd); + if (dd->regs[DD_ASIC_DATA] == 0) + { + DebugMessage(M64MSG_VERBOSE, "Disk drive motor put to sleep mode"); + } + else + { + DebugMessage(M64MSG_VERBOSE, "Disk drive motor put to brake mode"); + } + break; + + /* Set standby delay */ + case 0x06: + if ((dd->regs[DD_ASIC_DATA] & 0x01000000) == 0) { + dd->timer_standby = (dd->regs[DD_ASIC_DATA] >> 16) & 0xff; + DebugMessage(M64MSG_VERBOSE, "Set disk drive standby delay to %u seconds", dd->timer_standby); + } else { + dd->timer_standby = -1; + DebugMessage(M64MSG_VERBOSE, "Disable disk drive standby delay"); + } + break; + + /* Set sleep delay */ + case 0x07: + if ((dd->regs[DD_ASIC_DATA] & 0x01000000) == 0) { + dd->timer_sleep = (dd->regs[DD_ASIC_DATA] >> 16) & 0xff; + DebugMessage(M64MSG_VERBOSE, "Set disk drive sleep delay to %u seconds", dd->timer_sleep); + } else { + dd->timer_sleep = -1; + DebugMessage(M64MSG_VERBOSE, "Disable disk drive sleep delay"); + } break; /* Clear Disk change flag */ @@ -422,9 +597,60 @@ void write_dd_regs(void* opaque, uint32_t address, uint32_t value, uint32_t mask dd->regs[DD_ASIC_CMD_STATUS] &= ~DD_STATUS_DISK_CHNG; break; + /* Read ASIC version */ + case 0x0a: + if (dd->regs[DD_ASIC_DATA] == 0) + { + dd->regs[DD_ASIC_DATA] = 0x01140000; + if (dd->disk->development) + dd->regs[DD_ASIC_DATA] |= 0x10000000; + } + else + { + dd->regs[DD_ASIC_DATA] = 0x53000000; + } + break; + /* Set Disk type */ case 0x0b: - DebugMessage(M64MSG_VERBOSE, "Setting disk type %u", (dd->regs[DD_ASIC_DATA] >> 16) & 0xf); + dd->disk_type = (dd->regs[DD_ASIC_DATA] >> 16) & 0xf; + if (dd->disk_type > 6) { + DebugMessage(M64MSG_VERBOSE, "Setting invalid disk type %u, set to fallback disk type 6", dd->disk_type); + dd->disk_type = 6; + } else { + DebugMessage(M64MSG_VERBOSE, "Setting disk type %u", dd->disk_type); + } + break; + + /* Request controller status */ + case 0x0c: + dd->regs[DD_ASIC_DATA] = 0; + break; + + /* Standby */ + case 0x0d: + if ((dd->regs[DD_ASIC_CMD_STATUS] & (DD_STATUS_MTR_N_SPIN | DD_STATUS_HEAD_RTRCT)) != 0) { + //divided by 100 because F-Zero X Expansion Kit really dislikes anything higher + cycles = 160000; + } + dd_dv_standby(dd); + DebugMessage(M64MSG_VERBOSE, "Disk drive motor put to standby mode"); + break; + + /* Retry index lock */ + case 0x0e: + DebugMessage(M64MSG_VERBOSE, "Retry disk track lock"); + break; + + /* Write RTC from ASIC_DATA (BCD format) */ + case 0x0f: + DebugMessage(M64MSG_VERBOSE, "Write 64DD RTC Year %02x, Month %02x", (dd->regs[DD_ASIC_DATA] & 0xff000000) >> 24, (dd->regs[DD_ASIC_DATA] & 0x00ff0000) >> 16); + break; + case 0x10: + DebugMessage(M64MSG_VERBOSE, "Write 64DD RTC Day %02x, Hour %02x", (dd->regs[DD_ASIC_DATA] & 0xff000000) >> 24, (dd->regs[DD_ASIC_DATA] & 0x00ff0000) >> 16); + break; + case 0x11: + DebugMessage(M64MSG_VERBOSE, "Write 64DD RTC Minute %02x, Second %02x", (dd->regs[DD_ASIC_DATA] & 0xff000000) >> 24, (dd->regs[DD_ASIC_DATA] & 0x00ff0000) >> 16); break; /* Read RTC in ASIC_DATA (BCD format) */ @@ -438,9 +664,14 @@ void write_dd_regs(void* opaque, uint32_t address, uint32_t value, uint32_t mask dd->regs[DD_ASIC_DATA] = time2data(tm->tm_min, tm->tm_sec); break; + /* LED On/Off Timing */ + case 0x15: + DebugMessage(M64MSG_VERBOSE, "LED ON Time %02x, LED OFF Time %02x", (dd->regs[DD_ASIC_DATA] & 0xff000000) >> 24, (dd->regs[DD_ASIC_DATA] & 0x00ff0000) >> 16); + break; + /* Feature inquiry */ case 0x1b: - dd->regs[DD_ASIC_DATA] = 0x00000000; + dd->regs[DD_ASIC_DATA] = 0x00030000; break; default: @@ -448,7 +679,8 @@ void write_dd_regs(void* opaque, uint32_t address, uint32_t value, uint32_t mask } /* Signal a MECHA interrupt */ - signal_dd_interrupt(dd, DD_STATUS_MECHA_INT); + cp0_update_count(dd->r4300); + add_interrupt_event(&dd->r4300->cp0, DD_MC_INT, cycles); break; case DD_ASIC_BM_STATUS_CTL: @@ -461,6 +693,7 @@ void write_dd_regs(void* opaque, uint32_t address, uint32_t value, uint32_t mask /* clear MECHA interrupt */ if (value & DD_BM_CTL_MECHA_RST) { dd->regs[DD_ASIC_CMD_STATUS] &= ~DD_STATUS_MECHA_INT; + remove_event(&dd->r4300->cp0.q, DD_MC_INT); } /* start block transfer */ if (value & DD_BM_CTL_BLK_TRANS) { @@ -478,6 +711,7 @@ void write_dd_regs(void* opaque, uint32_t address, uint32_t value, uint32_t mask | DD_STATUS_BM_INT); dd->regs[DD_ASIC_BM_STATUS_CTL] = 0; dd->regs[DD_ASIC_CUR_SECTOR] = 0; + remove_event(&dd->r4300->cp0.q, DD_BM_INT); } /* clear DD interrupt if both MECHA and BM are cleared */ @@ -494,7 +728,7 @@ void write_dd_regs(void* opaque, uint32_t address, uint32_t value, uint32_t mask DebugMessage(M64MSG_WARNING, "Attempt to read disk with BM mode 0"); } dd->regs[DD_ASIC_BM_STATUS_CTL] |= DD_BM_STATUS_RUNNING; - dd_update_bm(dd); + add_interrupt_event(&dd->r4300->cp0, DD_BM_INT, 12500); } break; @@ -502,6 +736,19 @@ void write_dd_regs(void* opaque, uint32_t address, uint32_t value, uint32_t mask if (value != 0xaaaa0000) { DebugMessage(M64MSG_WARNING, "Unexpected hard reset value %08x", value); } + remove_event(&dd->r4300->cp0.q, DD_MC_INT); + remove_event(&dd->r4300->cp0.q, DD_BM_INT); + dd->regs[DD_ASIC_CMD_STATUS] &= ~(DD_STATUS_DATA_RQ + | DD_STATUS_C2_XFER + | DD_STATUS_BM_ERR + | DD_STATUS_BM_INT + | DD_STATUS_BUSY_STATE); + dd->regs[DD_ASIC_BM_STATUS_CTL] = 0; + dd->regs[DD_ASIC_CUR_SECTOR] = 0; + dd->timer_sleep = 1; + dd->timer_standby = 3; + dd_dv_sleep(dd); + clear_dd_interrupt(dd, DD_STATUS_MECHA_INT); dd->regs[DD_ASIC_CMD_STATUS] |= DD_STATUS_RST_STATE; break; @@ -632,17 +879,3 @@ unsigned int dd_dom_dma_write(void* opaque, uint8_t* dram, uint32_t dram_addr, u return cycles; } -void dd_on_pi_cart_addr_write(struct dd_controller* dd, uint32_t address) -{ - /* clear C2 xfer */ - if (address == MM_DD_C2S_BUFFER) { - dd->regs[DD_ASIC_CMD_STATUS] &= ~(DD_STATUS_C2_XFER | DD_STATUS_BM_ERR); - clear_dd_interrupt(dd, DD_STATUS_BM_INT); - } - /* clear data RQ */ - else if (address == MM_DD_DS_BUFFER) { - dd->regs[DD_ASIC_CMD_STATUS] &= ~(DD_STATUS_DATA_RQ | DD_STATUS_BM_ERR); - clear_dd_interrupt(dd, DD_STATUS_BM_INT); - } -} - diff --git a/mupen64plus-core/src/device/dd/dd_controller.h b/mupen64plus-core/src/device/dd/dd_controller.h index 4f87d4f6..2af70651 100644 --- a/mupen64plus-core/src/device/dd/dd_controller.h +++ b/mupen64plus-core/src/device/dd/dd_controller.h @@ -73,6 +73,11 @@ struct dd_controller uint8_t ds_buf[0x100]; /* 0x400-0x4ff: data buffer */ uint8_t ms_ram[0x40]; /* 0x580-0x5bf: micro sequencer */ + /* drive controller */ + unsigned char disk_type; /* [0-6] */ + short timer_standby; /* -1 = Disabled, in seconds */ + short timer_sleep; /* -1 = Disabled, in seconds */ + /* buffer manager */ unsigned char bm_write; /* [0-1] */ unsigned char bm_reset_held; /* [0-1] */ @@ -120,7 +125,10 @@ void write_dd_rom(void* opaque, uint32_t address, uint32_t value, uint32_t mask) unsigned int dd_dom_dma_read(void* opaque, const uint8_t* dram, uint32_t dram_addr, uint32_t cart_addr, uint32_t length); unsigned int dd_dom_dma_write(void* opaque, uint8_t* dram, uint32_t dram_addr, uint32_t cart_addr, uint32_t length); -void dd_on_pi_cart_addr_write(struct dd_controller* dd, uint32_t address); void dd_update_bm(void* opaque); +void dd_mecha_int_handler(void* opaque); +void dd_bm_int_handler(void* opaque); +void dd_dv_int_handler(void* opaque); + #endif diff --git a/mupen64plus-core/src/device/dd/disk.h b/mupen64plus-core/src/device/dd/disk.h index 2b7b576c..47a4d874 100644 --- a/mupen64plus-core/src/device/dd/disk.h +++ b/mupen64plus-core/src/device/dd/disk.h @@ -149,6 +149,7 @@ struct dd_disk uint16_t lba_phys_table[0x10DC]; uint8_t format; uint8_t development; + uint8_t region; size_t offset_sys; size_t offset_id; size_t offset_ram; diff --git a/mupen64plus-core/src/device/device.c b/mupen64plus-core/src/device/device.c index 3382052a..f3d61282 100644 --- a/mupen64plus-core/src/device/device.c +++ b/mupen64plus-core/src/device/device.c @@ -88,7 +88,7 @@ void init_device(struct device* dev, int randomize_interrupt, uint32_t start_address, /* ai */ - void* aout, const struct audio_out_backend_interface* iaout, + void* aout, const struct audio_out_backend_interface* iaout, float dma_modifier, /* si */ unsigned int si_dma_duration, /* rdram */ @@ -124,7 +124,10 @@ void init_device(struct device* dev, { &dev->pif, hw2_int_handler }, /* HW2 */ { dev, nmi_int_handler }, /* NMI */ { dev, reset_hard_handler }, /* reset_hard */ - { &dev->sp, rsp_end_of_dma_event } + { &dev->sp, rsp_end_of_dma_event }, + { &dev->dd, dd_mecha_int_handler }, /* DD MECHA */ + { &dev->dd, dd_bm_int_handler }, /* DD BM */ + { &dev->dd, dd_dv_int_handler }, /* DD DRIVE */ }; #define R(x) read_ ## x @@ -135,7 +138,7 @@ void init_device(struct device* dev, /* clear mappings */ { 0x00000000, 0xffffffff, M64P_MEM_NOTHING, { NULL, RW(open_bus) } }, /* memory map */ - { A(MM_RDRAM_DRAM, dram_size-1), M64P_MEM_RDRAM, { &dev->rdram, RW(rdram_dram) } }, + { A(MM_RDRAM_DRAM, 0x3efffff), M64P_MEM_RDRAM, { &dev->rdram, RW(rdram_dram) } }, { A(MM_RDRAM_REGS, 0xfffff), M64P_MEM_RDRAMREG, { &dev->rdram, RW(rdram_regs) } }, { A(MM_RSP_MEM, 0xffff), M64P_MEM_RSPMEM, { &dev->sp, RW(rsp_mem) } }, { A(MM_RSP_REGS, 0xffff), M64P_MEM_RSPREG, { &dev->sp, RW(rsp_regs) } }, @@ -182,7 +185,7 @@ void init_device(struct device* dev, emumode, count_per_op, count_per_op_denom_pot, no_compiled_jump, randomize_interrupt, start_address); init_rdp(&dev->dp, &dev->sp, &dev->mi, &dev->mem, &dev->rdram, &dev->r4300); init_rsp(&dev->sp, mem_base_u32(base, MM_RSP_MEM), &dev->mi, &dev->dp, &dev->ri); - init_ai(&dev->ai, &dev->mi, &dev->ri, &dev->vi, aout, iaout); + init_ai(&dev->ai, &dev->mi, &dev->ri, &dev->vi, aout, iaout, dma_modifier); init_mi(&dev->mi, &dev->r4300); init_pi(&dev->pi, get_pi_dma_handler, @@ -192,12 +195,12 @@ void init_device(struct device* dev, init_si(&dev->si, si_dma_duration, &dev->mi, &dev->pif, &dev->ri); init_vi(&dev->vi, vi_clock, expected_refresh_rate, &dev->mi, &dev->dp); - /* FIXME: should boot on cart, unless only a disk is present, but having no cart is not yet supported by ui/core, - * so use another way of selecting boot device: - * use CART unless DD is plugged and the plugged CART is not a combo media (cart+disk). + /* + * use CART unless DD is plugged and the plugged CART is not a combo media (cart+disk), + * or rom_size is 0 meaning there's no CART loaded */ uint8_t media = *((uint8_t*)mem_base_u32(base, MM_CART_ROM) + (0x3b ^ S8)); - uint32_t rom_base = (dd_rom_size > 0 && media != 'C') + uint32_t rom_base = (rom_size == 0 || (dd_rom_size > 0 && media != 'C')) ? MM_DD_ROM : MM_CART_ROM; diff --git a/mupen64plus-core/src/device/device.h b/mupen64plus-core/src/device/device.h index 1b94ad51..cb000a77 100644 --- a/mupen64plus-core/src/device/device.h +++ b/mupen64plus-core/src/device/device.h @@ -126,7 +126,7 @@ void init_device(struct device* dev, int randomize_interrupt, uint32_t start_address, /* ai */ - void* aout, const struct audio_out_backend_interface* iaout, + void* aout, const struct audio_out_backend_interface* iaout, float dma_modifier, /* si */ unsigned int si_dma_duration, /* rdram */ diff --git a/mupen64plus-core/src/device/pif/bootrom_hle.c b/mupen64plus-core/src/device/pif/bootrom_hle.c index e748f030..d701575f 100644 --- a/mupen64plus-core/src/device/pif/bootrom_hle.c +++ b/mupen64plus-core/src/device/pif/bootrom_hle.c @@ -62,7 +62,7 @@ void pif_bootrom_hle_execute(struct r4300_core* r4300) /* setup CP0 registers */ cp0_regs[CP0_STATUS_REG] = 0x34000000; - cp0_regs[CP0_CONFIG_REG] = 0x0006e463; + cp0_regs[CP0_CONFIG_REG] = 0x7006e463; /* XXX: wait for SP to finish last operation (poll halt) */ diff --git a/mupen64plus-core/src/device/r4300/cached_interp.c b/mupen64plus-core/src/device/r4300/cached_interp.c index 184dac1d..b46d99ac 100644 --- a/mupen64plus-core/src/device/r4300/cached_interp.c +++ b/mupen64plus-core/src/device/r4300/cached_interp.c @@ -283,23 +283,17 @@ void cached_interp_NOTCOMPILED2(void) #define cached_interp_BC2TL cached_interp_NI #define cached_interp_BC2TL_IDLE cached_interp_NI #define cached_interp_BC2TL_OUT cached_interp_NI -#define cached_interp_BREAK cached_interp_NI #define cached_interp_CFC0 cached_interp_NI -#define cached_interp_CFC2 cached_interp_NI #define cached_interp_CTC0 cached_interp_NI -#define cached_interp_CTC2 cached_interp_NI -#define cached_interp_DMFC0 cached_interp_NI -#define cached_interp_DMFC2 cached_interp_NI -#define cached_interp_DMTC0 cached_interp_NI -#define cached_interp_DMTC2 cached_interp_NI -#define cached_interp_LDC2 cached_interp_NI -#define cached_interp_LWC2 cached_interp_NI +#define cached_interp_DMTC0 cached_interp_MTC0 +#define cached_interp_DCFC2 cached_interp_RESERVED_COP2 +#define cached_interp_LDC2 cached_interp_RESERVED_COP2 +#define cached_interp_DCTC2 cached_interp_RESERVED_COP2 +#define cached_interp_LWC2 cached_interp_RESERVED_COP2 #define cached_interp_LLD cached_interp_NI -#define cached_interp_MFC2 cached_interp_NI -#define cached_interp_MTC2 cached_interp_NI #define cached_interp_SCD cached_interp_NI -#define cached_interp_SDC2 cached_interp_NI -#define cached_interp_SWC2 cached_interp_NI +#define cached_interp_SDC2 cached_interp_RESERVED_COP2 +#define cached_interp_SWC2 cached_interp_RESERVED_COP2 #define cached_interp_JR_IDLE cached_interp_NI #define cached_interp_JALR_IDLE cached_interp_NI #define cached_interp_CP1_ABS cached_interp_RESERVED @@ -522,6 +516,8 @@ enum r4300_opcode r4300_decode(struct precomp_instr* inst, struct r4300_core* r4 case R4300_OP_CFC0: case R4300_OP_CFC1: case R4300_OP_CFC2: + case R4300_OP_DCFC1: + case R4300_OP_DCFC2: case R4300_OP_DMFC0: case R4300_OP_DMFC1: case R4300_OP_DMFC2: @@ -617,7 +613,9 @@ enum r4300_opcode r4300_decode(struct precomp_instr* inst, struct r4300_core* r4 case R4300_OP_CTC0: case R4300_OP_CTC1: + case R4300_OP_DCTC1: case R4300_OP_CTC2: + case R4300_OP_DCTC2: case R4300_OP_DDIV: case R4300_OP_DDIVU: case R4300_OP_DIV: diff --git a/mupen64plus-core/src/device/r4300/cp0.c b/mupen64plus-core/src/device/r4300/cp0.c index ecf4818d..019d3772 100644 --- a/mupen64plus-core/src/device/r4300/cp0.c +++ b/mupen64plus-core/src/device/r4300/cp0.c @@ -61,7 +61,7 @@ void poweron_cp0(struct cp0* cp0) cp0_regs[CP0_RANDOM_REG] = UINT32_C(31); cp0_regs[CP0_STATUS_REG]= UINT32_C(0x34000000); cp0_regs[CP0_CONFIG_REG]= UINT32_C(0x6e463); - cp0_regs[CP0_PREVID_REG] = UINT32_C(0xb00); + cp0_regs[CP0_PREVID_REG] = UINT32_C(0xb10); cp0_regs[CP0_COUNT_REG] = UINT32_C(0x5000); cp0_regs[CP0_CAUSE_REG] = UINT32_C(0x5c); cp0_regs[CP0_CONTEXT_REG] = UINT32_C(0x7ffff0); @@ -91,6 +91,16 @@ uint32_t* r4300_cp0_regs(struct cp0* cp0) #endif } +uint64_t* r4300_cp0_latch(struct cp0* cp0) +{ +#ifndef NEW_DYNAREC + return &cp0->latch; +#else + /* New dynarec uses a different memory layout */ + return &cp0->new_dynarec_hot_state->cp0_latch; +#endif +} + uint32_t* r4300_cp0_last_addr(struct cp0* cp0) { return &cp0->last_addr; @@ -125,6 +135,19 @@ int check_cop1_unusable(struct r4300_core* r4300) return 0; } +int check_cop2_unusable(struct r4300_core* r4300) +{ + uint32_t* cp0_regs = r4300_cp0_regs(&r4300->cp0); + + if (!(cp0_regs[CP0_STATUS_REG] & CP0_STATUS_CU2)) + { + cp0_regs[CP0_CAUSE_REG] = CP0_CAUSE_EXCCODE_CPU | CP0_CAUSE_CE2; + exception_general(r4300); + return 1; + } + return 0; +} + void cp0_update_count(struct r4300_core* r4300) { struct cp0* cp0 = &r4300->cp0; diff --git a/mupen64plus-core/src/device/r4300/cp0.h b/mupen64plus-core/src/device/r4300/cp0.h index 78aa4c43..47a15a7c 100644 --- a/mupen64plus-core/src/device/r4300/cp0.h +++ b/mupen64plus-core/src/device/r4300/cp0.h @@ -97,6 +97,7 @@ #define CP0_CAUSE_IP6 UINT32_C(0x00004000) #define CP0_CAUSE_IP7 UINT32_C(0x00008000) /* timer */ #define CP0_CAUSE_CE1 UINT32_C(0x10000000) +#define CP0_CAUSE_CE2 UINT32_C(0x20000000) #define CP0_CAUSE_BD UINT32_C(0x80000000) @@ -109,8 +110,8 @@ enum r4300_cp0_registers CP0_CONTEXT_REG, CP0_PAGEMASK_REG, CP0_WIRED_REG, - /* 7 is unused */ - CP0_BADVADDR_REG = 8, + CP0_UNUSED_7, + CP0_BADVADDR_REG, CP0_COUNT_REG, CP0_ENTRYHI_REG, CP0_COMPARE_REG, @@ -123,12 +124,18 @@ enum r4300_cp0_registers CP0_WATCHLO_REG, CP0_WATCHHI_REG, CP0_XCONTEXT_REG, - /* 21 - 27 are unused */ - CP0_TAGLO_REG = 28, + CP0_UNUSED_21, + CP0_UNUSED_22, + CP0_UNUSED_23, + CP0_UNUSED_24, + CP0_UNUSED_25, + CP0_PARITYERR_REG, + CP0_CACHEERR_REG, + CP0_TAGLO_REG, CP0_TAGHI_REG, CP0_ERROREPC_REG, - /* 31 is unused */ - CP0_REGS_COUNT = 32 + CP0_UNUSED_31, + CP0_REGS_COUNT }; @@ -166,7 +173,7 @@ struct interrupt_handler void (*callback)(void*); }; -enum { CP0_INTERRUPT_HANDLERS_COUNT = 13 }; +enum { CP0_INTERRUPT_HANDLERS_COUNT = 16 }; enum { INTR_UNSAFE_R4300 = 0x01, @@ -178,6 +185,7 @@ struct cp0 #ifndef NEW_DYNAREC /* New dynarec uses a different memory layout */ uint32_t regs[CP0_REGS_COUNT]; + uint64_t latch; #endif /* set to avoid savestates/reset if state may be inconsistent @@ -220,6 +228,7 @@ void init_cp0(struct cp0* cp0, unsigned int count_per_op, unsigned int count_per void poweron_cp0(struct cp0* cp0); uint32_t* r4300_cp0_regs(struct cp0* cp0); +uint64_t* r4300_cp0_latch(struct cp0* cp0); uint32_t* r4300_cp0_last_addr(struct cp0* cp0); unsigned int* r4300_cp0_next_interrupt(struct cp0* cp0); @@ -229,6 +238,7 @@ unsigned int* r4300_cp0_next_interrupt(struct cp0* cp0); int* r4300_cp0_cycle_count(struct cp0* cp0); int check_cop1_unusable(struct r4300_core* r4300); +int check_cop2_unusable(struct r4300_core* r4300); void cp0_update_count(struct r4300_core* r4300); diff --git a/mupen64plus-core/src/device/r4300/cp1.c b/mupen64plus-core/src/device/r4300/cp1.c index 6c2336d7..c744f1b6 100644 --- a/mupen64plus-core/src/device/r4300/cp1.c +++ b/mupen64plus-core/src/device/r4300/cp1.c @@ -29,6 +29,12 @@ #define FCR31_FS_BIT UINT32_C(0x1000000) +#ifdef M64P_BIG_ENDIAN +#define DOUBLE_HALF_XOR 1 +#else +#define DOUBLE_HALF_XOR 0 +#endif + void init_cp1(struct cp1* cp1, struct new_dynarec_hot_state* new_dynarec_hot_state) { #ifdef NEW_DYNAREC @@ -39,7 +45,7 @@ void init_cp1(struct cp1* cp1, struct new_dynarec_hot_state* new_dynarec_hot_sta void poweron_cp1(struct cp1* cp1) { memset(cp1->regs, 0, 32 * sizeof(cp1->regs[0])); - *r4300_cp1_fcr0(cp1) = UINT32_C(0x511); + *r4300_cp1_fcr0(cp1) = UINT32_C(0xA00); *r4300_cp1_fcr31(cp1) = 0; set_fpr_pointers(cp1, UINT32_C(0x34000000)); /* c0_status value at poweron */ @@ -81,7 +87,7 @@ uint32_t* r4300_cp1_fcr0(struct cp1* cp1) /* New dynarec uses a different memory layout */ return &cp1->fcr0; #else - return &cp1->new_dynarec_hot_state->fcr0; + return &cp1->new_dynarec_hot_state->cp1_fcr0; #endif } @@ -91,7 +97,7 @@ uint32_t* r4300_cp1_fcr31(struct cp1* cp1) /* New dynarec uses a different memory layout */ return &cp1->fcr31; #else - return &cp1->new_dynarec_hot_state->fcr31; + return &cp1->new_dynarec_hot_state->cp1_fcr31; #endif } @@ -104,7 +110,7 @@ void set_fpr_pointers(struct cp1* cp1, uint32_t newStatus) { for (i = 0; i < 32; i++) { - (r4300_cp1_regs_simple(cp1))[i] = &cp1->regs[i & ~1].float32[i & 1]; + (r4300_cp1_regs_simple(cp1))[i] = &cp1->regs[i & ~1].float32[i & 1 ^ DOUBLE_HALF_XOR]; (r4300_cp1_regs_double(cp1))[i] = &cp1->regs[i & ~1].float64; } } @@ -112,7 +118,7 @@ void set_fpr_pointers(struct cp1* cp1, uint32_t newStatus) { for (i = 0; i < 32; i++) { - (r4300_cp1_regs_simple(cp1))[i] = &cp1->regs[i].float32[0]; + (r4300_cp1_regs_simple(cp1))[i] = &cp1->regs[i].float32[DOUBLE_HALF_XOR]; (r4300_cp1_regs_double(cp1))[i] = &cp1->regs[i].float64; } } diff --git a/mupen64plus-core/src/device/r4300/cp1.h b/mupen64plus-core/src/device/r4300/cp1.h index e0ef11b1..753e8778 100644 --- a/mupen64plus-core/src/device/r4300/cp1.h +++ b/mupen64plus-core/src/device/r4300/cp1.h @@ -89,7 +89,7 @@ struct cp1 #else #define R4300_CP1_FCR0_OFFSET (\ offsetof(struct r4300_core, new_dynarec_hot_state) + \ - offsetof(struct new_dynarec_hot_state, fcr0)) + offsetof(struct new_dynarec_hot_state, cp1_fcr0)) #endif #ifndef NEW_DYNAREC @@ -99,7 +99,7 @@ struct cp1 #else #define R4300_CP1_FCR31_OFFSET (\ offsetof(struct r4300_core, new_dynarec_hot_state) + \ - offsetof(struct new_dynarec_hot_state, fcr31)) + offsetof(struct new_dynarec_hot_state, cp1_fcr31)) #endif void init_cp1(struct cp1* cp1, struct new_dynarec_hot_state* new_dynarec_hot_state); diff --git a/mupen64plus-core/src/device/r4300/cp2.c b/mupen64plus-core/src/device/r4300/cp2.c new file mode 100644 index 00000000..3833931d --- /dev/null +++ b/mupen64plus-core/src/device/r4300/cp2.c @@ -0,0 +1,54 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus - cp2.c * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2002 Hacktarux * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include +#include + +#include "cp0.h" +#include "cp1.h" +#include "cp2.h" + +#include "new_dynarec/new_dynarec.h" + +#define FCR31_FS_BIT UINT32_C(0x2000000) + +void init_cp2(struct cp2* cp2, struct new_dynarec_hot_state* new_dynarec_hot_state) +{ +#ifdef NEW_DYNAREC + cp2->new_dynarec_hot_state = new_dynarec_hot_state; +#endif +} + +void poweron_cp2(struct cp2* cp2) +{ + *r4300_cp2_latch(cp2) = 0; +} + +uint64_t* r4300_cp2_latch(struct cp2* cp2) +{ +#ifndef NEW_DYNAREC + /* New dynarec uses a different memory layout */ + return &cp2->latch; +#else + return &cp2->new_dynarec_hot_state->cp2_latch; +#endif +} + diff --git a/mupen64plus-core/src/device/r4300/cp2.h b/mupen64plus-core/src/device/r4300/cp2.h new file mode 100644 index 00000000..0861bb84 --- /dev/null +++ b/mupen64plus-core/src/device/r4300/cp2.h @@ -0,0 +1,45 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus - cp2.h * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2002 Hacktarux * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef M64P_DEVICE_R4300_CP2_H +#define M64P_DEVICE_R4300_CP2_H + +#include +#include "osal/preproc.h" +#include "new_dynarec/new_dynarec.h" + +struct cp2 +{ + uint64_t latch; + +#ifdef NEW_DYNAREC + /* New dynarec uses a different memory layout */ + struct new_dynarec_hot_state* new_dynarec_hot_state; +#endif +}; + +void init_cp2(struct cp2* cp2, struct new_dynarec_hot_state* new_dynarec_hot_state); +void poweron_cp2(struct cp2* cp2); + +uint64_t* r4300_cp2_latch(struct cp2* cp2); + +#endif /* M64P_DEVICE_R4300_CP2_H */ + diff --git a/mupen64plus-core/src/device/r4300/fpu.h b/mupen64plus-core/src/device/r4300/fpu.h index 51b2b59b..6d175531 100644 --- a/mupen64plus-core/src/device/r4300/fpu.h +++ b/mupen64plus-core/src/device/r4300/fpu.h @@ -28,18 +28,8 @@ #ifdef _MSC_VER #define M64P_FPU_INLINE static __inline #include +#include -typedef enum { FE_TONEAREST = 0, FE_TOWARDZERO, FE_UPWARD, FE_DOWNWARD } eRoundType; -static void fesetround(eRoundType RoundType) -{ - static const unsigned int msRound[4] = { _RC_NEAR, _RC_CHOP, _RC_UP, _RC_DOWN }; -#if defined(__x86_64__) - _controlfp(msRound[RoundType], _MCW_RC); -#else - unsigned int oldX87, oldSSE2; - __control87_2(msRound[RoundType], _MCW_RC, &oldX87, &oldSSE2); -#endif -} static __inline double round(double x) { return floor(x + 0.5); } static __inline float roundf(float x) { return (float)floor(x + 0.5); } static __inline double trunc(double x) { return (double)(int)x; } @@ -57,6 +47,27 @@ static __inline float truncf(float x) { return (float)(int)x; } #define FCR31_CMP_BIT UINT32_C(0x800000) +#define FCR31_CAUSE_BITS UINT32_C(0x01F000) + +#define FCR31_CAUSE_INEXACT_BIT UINT32_C(0x001000) +#define FCR31_CAUSE_UNDERFLOW_BIT UINT32_C(0x002000) +#define FCR31_CAUSE_OVERFLOW_BIT UINT32_C(0x004000) +#define FCR31_CAUSE_DIVBYZERO_BIT UINT32_C(0x008000) +#define FCR31_CAUSE_INVALIDOP_BIT UINT32_C(0x010000) +#define FCR31_CAUSE_UNIMPLOP_BIT UINT32_C(0x020000) + +#define FCR31_ENABLE_INEXACT_BIT UINT32_C(0x000080) +#define FCR31_ENABLE_UNDERFLOW_BIT UINT32_C(0x000100) +#define FCR31_ENABLE_OVERFLOW_BIT UINT32_C(0x000200) +#define FCR31_ENABLE_DIVBYZERO_BIT UINT32_C(0x000400) +#define FCR31_ENABLE_INVALIDOP_BIT UINT32_C(0x000800) + +#define FCR31_FLAG_INEXACT_BIT UINT32_C(0x000004) +#define FCR31_FLAG_UNDERFLOW_BIT UINT32_C(0x000008) +#define FCR31_FLAG_OVERFLOW_BIT UINT32_C(0x000010) +#define FCR31_FLAG_DIVBYZERO_BIT UINT32_C(0x000020) +#define FCR31_FLAG_INVALIDOP_BIT UINT32_C(0x000040) + M64P_FPU_INLINE void set_rounding(uint32_t fcr31) { @@ -78,33 +89,219 @@ M64P_FPU_INLINE void set_rounding(uint32_t fcr31) #endif } -M64P_FPU_INLINE void cvt_s_w(uint32_t fcr31, const int32_t* source, float* dest) +#ifdef ACCURATE_FPU_BEHAVIOR +M64P_FPU_INLINE void fpu_reset_cause(uint32_t* fcr31) +{ + (*fcr31) &= ~FCR31_CAUSE_BITS; +} + +M64P_FPU_INLINE void fpu_reset_exceptions() +{ + feclearexcept(FE_ALL_EXCEPT); +} + +M64P_FPU_INLINE int fpu_check_exceptions(uint32_t* fcr31) +{ + int fexceptions; + + fexceptions = fetestexcept(FE_ALL_EXCEPT) & FE_ALL_EXCEPT; + + if (fexceptions & FE_DIVBYZERO) + { + (*fcr31) |= FCR31_CAUSE_DIVBYZERO_BIT; + (*fcr31) |= FCR31_FLAG_DIVBYZERO_BIT; + } + if (fexceptions & FE_INEXACT) + { + (*fcr31) |= FCR31_CAUSE_INEXACT_BIT; + (*fcr31) |= FCR31_FLAG_INEXACT_BIT; + } + if (fexceptions & FE_UNDERFLOW) + { + (*fcr31) |= FCR31_CAUSE_UNDERFLOW_BIT; + (*fcr31) |= FCR31_FLAG_UNDERFLOW_BIT; + } + if (fexceptions & FE_OVERFLOW) + { + (*fcr31) |= FCR31_CAUSE_OVERFLOW_BIT; + (*fcr31) |= FCR31_FLAG_OVERFLOW_BIT; + } + if (fexceptions & FE_INVALID) + { + (*fcr31) |= FCR31_CAUSE_INVALIDOP_BIT; + (*fcr31) |= FCR31_FLAG_INVALIDOP_BIT; + } + + return 0; // TODO: exceptions +} + +M64P_FPU_INLINE void fpu_check_input_float(uint32_t* fcr31, const float* value) +{ + switch (fpclassify(*value)) + { + default: + case FP_SUBNORMAL: // TODO + return; + case FP_NAN: + (*fcr31) |= FCR31_CAUSE_INVALIDOP_BIT; + (*fcr31) |= FCR31_FLAG_INVALIDOP_BIT; + break; + } +} + +M64P_FPU_INLINE void fpu_check_input_double(uint32_t* fcr31, const double* value) { - set_rounding(fcr31); + switch (fpclassify(*value)) + { + default: + case FP_SUBNORMAL: // TODO + return; + case FP_NAN: + (*fcr31) |= FCR31_CAUSE_INVALIDOP_BIT; + (*fcr31) |= FCR31_FLAG_INVALIDOP_BIT; + break; + } +} + +M64P_FPU_INLINE void fpu_check_output_float(uint32_t* fcr31, const float* value) +{ + switch (fpclassify(*value)) + { + case FP_SUBNORMAL: + (*fcr31) |= FCR31_CAUSE_UNDERFLOW_BIT; + (*fcr31) |= FCR31_FLAG_UNDERFLOW_BIT; + (*fcr31) |= FCR31_CAUSE_INEXACT_BIT; + (*fcr31) |= FCR31_FLAG_INEXACT_BIT; + break; + + case FP_NAN: + break; + + default: + break; + } +} + +M64P_FPU_INLINE void fpu_check_output_double(uint32_t* fcr31, const double* value) +{ + switch (fpclassify(*value)) + { + case FP_SUBNORMAL: + (*fcr31) |= FCR31_CAUSE_UNDERFLOW_BIT; + (*fcr31) |= FCR31_FLAG_UNDERFLOW_BIT; + (*fcr31) |= FCR31_CAUSE_INEXACT_BIT; + (*fcr31) |= FCR31_FLAG_INEXACT_BIT; + break; + + case FP_NAN: + break; + + default: + break; + } +} +#else +M64P_FPU_INLINE void fpu_reset_cause(uint32_t* fcr31) +{ +} + +M64P_FPU_INLINE void fpu_reset_exceptions() +{ +} + +M64P_FPU_INLINE int fpu_check_exceptions(uint32_t* fcr31) +{ + return 0; +} + +M64P_FPU_INLINE void fpu_check_input_float(uint32_t* fcr31, const float* value) +{ +} + +M64P_FPU_INLINE void fpu_check_input_double(uint32_t* fcr31, const double* value) +{ +} + +M64P_FPU_INLINE void fpu_check_output_float(uint32_t* fcr31, const float* value) +{ +} + +M64P_FPU_INLINE void fpu_check_output_double(uint32_t* fcr31, const double* value) +{ +} +#endif /* ACCURATE_FPU_BEHAVIOR */ + +M64P_FPU_INLINE void cvt_s_w(uint32_t* fcr31, const int32_t* source, float* dest) +{ + set_rounding(*fcr31); + + fpu_reset_cause(fcr31); + fpu_reset_exceptions(); + *dest = (float)*source; + + fpu_check_exceptions(fcr31); + fpu_check_output_float(fcr31, dest); } -M64P_FPU_INLINE void cvt_d_w(const int32_t* source, double* dest) +M64P_FPU_INLINE void cvt_d_w(uint32_t* fcr31, const int32_t* source, double* dest) { + fpu_reset_cause(fcr31); + fpu_reset_exceptions(); + *dest = (double)*source; + + fpu_check_exceptions(fcr31); + fpu_check_output_double(fcr31, dest); } -M64P_FPU_INLINE void cvt_s_l(uint32_t fcr31, const int64_t* source, float* dest) +M64P_FPU_INLINE void cvt_s_l(uint32_t* fcr31, const int64_t* source, float* dest) { - set_rounding(fcr31); + set_rounding(*fcr31); + + fpu_reset_cause(fcr31); + fpu_reset_exceptions(); + *dest = (float)*source; + + fpu_check_exceptions(fcr31); + fpu_check_output_float(fcr31, dest); } -M64P_FPU_INLINE void cvt_d_l(uint32_t fcr31, const int64_t* source, double* dest) +M64P_FPU_INLINE void cvt_d_l(uint32_t* fcr31, const int64_t* source, double* dest) { - set_rounding(fcr31); + set_rounding(*fcr31); + + fpu_reset_cause(fcr31); + fpu_reset_exceptions(); + *dest = (double)*source; + + fpu_check_exceptions(fcr31); + fpu_check_output_double(fcr31, dest); } -M64P_FPU_INLINE void cvt_d_s(const float* source, double* dest) +M64P_FPU_INLINE void cvt_d_s(uint32_t* fcr31, const float* source, double* dest) { + fpu_reset_cause(fcr31); + fpu_check_input_float(fcr31, source); + + fpu_reset_exceptions(); + *dest = (double)*source; + + fpu_check_exceptions(fcr31); + fpu_check_output_double(fcr31, dest); } -M64P_FPU_INLINE void cvt_s_d(uint32_t fcr31, const double* source, float* dest) +M64P_FPU_INLINE void cvt_s_d(uint32_t* fcr31, const double* source, float* dest) { - set_rounding(fcr31); + set_rounding(*fcr31); + + fpu_reset_cause(fcr31); + fpu_check_input_double(fcr31, source); + + fpu_reset_exceptions(); + *dest = (float)*source; + + fpu_check_exceptions(fcr31); + fpu_check_output_float(fcr31, dest); } M64P_FPU_INLINE void round_l_s(const float* source, int64_t* dest) @@ -234,53 +431,97 @@ M64P_FPU_INLINE void floor_w_d(const double* source, int32_t* dest) *dest = (int32_t)floor(*source); } -M64P_FPU_INLINE void cvt_w_s(uint32_t fcr31, const float* source, int32_t* dest) +M64P_FPU_INLINE void cvt_w_s(uint32_t* fcr31, const float* source, int32_t* dest) { - switch(fcr31 & 3) + fpu_reset_cause(fcr31); + + fpu_check_input_float(fcr31, source); + + fpu_reset_exceptions(); + + switch(*fcr31 & 3) { case 0: round_w_s(source, dest); return; case 1: trunc_w_s(source, dest); return; case 2: ceil_w_s (source, dest); return; case 3: floor_w_s(source, dest); return; } + + fpu_check_exceptions(fcr31); } -M64P_FPU_INLINE void cvt_w_d(uint32_t fcr31, const double* source, int32_t* dest) +M64P_FPU_INLINE void cvt_w_d(uint32_t* fcr31, const double* source, int32_t* dest) { - switch(fcr31 & 3) + fpu_reset_cause(fcr31); + + fpu_check_input_double(fcr31, source); + + fpu_reset_exceptions(); + + switch(*fcr31 & 3) { case 0: round_w_d(source, dest); return; case 1: trunc_w_d(source, dest); return; case 2: ceil_w_d (source, dest); return; case 3: floor_w_d(source, dest); return; } + + fpu_check_exceptions(fcr31); } -M64P_FPU_INLINE void cvt_l_s(uint32_t fcr31, const float* source, int64_t* dest) +M64P_FPU_INLINE void cvt_l_s(uint32_t* fcr31, const float* source, int64_t* dest) { - switch(fcr31 & 3) + fpu_reset_cause(fcr31); + fpu_reset_exceptions(); + + switch(*fcr31 & 3) { case 0: round_l_s(source, dest); return; case 1: trunc_l_s(source, dest); return; case 2: ceil_l_s (source, dest); return; case 3: floor_l_s(source, dest); return; } + + fpu_check_exceptions(fcr31); } -M64P_FPU_INLINE void cvt_l_d(uint32_t fcr31, const double* source, int64_t* dest) +M64P_FPU_INLINE void cvt_l_d(uint32_t* fcr31, const double* source, int64_t* dest) { - switch(fcr31 & 3) + fpu_reset_cause(fcr31); + fpu_reset_exceptions(); + + switch(*fcr31 & 3) { case 0: round_l_d(source, dest); return; case 1: trunc_l_d(source, dest); return; case 2: ceil_l_d (source, dest); return; case 3: floor_l_d(source, dest); return; } + + fpu_check_exceptions(fcr31); } -M64P_FPU_INLINE void c_f_s(uint32_t* fcr31) +M64P_FPU_INLINE void c_f_s(uint32_t* fcr31, const float* source, const float* target) { + fpu_reset_cause(fcr31); + + if ((isnan(*source) || isnan(*target))) + { + (*fcr31) &= ~FCR31_CMP_BIT; + (*fcr31) |= FCR31_CAUSE_INVALIDOP_BIT; + (*fcr31) |= FCR31_FLAG_INVALIDOP_BIT; + return; + } + *fcr31 &= ~FCR31_CMP_BIT; } M64P_FPU_INLINE void c_un_s(uint32_t* fcr31, const float* source, const float* target) { + fpu_reset_cause(fcr31); + + if ((isnan(*source) || isnan(*target))) + { + (*fcr31) |= FCR31_CMP_BIT; + return; + } + *fcr31 = (isnan(*source) || isnan(*target)) ? (*fcr31 | FCR31_CMP_BIT) : (*fcr31 & ~FCR31_CMP_BIT); @@ -288,14 +529,28 @@ M64P_FPU_INLINE void c_un_s(uint32_t* fcr31, const float* source, const float* t M64P_FPU_INLINE void c_eq_s(uint32_t* fcr31, const float* source, const float* target) { - if (isnan(*source) || isnan(*target)) { *fcr31 &= ~FCR31_CMP_BIT; return; } + fpu_reset_cause(fcr31); + + if (isnan(*source) || isnan(*target)) + { + (*fcr31) &= ~FCR31_CMP_BIT; + return; + } + *fcr31 = (*source == *target) ? (*fcr31 | FCR31_CMP_BIT) : (*fcr31 & ~FCR31_CMP_BIT); } M64P_FPU_INLINE void c_ueq_s(uint32_t* fcr31, const float* source, const float* target) { - if (isnan(*source) || isnan(*target)) { *fcr31 |= FCR31_CMP_BIT; return; } + fpu_reset_cause(fcr31); + + if (isnan(*source) || isnan(*target)) + { + (*fcr31) |= FCR31_CMP_BIT; + return; + } + *fcr31 = (*source == *target) ? (*fcr31 | FCR31_CMP_BIT) : (*fcr31 & ~FCR31_CMP_BIT); @@ -303,14 +558,28 @@ M64P_FPU_INLINE void c_ueq_s(uint32_t* fcr31, const float* source, const float* M64P_FPU_INLINE void c_olt_s(uint32_t* fcr31, const float* source, const float* target) { - if (isnan(*source) || isnan(*target)) { *fcr31 &= ~FCR31_CMP_BIT; return; } + fpu_reset_cause(fcr31); + + if (isnan(*source) || isnan(*target)) + { + (*fcr31) &= ~FCR31_CMP_BIT; + return; + } + *fcr31 = (*source < *target) ? (*fcr31 | FCR31_CMP_BIT) : (*fcr31 & ~FCR31_CMP_BIT); } M64P_FPU_INLINE void c_ult_s(uint32_t* fcr31, const float* source, const float* target) { - if (isnan(*source) || isnan(*target)) { *fcr31 |= FCR31_CMP_BIT; return; } + fpu_reset_cause(fcr31); + + if (isnan(*source) || isnan(*target)) + { + (*fcr31) |= FCR31_CMP_BIT; + return; + } + *fcr31 = (*source < *target) ? (*fcr31 | FCR31_CMP_BIT) : (*fcr31 & ~FCR31_CMP_BIT); @@ -318,14 +587,28 @@ M64P_FPU_INLINE void c_ult_s(uint32_t* fcr31, const float* source, const float* M64P_FPU_INLINE void c_ole_s(uint32_t* fcr31, const float* source, const float* target) { - if (isnan(*source) || isnan(*target)) { *fcr31 &= ~FCR31_CMP_BIT; return; } + fpu_reset_cause(fcr31); + + if (isnan(*source) || isnan(*target)) + { + (*fcr31) &= ~FCR31_CMP_BIT; + return; + } + *fcr31 = (*source <= *target) ? (*fcr31 | FCR31_CMP_BIT) : (*fcr31 & ~FCR31_CMP_BIT); } M64P_FPU_INLINE void c_ule_s(uint32_t* fcr31, const float* source, const float* target) { - if (isnan(*source) || isnan(*target)) { *fcr31 |= FCR31_CMP_BIT; return; } + fpu_reset_cause(fcr31); + + if (isnan(*source) || isnan(*target)) + { + (*fcr31) |= FCR31_CMP_BIT; + return; + } + *fcr31 = (*source <= *target) ? (*fcr31 | FCR31_CMP_BIT) : (*fcr31 &~ FCR31_CMP_BIT); @@ -333,25 +616,61 @@ M64P_FPU_INLINE void c_ule_s(uint32_t* fcr31, const float* source, const float* M64P_FPU_INLINE void c_sf_s(uint32_t* fcr31, const float* source, const float* target) { - //if (isnan(*source) || isnan(*target)) // FIXME - exception + fpu_reset_cause(fcr31); + + if (isnan(*source) || isnan(*target)) + { + (*fcr31) &= ~FCR31_CMP_BIT; + (*fcr31) |= FCR31_CAUSE_INVALIDOP_BIT; + (*fcr31) |= FCR31_FLAG_INVALIDOP_BIT; + return; + } + *fcr31 &= ~FCR31_CMP_BIT; } M64P_FPU_INLINE void c_ngle_s(uint32_t* fcr31, const float* source, const float* target) { - //if (isnan(*source) || isnan(*target)) // FIXME - exception + fpu_reset_cause(fcr31); + + if (isnan(*source) || isnan(*target)) + { + (*fcr31) |= FCR31_CMP_BIT; + (*fcr31) |= FCR31_CAUSE_INVALIDOP_BIT; + (*fcr31) |= FCR31_FLAG_INVALIDOP_BIT; + return; + } + *fcr31 &= ~FCR31_CMP_BIT; } M64P_FPU_INLINE void c_seq_s(uint32_t* fcr31, const float* source, const float* target) { - //if (isnan(*source) || isnan(*target)) // FIXME - exception + fpu_reset_cause(fcr31); + + if (isnan(*source) || isnan(*target)) + { + (*fcr31) &= ~FCR31_CMP_BIT; + (*fcr31) |= FCR31_CAUSE_INVALIDOP_BIT; + (*fcr31) |= FCR31_FLAG_INVALIDOP_BIT; + return; + } + *fcr31 = (*source == *target) ? (*fcr31 | FCR31_CMP_BIT) : (*fcr31 &~ FCR31_CMP_BIT); } M64P_FPU_INLINE void c_ngl_s(uint32_t* fcr31, const float* source, const float* target) { - //if (isnan(*source) || isnan(*target)) // FIXME - exception + fpu_reset_cause(fcr31); + + if (isnan(*source) || isnan(*target)) + { + (*fcr31) |= FCR31_CMP_BIT; + (*fcr31) |= FCR31_CAUSE_INVALIDOP_BIT; + (*fcr31) |= FCR31_FLAG_INVALIDOP_BIT; + return; + } + *fcr31 = (*source == *target) ? (*fcr31 | FCR31_CMP_BIT) : (*fcr31 & ~FCR31_CMP_BIT); @@ -359,14 +678,32 @@ M64P_FPU_INLINE void c_ngl_s(uint32_t* fcr31, const float* source, const float* M64P_FPU_INLINE void c_lt_s(uint32_t* fcr31, const float* source, const float* target) { - //if (isnan(*source) || isnan(*target)) // FIXME - exception + fpu_reset_cause(fcr31); + + if (isnan(*source) || isnan(*target)) + { + (*fcr31) &= ~FCR31_CMP_BIT; + (*fcr31) |= FCR31_CAUSE_INVALIDOP_BIT; + (*fcr31) |= FCR31_FLAG_INVALIDOP_BIT; + return; + } + *fcr31 = (*source < *target) ? (*fcr31 | FCR31_CMP_BIT) : (*fcr31 & ~FCR31_CMP_BIT); } M64P_FPU_INLINE void c_nge_s(uint32_t* fcr31, const float* source, const float* target) { - //if (isnan(*source) || isnan(*target)) // FIXME - exception + fpu_reset_cause(fcr31); + + if (isnan(*source) || isnan(*target)) + { + (*fcr31) |= FCR31_CMP_BIT; + (*fcr31) |= FCR31_CAUSE_INVALIDOP_BIT; + (*fcr31) |= FCR31_FLAG_INVALIDOP_BIT; + return; + } + *fcr31 = (*source < *target) ? (*fcr31 | FCR31_CMP_BIT) : (*fcr31 & ~FCR31_CMP_BIT); @@ -374,14 +711,32 @@ M64P_FPU_INLINE void c_nge_s(uint32_t* fcr31, const float* source, const float* M64P_FPU_INLINE void c_le_s(uint32_t* fcr31, const float* source, const float* target) { - //if (isnan(*source) || isnan(*target)) // FIXME - exception + fpu_reset_cause(fcr31); + + if (isnan(*source) || isnan(*target)) + { + (*fcr31) &= ~FCR31_CMP_BIT; + (*fcr31) |= FCR31_CAUSE_INVALIDOP_BIT; + (*fcr31) |= FCR31_FLAG_INVALIDOP_BIT; + return; + } + *fcr31 = (*source <= *target) ? (*fcr31 | FCR31_CMP_BIT) : (*fcr31 & ~FCR31_CMP_BIT); } M64P_FPU_INLINE void c_ngt_s(uint32_t* fcr31, const float* source, const float* target) { - //if (isnan(*source) || isnan(*target)) // FIXME - exception + fpu_reset_cause(fcr31); + + if (isnan(*source) || isnan(*target)) + { + (*fcr31) |= FCR31_CMP_BIT; + (*fcr31) |= FCR31_CAUSE_INVALIDOP_BIT; + (*fcr31) |= FCR31_FLAG_INVALIDOP_BIT; + return; + } + *fcr31 = (*source <= *target) ? (*fcr31 | FCR31_CMP_BIT) : (*fcr31 & ~FCR31_CMP_BIT); @@ -389,10 +744,20 @@ M64P_FPU_INLINE void c_ngt_s(uint32_t* fcr31, const float* source, const float* M64P_FPU_INLINE void c_f_d(uint32_t* fcr31) { + fpu_reset_cause(fcr31); + *fcr31 &= ~FCR31_CMP_BIT; } M64P_FPU_INLINE void c_un_d(uint32_t* fcr31, const double* source, const double* target) { + fpu_reset_cause(fcr31); + + if (isnan(*source) || isnan(*target)) + { + (*fcr31) &= ~FCR31_CMP_BIT; + return; + } + *fcr31 = (isnan(*source) || isnan(*target)) ? (*fcr31 | FCR31_CMP_BIT) : (*fcr31 & ~FCR31_CMP_BIT); @@ -400,14 +765,28 @@ M64P_FPU_INLINE void c_un_d(uint32_t* fcr31, const double* source, const double* M64P_FPU_INLINE void c_eq_d(uint32_t* fcr31, const double* source, const double* target) { - if (isnan(*source) || isnan(*target)) { *fcr31 &= ~FCR31_CMP_BIT; return; } + fpu_reset_cause(fcr31); + + if (isnan(*source) || isnan(*target)) + { + (*fcr31) &= ~FCR31_CMP_BIT; + return; + } + *fcr31 = (*source == *target) ? (*fcr31 | FCR31_CMP_BIT) : (*fcr31 & ~FCR31_CMP_BIT); } M64P_FPU_INLINE void c_ueq_d(uint32_t* fcr31, const double* source, const double* target) { - if (isnan(*source) || isnan(*target)) { *fcr31 |= FCR31_CMP_BIT; return; } + fpu_reset_cause(fcr31); + + if (isnan(*source) || isnan(*target)) + { + (*fcr31) |= FCR31_CMP_BIT; + return; + } + *fcr31 = (*source == *target) ? (*fcr31 | FCR31_CMP_BIT) : (*fcr31 & ~FCR31_CMP_BIT); @@ -415,14 +794,28 @@ M64P_FPU_INLINE void c_ueq_d(uint32_t* fcr31, const double* source, const double M64P_FPU_INLINE void c_olt_d(uint32_t* fcr31, const double* source, const double* target) { - if (isnan(*source) || isnan(*target)) { *fcr31 &= ~FCR31_CMP_BIT; return; } + fpu_reset_cause(fcr31); + + if (isnan(*source) || isnan(*target)) + { + (*fcr31) &= ~FCR31_CMP_BIT; + return; + } + *fcr31 = (*source < *target) ? (*fcr31 | FCR31_CMP_BIT) : (*fcr31 & ~FCR31_CMP_BIT); } M64P_FPU_INLINE void c_ult_d(uint32_t* fcr31, const double* source, const double* target) { - if (isnan(*source) || isnan(*target)) { *fcr31 |= FCR31_CMP_BIT; return; } + fpu_reset_cause(fcr31); + + if (isnan(*source) || isnan(*target)) + { + (*fcr31) |= FCR31_CMP_BIT; + return; + } + *fcr31 = (*source < *target) ? (*fcr31 | FCR31_CMP_BIT) : (*fcr31 & ~FCR31_CMP_BIT); @@ -430,14 +823,28 @@ M64P_FPU_INLINE void c_ult_d(uint32_t* fcr31, const double* source, const double M64P_FPU_INLINE void c_ole_d(uint32_t* fcr31, const double* source, const double* target) { - if (isnan(*source) || isnan(*target)) { *fcr31 &= ~FCR31_CMP_BIT; return; } + fpu_reset_cause(fcr31); + + if (isnan(*source) || isnan(*target)) + { + (*fcr31) &= ~FCR31_CMP_BIT; + return; + } + *fcr31 = (*source <= *target) ? (*fcr31 | FCR31_CMP_BIT) : (*fcr31 & ~FCR31_CMP_BIT); } M64P_FPU_INLINE void c_ule_d(uint32_t* fcr31, const double* source, const double* target) { - if (isnan(*source) || isnan(*target)) { *fcr31 |= FCR31_CMP_BIT; return; } + fpu_reset_cause(fcr31); + + if (isnan(*source) || isnan(*target)) + { + (*fcr31) |= FCR31_CMP_BIT; + return; + } + *fcr31 = (*source <= *target) ? (*fcr31 | FCR31_CMP_BIT) : (*fcr31 & ~FCR31_CMP_BIT); @@ -445,25 +852,61 @@ M64P_FPU_INLINE void c_ule_d(uint32_t* fcr31, const double* source, const double M64P_FPU_INLINE void c_sf_d(uint32_t* fcr31, const double* source, const double* target) { - //if (isnan(*source) || isnan(*target)) // FIXME - exception + fpu_reset_cause(fcr31); + + if (isnan(*source) || isnan(*target)) + { + (*fcr31) &= ~FCR31_CMP_BIT; + (*fcr31) |= FCR31_CAUSE_INVALIDOP_BIT; + (*fcr31) |= FCR31_FLAG_INVALIDOP_BIT; + return; + } + *fcr31 &= ~FCR31_CMP_BIT; } M64P_FPU_INLINE void c_ngle_d(uint32_t* fcr31, const double* source, const double* target) { - //if (isnan(*source) || isnan(*target)) // FIXME - exception + fpu_reset_cause(fcr31); + + if (isnan(*source) || isnan(*target)) + { + (*fcr31) |= FCR31_CMP_BIT; + (*fcr31) |= FCR31_CAUSE_INVALIDOP_BIT; + (*fcr31) |= FCR31_FLAG_INVALIDOP_BIT; + return; + } + *fcr31 &= ~FCR31_CMP_BIT; } M64P_FPU_INLINE void c_seq_d(uint32_t* fcr31, const double* source, const double* target) { - //if (isnan(*source) || isnan(*target)) // FIXME - exception + fpu_reset_cause(fcr31); + + if (isnan(*source) || isnan(*target)) + { + (*fcr31) &= ~FCR31_CMP_BIT; + (*fcr31) |= FCR31_CAUSE_INVALIDOP_BIT; + (*fcr31) |= FCR31_FLAG_INVALIDOP_BIT; + return; + } + *fcr31 = (*source == *target) ? (*fcr31 | FCR31_CMP_BIT) : (*fcr31 & ~FCR31_CMP_BIT); } M64P_FPU_INLINE void c_ngl_d(uint32_t* fcr31, const double* source, const double* target) { - //if (isnan(*source) || isnan(*target)) // FIXME - exception + fpu_reset_cause(fcr31); + + if (isnan(*source) || isnan(*target)) + { + (*fcr31) |= FCR31_CMP_BIT; + (*fcr31) |= FCR31_CAUSE_INVALIDOP_BIT; + (*fcr31) |= FCR31_FLAG_INVALIDOP_BIT; + return; + } + *fcr31 = (*source == *target) ? (*fcr31 | FCR31_CMP_BIT) : (*fcr31 & ~FCR31_CMP_BIT); @@ -471,14 +914,32 @@ M64P_FPU_INLINE void c_ngl_d(uint32_t* fcr31, const double* source, const double M64P_FPU_INLINE void c_lt_d(uint32_t* fcr31, const double* source, const double* target) { - //if (isnan(*source) || isnan(*target)) // FIXME - exception + fpu_reset_cause(fcr31); + + if (isnan(*source) || isnan(*target)) + { + (*fcr31) &= ~FCR31_CMP_BIT; + (*fcr31) |= FCR31_CAUSE_INVALIDOP_BIT; + (*fcr31) |= FCR31_FLAG_INVALIDOP_BIT; + return; + } + *fcr31 = (*source < *target) ? (*fcr31 | FCR31_CMP_BIT) : (*fcr31 & ~FCR31_CMP_BIT); } M64P_FPU_INLINE void c_nge_d(uint32_t* fcr31, const double* source, const double* target) { - //if (isnan(*source) || isnan(*target)) // FIXME - exception + fpu_reset_cause(fcr31); + + if (isnan(*source) || isnan(*target)) + { + (*fcr31) |= FCR31_CMP_BIT; + (*fcr31) |= FCR31_CAUSE_INVALIDOP_BIT; + (*fcr31) |= FCR31_FLAG_INVALIDOP_BIT; + return; + } + *fcr31 = (*source < *target) ? (*fcr31 | FCR31_CMP_BIT) : (*fcr31 & ~FCR31_CMP_BIT); @@ -486,93 +947,244 @@ M64P_FPU_INLINE void c_nge_d(uint32_t* fcr31, const double* source, const double M64P_FPU_INLINE void c_le_d(uint32_t* fcr31, const double* source, const double* target) { - //if (isnan(*source) || isnan(*target)) // FIXME - exception + fpu_reset_cause(fcr31); + + if (isnan(*source) || isnan(*target)) + { + (*fcr31) &= ~FCR31_CMP_BIT; + (*fcr31) |= FCR31_CAUSE_INVALIDOP_BIT; + (*fcr31) |= FCR31_FLAG_INVALIDOP_BIT; + return; + } + *fcr31 = (*source <= *target) ? (*fcr31 | FCR31_CMP_BIT) : (*fcr31 & ~FCR31_CMP_BIT); } M64P_FPU_INLINE void c_ngt_d(uint32_t* fcr31, const double* source, const double* target) { - //if (isnan(*source) || isnan(*target)) // FIXME - exception + fpu_reset_cause(fcr31); + + if (isnan(*source) || isnan(*target)) + { + (*fcr31) |= FCR31_CMP_BIT; + (*fcr31) |= FCR31_CAUSE_INVALIDOP_BIT; + (*fcr31) |= FCR31_FLAG_INVALIDOP_BIT; + return; + } + *fcr31 = (*source <= *target) ? (*fcr31 | FCR31_CMP_BIT) : (*fcr31 & ~FCR31_CMP_BIT); } -M64P_FPU_INLINE void add_s(uint32_t fcr31, const float* source1, const float* source2, float* target) +M64P_FPU_INLINE void add_s(uint32_t* fcr31, const float* source1, const float* source2, float* target) { - set_rounding(fcr31); + set_rounding(*fcr31); + + fpu_reset_cause(fcr31); + fpu_check_input_float(fcr31, source1); + fpu_check_input_float(fcr31, source2); + + fpu_reset_exceptions(); + *target = *source1 + *source2; + + fpu_check_exceptions(fcr31); + + fpu_check_output_float(fcr31, target); } -M64P_FPU_INLINE void sub_s(uint32_t fcr31, const float* source1, const float* source2, float* target) +M64P_FPU_INLINE void sub_s(uint32_t* fcr31, const float* source1, const float* source2, float* target) { - set_rounding(fcr31); + set_rounding(*fcr31); + + fpu_reset_cause(fcr31); + fpu_check_input_float(fcr31, source1); + fpu_check_input_float(fcr31, source2); + + fpu_reset_exceptions(); + *target = *source1 - *source2; + + fpu_check_exceptions(fcr31); + + fpu_check_output_float(fcr31, target); } -M64P_FPU_INLINE void mul_s(uint32_t fcr31, const float* source1, const float* source2, float* target) +M64P_FPU_INLINE void mul_s(uint32_t* fcr31, const float* source1, const float* source2, float* target) { - set_rounding(fcr31); + set_rounding(*fcr31); + + fpu_reset_cause(fcr31); + fpu_check_input_float(fcr31, source1); + fpu_check_input_float(fcr31, source2); + + fpu_reset_exceptions(); + *target = *source1 * *source2; + + fpu_check_exceptions(fcr31); + + fpu_check_output_float(fcr31, target); } -M64P_FPU_INLINE void div_s(uint32_t fcr31, const float* source1, const float* source2, float* target) +M64P_FPU_INLINE void div_s(uint32_t* fcr31, const float* source1, const float* source2, float* target) { - set_rounding(fcr31); + set_rounding(*fcr31); + + fpu_reset_cause(fcr31); + fpu_check_input_float(fcr31, source1); + fpu_check_input_float(fcr31, source2); + + fpu_reset_exceptions(); + *target = *source1 / *source2; + + fpu_check_exceptions(fcr31); + + fpu_check_output_float(fcr31, target); } -M64P_FPU_INLINE void sqrt_s(uint32_t fcr31, const float* source, float* target) +M64P_FPU_INLINE void sqrt_s(uint32_t* fcr31, const float* source, float* target) { - set_rounding(fcr31); + set_rounding(*fcr31); + + fpu_reset_cause(fcr31); + fpu_check_input_float(fcr31, source); + + fpu_reset_exceptions(); + *target = sqrtf(*source); + + fpu_check_exceptions(fcr31); + + fpu_check_output_float(fcr31, target); } -M64P_FPU_INLINE void abs_s(const float* source, float* target) +M64P_FPU_INLINE void abs_s(uint32_t* fcr31, const float* source, float* target) { + fpu_reset_cause(fcr31); + fpu_check_input_float(fcr31, source); + *target = fabsf(*source); + + fpu_check_output_float(fcr31, target); } M64P_FPU_INLINE void mov_s(const float* source, float* target) { *target = *source; } -M64P_FPU_INLINE void neg_s(const float* source, float* target) +M64P_FPU_INLINE void neg_s(uint32_t* fcr31, const float* source, float* target) { + fpu_reset_cause(fcr31); + fpu_check_input_float(fcr31, source); + *target = - *source; + + fpu_check_output_float(fcr31, target); } -M64P_FPU_INLINE void add_d(uint32_t fcr31, const double* source1, const double* source2, double* target) +M64P_FPU_INLINE void add_d(uint32_t* fcr31, const double* source1, const double* source2, double* target) { - set_rounding(fcr31); + set_rounding(*fcr31); + + fpu_reset_cause(fcr31); + fpu_check_input_double(fcr31, source1); + fpu_check_input_double(fcr31, source2); + + fpu_reset_exceptions(); + *target = *source1 + *source2; + + fpu_check_exceptions(fcr31); + + fpu_check_output_double(fcr31, target); } -M64P_FPU_INLINE void sub_d(uint32_t fcr31, const double* source1, const double* source2, double* target) +M64P_FPU_INLINE void sub_d(uint32_t* fcr31, const double* source1, const double* source2, double* target) { - set_rounding(fcr31); + set_rounding(*fcr31); + + fpu_reset_cause(fcr31); + fpu_check_input_double(fcr31, source1); + fpu_check_input_double(fcr31, source2); + + fpu_reset_exceptions(); + *target = *source1 - *source2; + + fpu_check_exceptions(fcr31); + + fpu_check_output_double(fcr31, target); } -M64P_FPU_INLINE void mul_d(uint32_t fcr31, const double* source1, const double* source2, double* target) +M64P_FPU_INLINE void mul_d(uint32_t* fcr31, const double* source1, const double* source2, double* target) { - set_rounding(fcr31); + set_rounding(*fcr31); + + fpu_reset_cause(fcr31); + fpu_check_input_double(fcr31, source1); + fpu_check_input_double(fcr31, source2); + + fpu_reset_exceptions(); + *target = *source1 * *source2; + + fpu_check_exceptions(fcr31); + + fpu_check_output_double(fcr31, target); } -M64P_FPU_INLINE void div_d(uint32_t fcr31, const double* source1, const double* source2, double* target) +M64P_FPU_INLINE void div_d(uint32_t* fcr31, const double* source1, const double* source2, double* target) { - set_rounding(fcr31); + set_rounding(*fcr31); + + fpu_reset_cause(fcr31); + fpu_check_input_double(fcr31, source1); + fpu_check_input_double(fcr31, source2); + + fpu_reset_exceptions(); + *target = *source1 / *source2; + + fpu_check_exceptions(fcr31); + + fpu_check_output_double(fcr31, target); } -M64P_FPU_INLINE void sqrt_d(uint32_t fcr31, const double* source, double* target) +M64P_FPU_INLINE void sqrt_d(uint32_t* fcr31, const double* source, double* target) { - set_rounding(fcr31); + set_rounding(*fcr31); + + + fpu_reset_cause(fcr31); + fpu_check_input_double(fcr31, source); + + fpu_reset_exceptions(); + *target = sqrt(*source); + + fpu_check_exceptions(fcr31); + + fpu_check_output_double(fcr31, target); } -M64P_FPU_INLINE void abs_d(const double* source, double* target) +M64P_FPU_INLINE void abs_d(uint32_t* fcr31, const double* source, double* target) { + fpu_reset_cause(fcr31); + fpu_check_input_double(fcr31, source); + + fpu_reset_exceptions(); + *target = fabs(*source); + + fpu_check_exceptions(fcr31); + + fpu_check_output_double(fcr31, target); } M64P_FPU_INLINE void mov_d(const double* source, double* target) { *target = *source; } -M64P_FPU_INLINE void neg_d(const double* source, double* target) +M64P_FPU_INLINE void neg_d(uint32_t* fcr31, const double* source, double* target) { + fpu_reset_cause(fcr31); + fpu_check_input_double(fcr31, source); + *target = - *source; + + fpu_check_output_double(fcr31, target); } #endif /* M64P_DEVICE_R4300_FPU_H */ diff --git a/mupen64plus-core/src/device/r4300/idec.c b/mupen64plus-core/src/device/r4300/idec.c index bf90183f..831e26e4 100644 --- a/mupen64plus-core/src/device/r4300/idec.c +++ b/mupen64plus-core/src/device/r4300/idec.c @@ -145,7 +145,9 @@ #define CACHE { R4300_OP_CACHE, IDEC_CACHEOP_BASE_OFFSET } #define CFC0 { R4300_OP_CFC0, IDEC_RT_CCR0 } #define CFC1 { R4300_OP_CFC1, IDEC_RT_FCR } +#define DCFC1 { R4300_OP_DCFC1, IDEC_RT_FCR } #define CFC2 { R4300_OP_CFC2, IDEC_RT_CCR2 } +#define DCFC2 { R4300_OP_DCFC2, IDEC_RT_CCR2 } #define CP1_ABS { R4300_OP_CP1_ABS, IDEC_FD_FS_FMT } #define CP1_ADD { R4300_OP_CP1_ADD, IDEC_FD_FS_FT_FMT } #define CP1_C_EQ { R4300_OP_CP1_C_EQ, IDEC_FS_FT_FMT } @@ -184,7 +186,9 @@ #define CP1_TRUNC_W { R4300_OP_CP1_TRUNC_W, IDEC_FDW_FS_FMT } #define CTC0 { R4300_OP_CTC0, IDEC_RT_CCR0 } #define CTC1 { R4300_OP_CTC1, IDEC_RT_FCR } +#define DCTC1 { R4300_OP_DCTC1, IDEC_RT_FCR } #define CTC2 { R4300_OP_CTC2, IDEC_RT_CCR2 } +#define DCTC2 { R4300_OP_DCTC2, IDEC_RT_CCR2 } #define DADD { R4300_OP_DADD, IDEC_RD_RS_RT } #define DADDI { R4300_OP_DADDI, IDEC_RT_RS_SIMM } #define DADDIU { R4300_OP_DADDIU, IDEC_RT_RS_SIMM } @@ -387,8 +391,8 @@ static const struct r4300_idec r4300_op_table[] = { /* COP1 opcodes table * 240-247 */ - MFC1, DMFC1, CFC1, RESERVED, - MTC1, DMTC1, CTC1, RESERVED, + MFC1, DMFC1, CFC1, DCFC1, + MTC1, DMTC1, CTC1, DCTC1, /* BC1 opcodes table * 248-255 */ @@ -416,8 +420,8 @@ static const struct r4300_idec r4300_op_table[] = { /* COP2 opcodes table * 320-327 */ - MFC2, DMFC2, CFC2, RESERVED, - MTC2, DMTC2, CTC2, RESERVED, + MFC2, DMFC2, CFC2, DCFC2, + MTC2, DMTC2, CTC2, DCTC2, /* BC2 opcodes table * 328-335 */ diff --git a/mupen64plus-core/src/device/r4300/interrupt.c b/mupen64plus-core/src/device/r4300/interrupt.c index dd37e6c7..9c81a184 100644 --- a/mupen64plus-core/src/device/r4300/interrupt.c +++ b/mupen64plus-core/src/device/r4300/interrupt.c @@ -641,6 +641,21 @@ void gen_interrupt(struct r4300_core* r4300) call_interrupt_handler(&r4300->cp0, 12); break; + case DD_MC_INT: + remove_interrupt_event(&r4300->cp0); + call_interrupt_handler(&r4300->cp0, 13); + break; + + case DD_BM_INT: + remove_interrupt_event(&r4300->cp0); + call_interrupt_handler(&r4300->cp0, 14); + break; + + case DD_DV_INT: + remove_interrupt_event(&r4300->cp0); + call_interrupt_handler(&r4300->cp0, 15); + break; + default: DebugMessage(M64MSG_ERROR, "Unknown interrupt queue event type %.8X.", r4300->cp0.q.first->data.type); remove_interrupt_event(&r4300->cp0); diff --git a/mupen64plus-core/src/device/r4300/interrupt.h b/mupen64plus-core/src/device/r4300/interrupt.h index 7f50fe2b..3963cdc9 100644 --- a/mupen64plus-core/src/device/r4300/interrupt.h +++ b/mupen64plus-core/src/device/r4300/interrupt.h @@ -54,17 +54,20 @@ void check_int_handler(void* opaque); void special_int_handler(void* opaque); void nmi_int_handler(void* opaque); -#define VI_INT 0x001 -#define COMPARE_INT 0x002 -#define CHECK_INT 0x004 -#define SI_INT 0x008 -#define PI_INT 0x010 -#define SPECIAL_INT 0x020 -#define AI_INT 0x040 -#define SP_INT 0x080 -#define DP_INT 0x100 -#define HW2_INT 0x200 -#define NMI_INT 0x400 -#define RSP_DMA_EVT 0x800 +#define VI_INT 0x0001 +#define COMPARE_INT 0x0002 +#define CHECK_INT 0x0004 +#define SI_INT 0x0008 +#define PI_INT 0x0010 +#define SPECIAL_INT 0x0020 +#define AI_INT 0x0040 +#define SP_INT 0x0080 +#define DP_INT 0x0100 +#define HW2_INT 0x0200 +#define NMI_INT 0x0400 +#define RSP_DMA_EVT 0x0800 +#define DD_MC_INT 0x1000 +#define DD_BM_INT 0x2000 +#define DD_DV_INT 0x4000 #endif /* M64P_DEVICE_R4300_INTERRUPT_H */ diff --git a/mupen64plus-core/src/device/r4300/mips_instructions.def b/mupen64plus-core/src/device/r4300/mips_instructions.def index 89218aa3..d6d46bd7 100644 --- a/mupen64plus-core/src/device/r4300/mips_instructions.def +++ b/mupen64plus-core/src/device/r4300/mips_instructions.def @@ -114,8 +114,36 @@ DECLARE_INSTRUCTION(NI) DECLARE_INSTRUCTION(RESERVED) { DECLARE_R4300 + + uint32_t* cp0_regs = r4300_cp0_regs(&r4300->cp0); + DebugMessage(M64MSG_ERROR, "reserved opcode: %" PRIX32 ":%" PRIX32, PCADDR, *fast_mem_access(r4300, PCADDR)); - *r4300_stop(r4300) = 1; + + cp0_regs[CP0_CAUSE_REG] = CP0_CAUSE_EXCCODE_RI; + exception_general(r4300); +} + +DECLARE_INSTRUCTION(RESERVED_COP2) +{ + DECLARE_R4300 + + uint32_t* cp0_regs = r4300_cp0_regs(&r4300->cp0); + + if (check_cop2_unusable(r4300)) { return; } + + cp0_regs[CP0_CAUSE_REG] = CP0_CAUSE_EXCCODE_RI | CP0_CAUSE_CE2; + exception_general(r4300); +} + +/* BREAK */ + +DECLARE_INSTRUCTION(BREAK) +{ + DECLARE_R4300 + uint32_t* cp0_regs = r4300_cp0_regs(&r4300->cp0); + + cp0_regs[CP0_CAUSE_REG] = CP0_CAUSE_EXCCODE_BP; + exception_general(r4300); } /* Load instructions */ @@ -682,14 +710,14 @@ DECLARE_INSTRUCTION(DSRL32) DECLARE_INSTRUCTION(SRA) { DECLARE_R4300 - rrd = SE32((int32_t) rrt32 >> rsa); + rrd = SE32(rrt >> rsa); ADD_TO_PC(1); } DECLARE_INSTRUCTION(SRAV) { DECLARE_R4300 - rrd = SE32((int32_t) rrt32 >> (rrs32 & 0x1F)); + rrd = SE32(rrt >> (rrs32 & 0x1F)); ADD_TO_PC(1); } @@ -1213,6 +1241,7 @@ DECLARE_INSTRUCTION(MFC0) { DECLARE_R4300 uint32_t* cp0_regs = r4300_cp0_regs(&r4300->cp0); + uint64_t* cp0_latch = r4300_cp0_latch(&r4300->cp0); switch(rfs) { @@ -1220,12 +1249,64 @@ DECLARE_INSTRUCTION(MFC0) cp0_update_count(r4300); cp0_regs[CP0_RANDOM_REG] = (cp0_regs[CP0_COUNT_REG]/r4300->cp0.count_per_op % (32 - cp0_regs[CP0_WIRED_REG])) + cp0_regs[CP0_WIRED_REG]; + rrt = SE32(cp0_regs[rfs]); break; case CP0_COUNT_REG: cp0_update_count(r4300); + rrt = SE32(cp0_regs[rfs]); + break; + case CP0_UNUSED_7: + case CP0_UNUSED_21: + case CP0_UNUSED_22: + case CP0_UNUSED_23: + case CP0_UNUSED_24: + case CP0_UNUSED_25: + case CP0_UNUSED_31: + rrt = (*cp0_latch); + break; + default: + rrt = SE32(cp0_regs[rfs]); break; } - rrt = SE32(cp0_regs[rfs]); + + ADD_TO_PC(1); +} + +DECLARE_INSTRUCTION(DMFC0) +{ + DECLARE_R4300 + uint32_t* cp0_regs = r4300_cp0_regs(&r4300->cp0); + uint64_t* cp0_latch = r4300_cp0_latch(&r4300->cp0); + + switch(rfs) + { + case CP0_RANDOM_REG: + cp0_update_count(r4300); + cp0_regs[CP0_RANDOM_REG] = (cp0_regs[CP0_COUNT_REG]/r4300->cp0.count_per_op % (32 - cp0_regs[CP0_WIRED_REG])) + + cp0_regs[CP0_WIRED_REG]; + rrt = cp0_regs[rfs]; + break; + case CP0_COUNT_REG: + cp0_update_count(r4300); + rrt = cp0_regs[rfs]; + break; + case CP0_EPC_REG: + rrt = SE32(cp0_regs[rfs]); + break; + case CP0_UNUSED_7: + case CP0_UNUSED_21: + case CP0_UNUSED_22: + case CP0_UNUSED_23: + case CP0_UNUSED_24: + case CP0_UNUSED_25: + case CP0_UNUSED_31: + rrt = (*cp0_latch); + break; + default: + rrt = cp0_regs[rfs]; + break; + } + ADD_TO_PC(1); } @@ -1233,8 +1314,11 @@ DECLARE_INSTRUCTION(MTC0) { DECLARE_R4300 uint32_t* cp0_regs = r4300_cp0_regs(&r4300->cp0); + uint64_t* cp0_latch = r4300_cp0_latch(&r4300->cp0); int* cp0_cycle_count = r4300_cp0_cycle_count(&r4300->cp0); + (*cp0_latch) = rrt32; + switch(rfs) { case CP0_INDEX_REG: @@ -1261,7 +1345,7 @@ DECLARE_INSTRUCTION(MTC0) cp0_regs[CP0_PAGEMASK_REG] = rrt32 & UINT32_C(0x01FFE000); break; case CP0_WIRED_REG: - cp0_regs[CP0_WIRED_REG] = rrt32; + cp0_regs[CP0_WIRED_REG] = rrt32 & UINT32_C(0x0000003F); cp0_regs[CP0_RANDOM_REG] = UINT32_C(31); break; case CP0_BADVADDR_REG: @@ -1292,6 +1376,8 @@ DECLARE_INSTRUCTION(MTC0) cp0_regs[CP0_CAUSE_REG] &= ~CP0_CAUSE_IP7; break; case CP0_STATUS_REG: + rrt32 &= ~UINT32_C(0x080000); /* 19th bit isn't writable */ + if((rrt32 & CP0_STATUS_FR) != (cp0_regs[CP0_STATUS_REG] & CP0_STATUS_FR)) set_fpr_pointers(&r4300->cp1, rrt32); @@ -1313,7 +1399,12 @@ DECLARE_INSTRUCTION(MTC0) case CP0_PREVID_REG: break; case CP0_CONFIG_REG: - cp0_regs[CP0_CONFIG_REG] = rrt32; + cp0_regs[CP0_CONFIG_REG] = (rrt32 & UINT32_C(0x0000000F)) + | (cp0_regs[CP0_CONFIG_REG] & UINT32_C(0x00008000)) + | (cp0_regs[CP0_CONFIG_REG] & UINT32_C(0x7FFFFFFF)); + break; + case CP0_LLADDR_REG: + cp0_regs[CP0_LLADDR_REG] = rrt32; break; case CP0_WATCHLO_REG: cp0_regs[CP0_WATCHLO_REG] = rrt32; @@ -1321,6 +1412,13 @@ DECLARE_INSTRUCTION(MTC0) case CP0_WATCHHI_REG: cp0_regs[CP0_WATCHHI_REG] = rrt32; break; + case CP0_XCONTEXT_REG: + break; + case CP0_CACHEERR_REG: + break; + case CP0_PARITYERR_REG: + cp0_regs[CP0_PARITYERR_REG] = rrt32 & UINT32_C(0x000000FF); + break; case CP0_TAGLO_REG: cp0_regs[CP0_TAGLO_REG] = rrt32 & UINT32_C(0x0FFFFFC0); break; @@ -1331,9 +1429,9 @@ DECLARE_INSTRUCTION(MTC0) cp0_regs[CP0_ERROREPC_REG] = rrt32; break; default: - DebugMessage(M64MSG_ERROR, "Unknown MTC0 write: %d", rfs); - *r4300_stop(r4300)=1; + break; } + ADD_TO_PC(1); } @@ -1414,6 +1512,22 @@ DECLARE_INSTRUCTION(CFC1) ADD_TO_PC(1); } +DECLARE_INSTRUCTION(DCFC1) +{ + DECLARE_R4300 + if (check_cop1_unusable(r4300)) { return; } + + uint32_t* cp0_regs = r4300_cp0_regs(&r4300->cp0); + + fpu_reset_cause(r4300_cp1_fcr31(&r4300->cp1)); + + /* Unimplemented Operation */ + (*r4300_cp1_fcr31(&r4300->cp1)) |= FCR31_CAUSE_UNIMPLOP_BIT; + + cp0_regs[CP0_CAUSE_REG] = CP0_CAUSE_EXCCODE_FPE; + exception_general(r4300); +} + DECLARE_INSTRUCTION(MTC1) { DECLARE_R4300 @@ -1444,13 +1558,79 @@ DECLARE_INSTRUCTION(CTC1) ADD_TO_PC(1); } +DECLARE_INSTRUCTION(DCTC1) +{ + DECLARE_R4300 + if (check_cop1_unusable(r4300)) { return; } + + uint32_t* cp0_regs = r4300_cp0_regs(&r4300->cp0); + + fpu_reset_cause(r4300_cp1_fcr31(&r4300->cp1)); + + /* Unimplemented Operation */ + (*r4300_cp1_fcr31(&r4300->cp1)) |= FCR31_CAUSE_UNIMPLOP_BIT; + + cp0_regs[CP0_CAUSE_REG] = CP0_CAUSE_EXCCODE_FPE; + exception_general(r4300); +} + +/* CP2 load/store instructions */ + +DECLARE_INSTRUCTION(MFC2) +{ + DECLARE_R4300 + if (check_cop2_unusable(r4300)) { return; } + rrt = SE32(*r4300_cp2_latch(&r4300->cp2)); + ADD_TO_PC(1); +} + +DECLARE_INSTRUCTION(DMFC2) +{ + DECLARE_R4300 + if (check_cop2_unusable(r4300)) { return; } + rrt = (*r4300_cp2_latch(&r4300->cp2)); + ADD_TO_PC(1); +} + +DECLARE_INSTRUCTION(CFC2) +{ + DECLARE_R4300 + if (check_cop2_unusable(r4300)) { return; } + rrt = SE32(*r4300_cp2_latch(&r4300->cp2)); + ADD_TO_PC(1); +} + +DECLARE_INSTRUCTION(MTC2) +{ + DECLARE_R4300 + if (check_cop2_unusable(r4300)) { return; } + (*r4300_cp2_latch(&r4300->cp2)) = rrt; + ADD_TO_PC(1); +} + +DECLARE_INSTRUCTION(DMTC2) +{ + DECLARE_R4300 + if (check_cop2_unusable(r4300)) { return; } + (*r4300_cp2_latch(&r4300->cp2)) = rrt; + ADD_TO_PC(1); +} + +DECLARE_INSTRUCTION(CTC2) +{ + DECLARE_R4300 + if (check_cop2_unusable(r4300)) { return; } + (*r4300_cp2_latch(&r4300->cp2)) = rrt; + ADD_TO_PC(1); +} + /* CP1 computational instructions */ DECLARE_INSTRUCTION(ABS_S) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - abs_s((r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cffd]); + abs_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cffd]); ADD_TO_PC(1); } @@ -1458,7 +1638,7 @@ DECLARE_INSTRUCTION(ABS_D) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - abs_d((r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cffd]); + abs_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cffd]); ADD_TO_PC(1); } @@ -1466,7 +1646,7 @@ DECLARE_INSTRUCTION(ADD_S) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - add_s(*r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cfft], (r4300_cp1_regs_simple(&r4300->cp1))[cffd]); + add_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cfft], (r4300_cp1_regs_simple(&r4300->cp1))[cffd]); ADD_TO_PC(1); } @@ -1474,7 +1654,7 @@ DECLARE_INSTRUCTION(ADD_D) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - add_d(*r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cfft], (r4300_cp1_regs_double(&r4300->cp1))[cffd]); + add_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cfft], (r4300_cp1_regs_double(&r4300->cp1))[cffd]); ADD_TO_PC(1); } @@ -1482,11 +1662,7 @@ DECLARE_INSTRUCTION(DIV_S) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - if(((*r4300_cp1_fcr31(&r4300->cp1)) & UINT32_C(0x400)) && *(r4300_cp1_regs_simple(&r4300->cp1))[cfft] == 0) - { - DebugMessage(M64MSG_ERROR, "DIV_S by 0"); - } - div_s(*r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cfft], (r4300_cp1_regs_simple(&r4300->cp1))[cffd]); + div_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cfft], (r4300_cp1_regs_simple(&r4300->cp1))[cffd]); ADD_TO_PC(1); } @@ -1494,16 +1670,7 @@ DECLARE_INSTRUCTION(DIV_D) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - if(((*r4300_cp1_fcr31(&r4300->cp1)) & UINT32_C(0x400)) && *(r4300_cp1_regs_double(&r4300->cp1))[cfft] == 0) - { - //(*r4300_cp1_fcr31(&r4300->cp1)) |= 0x8020; - /*(*r4300_cp1_fcr31(&r4300->cp1)) |= 0x8000; - Cause = 15 << 2; - exception_general(r4300);*/ - DebugMessage(M64MSG_ERROR, "DIV_D by 0"); - //return; - } - div_d(*r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cfft], (r4300_cp1_regs_double(&r4300->cp1))[cffd]); + div_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cfft], (r4300_cp1_regs_double(&r4300->cp1))[cffd]); ADD_TO_PC(1); } @@ -1527,7 +1694,7 @@ DECLARE_INSTRUCTION(MUL_S) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - mul_s(*r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cfft], (r4300_cp1_regs_simple(&r4300->cp1))[cffd]); + mul_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cfft], (r4300_cp1_regs_simple(&r4300->cp1))[cffd]); ADD_TO_PC(1); } @@ -1535,7 +1702,7 @@ DECLARE_INSTRUCTION(MUL_D) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - mul_d(*r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cfft], (r4300_cp1_regs_double(&r4300->cp1))[cffd]); + mul_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cfft], (r4300_cp1_regs_double(&r4300->cp1))[cffd]); ADD_TO_PC(1); } @@ -1543,7 +1710,7 @@ DECLARE_INSTRUCTION(NEG_S) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - neg_s((r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cffd]); + neg_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cffd]); ADD_TO_PC(1); } @@ -1551,7 +1718,7 @@ DECLARE_INSTRUCTION(NEG_D) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - neg_d((r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cffd]); + neg_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cffd]); ADD_TO_PC(1); } @@ -1559,7 +1726,7 @@ DECLARE_INSTRUCTION(SQRT_S) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - sqrt_s(*r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cffd]); + sqrt_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cffd]); ADD_TO_PC(1); } @@ -1567,7 +1734,7 @@ DECLARE_INSTRUCTION(SQRT_D) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - sqrt_d(*r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cffd]); + sqrt_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cffd]); ADD_TO_PC(1); } @@ -1575,7 +1742,7 @@ DECLARE_INSTRUCTION(SUB_S) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - sub_s(*r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cfft], (r4300_cp1_regs_simple(&r4300->cp1))[cffd]); + sub_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cfft], (r4300_cp1_regs_simple(&r4300->cp1))[cffd]); ADD_TO_PC(1); } @@ -1583,7 +1750,7 @@ DECLARE_INSTRUCTION(SUB_D) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - sub_d(*r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cfft], (r4300_cp1_regs_double(&r4300->cp1))[cffd]); + sub_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cfft], (r4300_cp1_regs_double(&r4300->cp1))[cffd]); ADD_TO_PC(1); } @@ -1719,7 +1886,7 @@ DECLARE_INSTRUCTION(CVT_S_D) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - cvt_s_d(*r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cffd]); + cvt_s_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cffd]); ADD_TO_PC(1); } @@ -1727,7 +1894,7 @@ DECLARE_INSTRUCTION(CVT_S_W) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - cvt_s_w(*r4300_cp1_fcr31(&r4300->cp1), (int32_t*) (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cffd]); + cvt_s_w(r4300_cp1_fcr31(&r4300->cp1), (int32_t*) (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cffd]); ADD_TO_PC(1); } @@ -1735,7 +1902,7 @@ DECLARE_INSTRUCTION(CVT_S_L) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - cvt_s_l(*r4300_cp1_fcr31(&r4300->cp1), (int64_t*) (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cffd]); + cvt_s_l(r4300_cp1_fcr31(&r4300->cp1), (int64_t*) (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cffd]); ADD_TO_PC(1); } @@ -1743,7 +1910,7 @@ DECLARE_INSTRUCTION(CVT_D_S) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - cvt_d_s((r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cffd]); + cvt_d_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cffd]); ADD_TO_PC(1); } @@ -1751,7 +1918,7 @@ DECLARE_INSTRUCTION(CVT_D_W) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - cvt_d_w((int32_t*) (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cffd]); + cvt_d_w(r4300_cp1_fcr31(&r4300->cp1), (int32_t*) (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cffd]); ADD_TO_PC(1); } @@ -1759,7 +1926,7 @@ DECLARE_INSTRUCTION(CVT_D_L) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - cvt_d_l(*r4300_cp1_fcr31(&r4300->cp1), (int64_t*) (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cffd]); + cvt_d_l(r4300_cp1_fcr31(&r4300->cp1), (int64_t*) (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cffd]); ADD_TO_PC(1); } @@ -1767,7 +1934,7 @@ DECLARE_INSTRUCTION(CVT_W_S) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - cvt_w_s(*r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (int32_t*) (r4300_cp1_regs_simple(&r4300->cp1))[cffd]); + cvt_w_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (int32_t*) (r4300_cp1_regs_simple(&r4300->cp1))[cffd]); ADD_TO_PC(1); } @@ -1775,7 +1942,7 @@ DECLARE_INSTRUCTION(CVT_W_D) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - cvt_w_d(*r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (int32_t*) (r4300_cp1_regs_simple(&r4300->cp1))[cffd]); + cvt_w_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (int32_t*) (r4300_cp1_regs_simple(&r4300->cp1))[cffd]); ADD_TO_PC(1); } @@ -1783,7 +1950,7 @@ DECLARE_INSTRUCTION(CVT_L_S) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - cvt_l_s(*r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (int64_t*) (r4300_cp1_regs_double(&r4300->cp1))[cffd]); + cvt_l_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (int64_t*) (r4300_cp1_regs_double(&r4300->cp1))[cffd]); ADD_TO_PC(1); } @@ -1791,7 +1958,7 @@ DECLARE_INSTRUCTION(CVT_L_D) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - cvt_l_d(*r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (int64_t*) (r4300_cp1_regs_double(&r4300->cp1))[cffd]); + cvt_l_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (int64_t*) (r4300_cp1_regs_double(&r4300->cp1))[cffd]); ADD_TO_PC(1); } @@ -1801,7 +1968,7 @@ DECLARE_INSTRUCTION(C_F_S) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - c_f_s(r4300_cp1_fcr31(&r4300->cp1)); + c_f_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cffd]); ADD_TO_PC(1); } @@ -1929,11 +2096,6 @@ DECLARE_INSTRUCTION(C_SF_S) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - if (isnan(*(r4300_cp1_regs_simple(&r4300->cp1))[cffs]) || isnan(*(r4300_cp1_regs_simple(&r4300->cp1))[cfft])) - { - DebugMessage(M64MSG_ERROR, "Invalid operation exception in C opcode"); - *r4300_stop(r4300)=1; - } c_sf_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cfft]); ADD_TO_PC(1); } @@ -1941,11 +2103,6 @@ DECLARE_INSTRUCTION(C_SF_S) DECLARE_INSTRUCTION(C_SF_D) { DECLARE_R4300 - if (isnan(*(r4300_cp1_regs_double(&r4300->cp1))[cffs]) || isnan(*(r4300_cp1_regs_double(&r4300->cp1))[cfft])) - { - DebugMessage(M64MSG_ERROR, "Invalid operation exception in C opcode"); - *r4300_stop(r4300)=1; - } c_sf_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cfft]); ADD_TO_PC(1); } @@ -1954,11 +2111,6 @@ DECLARE_INSTRUCTION(C_NGLE_S) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - if (isnan(*(r4300_cp1_regs_simple(&r4300->cp1))[cffs]) || isnan(*(r4300_cp1_regs_simple(&r4300->cp1))[cfft])) - { - DebugMessage(M64MSG_ERROR, "Invalid operation exception in C opcode"); - *r4300_stop(r4300)=1; - } c_ngle_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cfft]); ADD_TO_PC(1); } @@ -1966,11 +2118,6 @@ DECLARE_INSTRUCTION(C_NGLE_S) DECLARE_INSTRUCTION(C_NGLE_D) { DECLARE_R4300 - if (isnan(*(r4300_cp1_regs_double(&r4300->cp1))[cffs]) || isnan(*(r4300_cp1_regs_double(&r4300->cp1))[cfft])) - { - DebugMessage(M64MSG_ERROR, "Invalid operation exception in C opcode"); - *r4300_stop(r4300)=1; - } c_ngle_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cfft]); ADD_TO_PC(1); } @@ -1979,11 +2126,6 @@ DECLARE_INSTRUCTION(C_SEQ_S) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - if (isnan(*(r4300_cp1_regs_simple(&r4300->cp1))[cffs]) || isnan(*(r4300_cp1_regs_simple(&r4300->cp1))[cfft])) - { - DebugMessage(M64MSG_ERROR, "Invalid operation exception in C opcode"); - *r4300_stop(r4300)=1; - } c_seq_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cfft]); ADD_TO_PC(1); } @@ -1991,11 +2133,6 @@ DECLARE_INSTRUCTION(C_SEQ_S) DECLARE_INSTRUCTION(C_SEQ_D) { DECLARE_R4300 - if (isnan(*(r4300_cp1_regs_double(&r4300->cp1))[cffs]) || isnan(*(r4300_cp1_regs_double(&r4300->cp1))[cfft])) - { - DebugMessage(M64MSG_ERROR, "Invalid operation exception in C opcode"); - *r4300_stop(r4300)=1; - } c_seq_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cfft]); ADD_TO_PC(1); } @@ -2004,11 +2141,6 @@ DECLARE_INSTRUCTION(C_NGL_S) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - if (isnan(*(r4300_cp1_regs_simple(&r4300->cp1))[cffs]) || isnan(*(r4300_cp1_regs_simple(&r4300->cp1))[cfft])) - { - DebugMessage(M64MSG_ERROR, "Invalid operation exception in C opcode"); - *r4300_stop(r4300)=1; - } c_ngl_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cfft]); ADD_TO_PC(1); } @@ -2016,11 +2148,6 @@ DECLARE_INSTRUCTION(C_NGL_S) DECLARE_INSTRUCTION(C_NGL_D) { DECLARE_R4300 - if (isnan(*(r4300_cp1_regs_double(&r4300->cp1))[cffs]) || isnan(*(r4300_cp1_regs_double(&r4300->cp1))[cfft])) - { - DebugMessage(M64MSG_ERROR, "Invalid operation exception in C opcode"); - *r4300_stop(r4300)=1; - } c_ngl_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cfft]); ADD_TO_PC(1); } @@ -2029,11 +2156,6 @@ DECLARE_INSTRUCTION(C_LT_S) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - if (isnan(*(r4300_cp1_regs_simple(&r4300->cp1))[cffs]) || isnan(*(r4300_cp1_regs_simple(&r4300->cp1))[cfft])) - { - DebugMessage(M64MSG_ERROR, "Invalid operation exception in C opcode"); - *r4300_stop(r4300)=1; - } c_lt_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cfft]); ADD_TO_PC(1); } @@ -2042,11 +2164,6 @@ DECLARE_INSTRUCTION(C_LT_D) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - if (isnan(*(r4300_cp1_regs_double(&r4300->cp1))[cffs]) || isnan(*(r4300_cp1_regs_double(&r4300->cp1))[cfft])) - { - DebugMessage(M64MSG_ERROR, "Invalid operation exception in C opcode"); - *r4300_stop(r4300)=1; - } c_lt_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cfft]); ADD_TO_PC(1); } @@ -2055,11 +2172,6 @@ DECLARE_INSTRUCTION(C_NGE_S) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - if (isnan(*(r4300_cp1_regs_simple(&r4300->cp1))[cffs]) || isnan(*(r4300_cp1_regs_simple(&r4300->cp1))[cfft])) - { - DebugMessage(M64MSG_ERROR, "Invalid operation exception in C opcode"); - *r4300_stop(r4300)=1; - } c_nge_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cfft]); ADD_TO_PC(1); } @@ -2068,11 +2180,6 @@ DECLARE_INSTRUCTION(C_NGE_D) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - if (isnan(*(r4300_cp1_regs_double(&r4300->cp1))[cffs]) || isnan(*(r4300_cp1_regs_double(&r4300->cp1))[cfft])) - { - DebugMessage(M64MSG_ERROR, "Invalid operation exception in C opcode"); - *r4300_stop(r4300)=1; - } c_nge_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cfft]); ADD_TO_PC(1); } @@ -2081,11 +2188,6 @@ DECLARE_INSTRUCTION(C_LE_S) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - if (isnan(*(r4300_cp1_regs_simple(&r4300->cp1))[cffs]) || isnan(*(r4300_cp1_regs_simple(&r4300->cp1))[cfft])) - { - DebugMessage(M64MSG_ERROR, "Invalid operation exception in C opcode"); - *r4300_stop(r4300)=1; - } c_le_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cfft]); ADD_TO_PC(1); } @@ -2094,11 +2196,6 @@ DECLARE_INSTRUCTION(C_LE_D) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - if (isnan(*(r4300_cp1_regs_double(&r4300->cp1))[cffs]) || isnan(*(r4300_cp1_regs_double(&r4300->cp1))[cfft])) - { - DebugMessage(M64MSG_ERROR, "Invalid operation exception in C opcode"); - *r4300_stop(r4300)=1; - } c_le_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cfft]); ADD_TO_PC(1); } @@ -2107,11 +2204,6 @@ DECLARE_INSTRUCTION(C_NGT_S) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - if (isnan(*(r4300_cp1_regs_simple(&r4300->cp1))[cffs]) || isnan(*(r4300_cp1_regs_simple(&r4300->cp1))[cfft])) - { - DebugMessage(M64MSG_ERROR, "Invalid operation exception in C opcode"); - *r4300_stop(r4300)=1; - } c_ngt_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_simple(&r4300->cp1))[cfft]); ADD_TO_PC(1); } @@ -2120,11 +2212,6 @@ DECLARE_INSTRUCTION(C_NGT_D) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - if (isnan(*(r4300_cp1_regs_double(&r4300->cp1))[cffs]) || isnan(*(r4300_cp1_regs_double(&r4300->cp1))[cfft])) - { - DebugMessage(M64MSG_ERROR, "Invalid operation exception in C opcode"); - *r4300_stop(r4300)=1; - } c_ngt_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cfft]); ADD_TO_PC(1); } diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/arm/assem_arm.c b/mupen64plus-core/src/device/r4300/new_dynarec/arm/assem_arm.c index ae80f266..95b1ba3e 100644 --- a/mupen64plus-core/src/device/r4300/new_dynarec/arm/assem_arm.c +++ b/mupen64plus-core/src/device/r4300/new_dynarec/arm/assem_arm.c @@ -21,7 +21,7 @@ /* ease access to fp-addressed variables */ #define fp_cycle_count (offsetof(struct new_dynarec_hot_state, cycle_count)) #define fp_invc_ptr (offsetof(struct new_dynarec_hot_state, invc_ptr)) -#define fp_fcr31 (offsetof(struct new_dynarec_hot_state, fcr31)) +#define fp_fcr31 (offsetof(struct new_dynarec_hot_state, cp1_fcr31)) #define fp_regs (offsetof(struct new_dynarec_hot_state, regs)) #define fp_hi (offsetof(struct new_dynarec_hot_state, hi)) #define fp_lo (offsetof(struct new_dynarec_hot_state, lo)) @@ -3129,69 +3129,63 @@ static void fconv_assemble_arm(int i,struct regstat *i_regs) save_regs(reglist); if(opcode2[i]==0x14&&(source[i]&0x3f)==0x20) { - if(fs>=0) emit_mov(fs,ARG1_REG); - else emit_loadreg(FSREG,ARG1_REG); + emit_addimm(FP,fp_fcr31,ARG1_REG); emit_readptr((u_int)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>>11)&0x1f],ARG2_REG); emit_readptr((u_int)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>> 6)&0x1f],ARG3_REG); emit_call((int)cvt_s_w); } if(opcode2[i]==0x14&&(source[i]&0x3f)==0x21) { + emit_addimm(FP,fp_fcr31,ARG1_REG); emit_readptr((u_int)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>>11)&0x1f],ARG1_REG); emit_readptr((u_int)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>> 6)&0x1f],ARG2_REG); emit_call((int)cvt_d_w); } if(opcode2[i]==0x15&&(source[i]&0x3f)==0x20) { - if(fs>=0) emit_mov(fs,ARG1_REG); - else emit_loadreg(FSREG,ARG1_REG); + emit_addimm(FP,fp_fcr31,ARG1_REG); emit_readptr((u_int)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>>11)&0x1f],ARG2_REG); emit_readptr((u_int)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>> 6)&0x1f],ARG3_REG); emit_call((int)cvt_s_l); } if(opcode2[i]==0x15&&(source[i]&0x3f)==0x21) { - if(fs>=0) emit_mov(fs,ARG1_REG); - else emit_loadreg(FSREG,ARG1_REG); + emit_addimm(FP,fp_fcr31,ARG1_REG); emit_readptr((u_int)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>>11)&0x1f],ARG2_REG); emit_readptr((u_int)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>> 6)&0x1f],ARG3_REG); emit_call((int)cvt_d_l); } if(opcode2[i]==0x10&&(source[i]&0x3f)==0x21) { + emit_addimm(FP,fp_fcr31,ARG1_REG); emit_readptr((u_int)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>>11)&0x1f],ARG1_REG); emit_readptr((u_int)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>> 6)&0x1f],ARG2_REG); emit_call((int)cvt_d_s); } if(opcode2[i]==0x10&&(source[i]&0x3f)==0x24) { - if(fs>=0) emit_mov(fs,ARG1_REG); - else emit_loadreg(FSREG,ARG1_REG); + emit_addimm(FP,fp_fcr31,ARG1_REG); emit_readptr((u_int)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>>11)&0x1f],ARG2_REG); emit_readptr((u_int)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>> 6)&0x1f],ARG3_REG); emit_call((int)cvt_w_s); } if(opcode2[i]==0x10&&(source[i]&0x3f)==0x25) { - if(fs>=0) emit_mov(fs,ARG1_REG); - else emit_loadreg(FSREG,ARG1_REG); + emit_addimm(FP,fp_fcr31,ARG1_REG); emit_readptr((u_int)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>>11)&0x1f],ARG2_REG); emit_readptr((u_int)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>> 6)&0x1f],ARG3_REG); emit_call((int)cvt_l_s); } if(opcode2[i]==0x11&&(source[i]&0x3f)==0x20) { - if(fs>=0) emit_mov(fs,ARG1_REG); - else emit_loadreg(FSREG,ARG1_REG); + emit_addimm(FP,fp_fcr31,ARG1_REG); emit_readptr((u_int)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>>11)&0x1f],ARG2_REG); emit_readptr((u_int)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>> 6)&0x1f],ARG3_REG); emit_call((int)cvt_s_d); } if(opcode2[i]==0x11&&(source[i]&0x3f)==0x24) { - if(fs>=0) emit_mov(fs,ARG1_REG); - else emit_loadreg(FSREG,ARG1_REG); + emit_addimm(FP,fp_fcr31,ARG1_REG); emit_readptr((u_int)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>>11)&0x1f],ARG2_REG); emit_readptr((u_int)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>> 6)&0x1f],ARG3_REG); emit_call((int)cvt_w_d); } if(opcode2[i]==0x11&&(source[i]&0x3f)==0x25) { - if(fs>=0) emit_mov(fs,ARG1_REG); - else emit_loadreg(FSREG,ARG1_REG); + emit_addimm(FP,fp_fcr31,ARG1_REG); emit_readptr((u_int)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>>11)&0x1f],ARG2_REG); emit_readptr((u_int)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>> 6)&0x1f],ARG3_REG); emit_call((int)cvt_l_d); @@ -3572,19 +3566,18 @@ static void float_assemble(int i,struct regstat *i_regs) switch(source[i]&0x3f) { case 0x00: case 0x01: case 0x02: case 0x03: - if(fs>=0) emit_mov(fs,ARG1_REG); - else emit_loadreg(FSREG,ARG1_REG); + emit_addimm(FP,fp_fcr31,ARG1_REG); emit_readptr((u_int)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>>11)&0x1f],ARG2_REG); emit_readptr((u_int)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>>16)&0x1f],ARG3_REG); emit_readptr((u_int)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>> 6)&0x1f],ARG4_REG); break; case 0x04: - if(fs>=0) emit_mov(fs,ARG1_REG); - else emit_loadreg(FSREG,ARG1_REG); + emit_addimm(FP,fp_fcr31,ARG1_REG); emit_readptr((u_int)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>>11)&0x1f],ARG2_REG); emit_readptr((u_int)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>> 6)&0x1f],ARG3_REG); break; case 0x05: case 0x06: case 0x07: + emit_addimm(FP,fp_fcr31,ARG1_REG); emit_readptr((u_int)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>>11)&0x1f],ARG1_REG); emit_readptr((u_int)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>> 6)&0x1f],ARG2_REG); break; @@ -3608,19 +3601,18 @@ static void float_assemble(int i,struct regstat *i_regs) switch(source[i]&0x3f) { case 0x00: case 0x01: case 0x02: case 0x03: - if(fs>=0) emit_mov(fs,ARG1_REG); - else emit_loadreg(FSREG,ARG1_REG); + emit_addimm(FP,fp_fcr31,ARG1_REG); emit_readptr((u_int)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>>11)&0x1f],ARG2_REG); emit_readptr((u_int)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>>16)&0x1f],ARG3_REG); emit_readptr((u_int)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>> 6)&0x1f],ARG4_REG); break; case 0x04: - if(fs>=0) emit_mov(fs,ARG1_REG); - else emit_loadreg(FSREG,ARG1_REG); + emit_addimm(FP,fp_fcr31,ARG1_REG); emit_readptr((u_int)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>>11)&0x1f],ARG2_REG); emit_readptr((u_int)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>> 6)&0x1f],ARG3_REG); break; case 0x05: case 0x06: case 0x07: + emit_addimm(FP,fp_fcr31,ARG1_REG); emit_readptr((u_int)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>>11)&0x1f],ARG1_REG); emit_readptr((u_int)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>> 6)&0x1f],ARG2_REG); break; diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/arm64/assem_arm64.c b/mupen64plus-core/src/device/r4300/new_dynarec/arm64/assem_arm64.c index 30e40bda..46243eca 100644 --- a/mupen64plus-core/src/device/r4300/new_dynarec/arm64/assem_arm64.c +++ b/mupen64plus-core/src/device/r4300/new_dynarec/arm64/assem_arm64.c @@ -20,7 +20,7 @@ #define fp_cycle_count (offsetof(struct new_dynarec_hot_state, cycle_count)) #define fp_invc_ptr (offsetof(struct new_dynarec_hot_state, invc_ptr)) -#define fp_fcr31 (offsetof(struct new_dynarec_hot_state, fcr31)) +#define fp_fcr31 (offsetof(struct new_dynarec_hot_state, cp1_fcr31)) #define fp_regs (offsetof(struct new_dynarec_hot_state, regs)) #define fp_hi (offsetof(struct new_dynarec_hot_state, hi)) #define fp_lo (offsetof(struct new_dynarec_hot_state, lo)) @@ -3746,69 +3746,63 @@ static void fconv_assemble_arm64(int i,struct regstat *i_regs) save_regs(reglist); if(opcode2[i]==0x14&&(source[i]&0x3f)==0x20) { - if(fs>=0) emit_mov(fs,ARG1_REG); - else emit_loadreg(FSREG,ARG1_REG); + emit_addimm64(FP,fp_fcr31,ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>>11)&0x1f],ARG2_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>> 6)&0x1f],ARG3_REG); emit_call((intptr_t)cvt_s_w); } if(opcode2[i]==0x14&&(source[i]&0x3f)==0x21) { + emit_addimm64(FP,fp_fcr31,ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>>11)&0x1f],ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>> 6)&0x1f],ARG2_REG); emit_call((intptr_t)cvt_d_w); } if(opcode2[i]==0x15&&(source[i]&0x3f)==0x20) { - if(fs>=0) emit_mov(fs,ARG1_REG); - else emit_loadreg(FSREG,ARG1_REG); + emit_addimm64(FP,fp_fcr31,ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>>11)&0x1f],ARG2_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>> 6)&0x1f],ARG3_REG); emit_call((intptr_t)cvt_s_l); } if(opcode2[i]==0x15&&(source[i]&0x3f)==0x21) { - if(fs>=0) emit_mov(fs,ARG1_REG); - else emit_loadreg(FSREG,ARG1_REG); + emit_addimm64(FP,fp_fcr31,ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>>11)&0x1f],ARG2_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>> 6)&0x1f],ARG3_REG); emit_call((intptr_t)cvt_d_l); } if(opcode2[i]==0x10&&(source[i]&0x3f)==0x21) { + emit_addimm64(FP,fp_fcr31,ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>>11)&0x1f],ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>> 6)&0x1f],ARG2_REG); emit_call((intptr_t)cvt_d_s); } if(opcode2[i]==0x10&&(source[i]&0x3f)==0x24) { - if(fs>=0) emit_mov(fs,ARG1_REG); - else emit_loadreg(FSREG,ARG1_REG); + emit_addimm64(FP,fp_fcr31,ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>>11)&0x1f],ARG2_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>> 6)&0x1f],ARG3_REG); emit_call((intptr_t)cvt_w_s); } if(opcode2[i]==0x10&&(source[i]&0x3f)==0x25) { - if(fs>=0) emit_mov(fs,ARG1_REG); - else emit_loadreg(FSREG,ARG1_REG); + emit_addimm64(FP,fp_fcr31,ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>>11)&0x1f],ARG2_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>> 6)&0x1f],ARG3_REG); emit_call((intptr_t)cvt_l_s); } if(opcode2[i]==0x11&&(source[i]&0x3f)==0x20) { - if(fs>=0) emit_mov(fs,ARG1_REG); - else emit_loadreg(FSREG,ARG1_REG); + emit_addimm64(FP,fp_fcr31,ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>>11)&0x1f],ARG2_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>> 6)&0x1f],ARG3_REG); emit_call((intptr_t)cvt_s_d); } if(opcode2[i]==0x11&&(source[i]&0x3f)==0x24) { - if(fs>=0) emit_mov(fs,ARG1_REG); - else emit_loadreg(FSREG,ARG1_REG); + emit_addimm64(FP,fp_fcr31,ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>>11)&0x1f],ARG2_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>> 6)&0x1f],ARG3_REG); emit_call((intptr_t)cvt_w_d); } if(opcode2[i]==0x11&&(source[i]&0x3f)==0x25) { - if(fs>=0) emit_mov(fs,ARG1_REG); - else emit_loadreg(FSREG,ARG1_REG); + emit_addimm64(FP,fp_fcr31,ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>>11)&0x1f],ARG2_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>> 6)&0x1f],ARG3_REG); emit_call((intptr_t)cvt_l_d); @@ -4185,19 +4179,18 @@ static void float_assemble(int i,struct regstat *i_regs) switch(source[i]&0x3f) { case 0x00: case 0x01: case 0x02: case 0x03: - if(fs>=0) emit_mov(fs,ARG1_REG); - else emit_loadreg(FSREG,ARG1_REG); + emit_addimm64(FP,fp_fcr31,ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>>11)&0x1f],ARG2_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>>16)&0x1f],ARG3_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>> 6)&0x1f],ARG4_REG); break; case 0x04: - if(fs>=0) emit_mov(fs,ARG1_REG); - else emit_loadreg(FSREG,ARG1_REG); + emit_addimm64(FP,fp_fcr31,ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>>11)&0x1f],ARG2_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>> 6)&0x1f],ARG3_REG); break; case 0x05: case 0x06: case 0x07: + emit_addimm64(FP,fp_fcr31,ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>>11)&0x1f],ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>> 6)&0x1f],ARG2_REG); break; @@ -4220,19 +4213,18 @@ static void float_assemble(int i,struct regstat *i_regs) switch(source[i]&0x3f) { case 0x00: case 0x01: case 0x02: case 0x03: - if(fs>=0) emit_mov(fs,ARG1_REG); - else emit_loadreg(FSREG,ARG1_REG); + emit_addimm64(FP,fp_fcr31,ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>>11)&0x1f],ARG2_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>>16)&0x1f],ARG3_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>> 6)&0x1f],ARG4_REG); break; case 0x04: - if(fs>=0) emit_mov(fs,ARG1_REG); - else emit_loadreg(FSREG,ARG1_REG); + emit_addimm64(FP,fp_fcr31,ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>>11)&0x1f],ARG2_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>> 6)&0x1f],ARG3_REG); break; case 0x05: case 0x06: case 0x07: + emit_addimm64(FP,fp_fcr31,ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>>11)&0x1f],ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>> 6)&0x1f],ARG2_REG); break; diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/new_dynarec.c b/mupen64plus-core/src/device/r4300/new_dynarec/new_dynarec.c index 5cc0fe1e..8b0df163 100644 --- a/mupen64plus-core/src/device/r4300/new_dynarec/new_dynarec.c +++ b/mupen64plus-core/src/device/r4300/new_dynarec/new_dynarec.c @@ -22,6 +22,7 @@ #include #include #include +#include // needed for u_int, u_char, etc #include #include @@ -2302,7 +2303,7 @@ static void tlb_speed_hacks() { u_int addr; int n; - switch (ROM_HEADER.Country_code&0xFF) + switch (ROM_HEADER.Country_code) { case 0x45: // U addr=0x34b30; @@ -2344,8 +2345,8 @@ static void tlb_speed_hacks() u_int verify_dirty(struct ll_entry * head) { void *source; - if((int)head->start>=0xA0000000&&(int)head->start<0xA07FFFFF) { - source=(void *)((uintptr_t)g_dev.rdram.dram+head->start-0xA0000000); + if((int)head->start>=0xa0000000&&(int)head->start<0xa07fffff) { + source=(void *)((uintptr_t)g_dev.rdram.dram+head->start-0xa0000000); }else if((int)head->start>=0xa4000000&&(int)head->start<0xa4001000) { source=(void *)((uintptr_t)g_dev.sp.mem+head->start-0xa4000000); }else if((int)head->start>=0x80000000&&(int)head->start<0x80800000) { @@ -5415,7 +5416,7 @@ static void cop1_assemble(int i,struct regstat *i_regs) signed char fs=get_reg(i_regs->regmap,FSREG); if(tl>=0) { u_int copr=(source[i]>>11)&0x1f; - if(copr==0) emit_readword((intptr_t)&g_dev.r4300.new_dynarec_hot_state.fcr0,tl); + if(copr==0) emit_readword((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_fcr0,tl); if(copr==31) { if(fs>=0) emit_mov(fs,tl); @@ -8793,9 +8794,9 @@ int new_recompile_block(int addr) #endif start = (u_int)addr&~3; //assert(((u_int)addr&1)==0); - if ((int)addr >= 0xA0000000 && (int)addr < 0xA07FFFFF) { - source = (u_int *)((uintptr_t)g_dev.rdram.dram+start-0xA0000000); - pagelimit = 0xA07FFFFF; + if ((int)addr >= 0xa0000000 && (int)addr < 0xa07fffff) { + source = (u_int *)((uintptr_t)g_dev.rdram.dram+start-0xa0000000); + pagelimit = 0xa07fffff; } else if ((int)addr >= 0xa4000000 && (int)addr < 0xa4001000) { source = (u_int *)((uintptr_t)g_dev.sp.mem+start-0xa4000000); diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/new_dynarec.h b/mupen64plus-core/src/device/r4300/new_dynarec/new_dynarec.h index f1e5d3ee..a8fd37c3 100644 --- a/mupen64plus-core/src/device/r4300/new_dynarec/new_dynarec.h +++ b/mupen64plus-core/src/device/r4300/new_dynarec/new_dynarec.h @@ -61,14 +61,16 @@ struct new_dynarec_hot_state uint64_t rdword; uint64_t wdword; uint32_t wword; - uint32_t fcr0; - uint32_t fcr31; + uint32_t cp1_fcr0; + uint32_t cp1_fcr31; int64_t regs[32]; int64_t hi; int64_t lo; uint32_t cp0_regs[32]; + uint64_t cp0_latch; float* cp1_regs_simple[32]; double* cp1_regs_double[32]; + uint64_t cp2_latch; uint32_t rounding_modes[4]; int branch_target; struct precomp_instr* pc; diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/x64/assem_x64.c b/mupen64plus-core/src/device/r4300/new_dynarec/x64/assem_x64.c index 03462da6..e71bad24 100644 --- a/mupen64plus-core/src/device/r4300/new_dynarec/x64/assem_x64.c +++ b/mupen64plus-core/src/device/r4300/new_dynarec/x64/assem_x64.c @@ -931,7 +931,7 @@ static void emit_loadreg(int r, int hr) if((r&63)==LOREG) addr=(intptr_t)&g_dev.r4300.new_dynarec_hot_state.lo+((r&64)>>4); if(r==CCREG) addr=(intptr_t)&g_dev.r4300.new_dynarec_hot_state.cycle_count; if(r==CSREG) addr=(intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp0_regs[CP0_STATUS_REG]; - if(r==FSREG) addr=(intptr_t)&g_dev.r4300.new_dynarec_hot_state.fcr31; + if(r==FSREG) addr=(intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_fcr31; assert(addr-(intptr_t)out>=-2147483648LL&&addr-(intptr_t)out<2147483647LL); assem_debug("mov %llx+%d,%%%s",addr,r,regname[hr]); if(hr>=8) output_rex(0,hr>>3,0,0); @@ -946,7 +946,7 @@ static void emit_storereg(int r, int hr) if((r&63)==HIREG) addr=(intptr_t)&g_dev.r4300.new_dynarec_hot_state.hi+((r&64)>>4); if((r&63)==LOREG) addr=(intptr_t)&g_dev.r4300.new_dynarec_hot_state.lo+((r&64)>>4); if(r==CCREG) addr=(intptr_t)&g_dev.r4300.new_dynarec_hot_state.cycle_count; - if(r==FSREG) addr=(intptr_t)&g_dev.r4300.new_dynarec_hot_state.fcr31; + if(r==FSREG) addr=(intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_fcr31; assert((r&63)!=CSREG); assert((r&63)!=0); assert((r&63)<=CCREG); @@ -3621,69 +3621,63 @@ static void fconv_assemble_x64(int i,struct regstat *i_regs) save_regs(reglist); if(opcode2[i]==0x14&&(source[i]&0x3f)==0x20) { - if(fs>=0) emit_mov(fs,ARG1_REG); - else emit_loadreg(FSREG,ARG1_REG); + emit_lea_rip((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_fcr31,ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>>11)&0x1f],ARG2_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>> 6)&0x1f],ARG3_REG); emit_call((intptr_t)cvt_s_w); } if(opcode2[i]==0x14&&(source[i]&0x3f)==0x21) { + emit_lea_rip((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_fcr31,ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>>11)&0x1f],ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>> 6)&0x1f],ARG2_REG); emit_call((intptr_t)cvt_d_w); } if(opcode2[i]==0x15&&(source[i]&0x3f)==0x20) { - if(fs>=0) emit_mov(fs,ARG1_REG); - else emit_loadreg(FSREG,ARG1_REG); + emit_lea_rip((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_fcr31,ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>>11)&0x1f],ARG2_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>> 6)&0x1f],ARG3_REG); emit_call((intptr_t)cvt_s_l); } if(opcode2[i]==0x15&&(source[i]&0x3f)==0x21) { - if(fs>=0) emit_mov(fs,ARG1_REG); - else emit_loadreg(FSREG,ARG1_REG); + emit_lea_rip((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_fcr31,ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>>11)&0x1f],ARG2_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>> 6)&0x1f],ARG3_REG); emit_call((intptr_t)cvt_d_l); } if(opcode2[i]==0x10&&(source[i]&0x3f)==0x21) { + emit_lea_rip((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_fcr31,ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>>11)&0x1f],ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>> 6)&0x1f],ARG2_REG); emit_call((intptr_t)cvt_d_s); } if(opcode2[i]==0x10&&(source[i]&0x3f)==0x24) { - if(fs>=0) emit_mov(fs,ARG1_REG); - else emit_loadreg(FSREG,ARG1_REG); + emit_lea_rip((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_fcr31,ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>>11)&0x1f],ARG2_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>> 6)&0x1f],ARG3_REG); emit_call((intptr_t)cvt_w_s); } if(opcode2[i]==0x10&&(source[i]&0x3f)==0x25) { - if(fs>=0) emit_mov(fs,ARG1_REG); - else emit_loadreg(FSREG,ARG1_REG); + emit_lea_rip((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_fcr31,ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>>11)&0x1f],ARG2_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>> 6)&0x1f],ARG3_REG); emit_call((intptr_t)cvt_l_s); } if(opcode2[i]==0x11&&(source[i]&0x3f)==0x20) { - if(fs>=0) emit_mov(fs,ARG1_REG); - else emit_loadreg(FSREG,ARG1_REG); + emit_lea_rip((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_fcr31,ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>>11)&0x1f],ARG2_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>> 6)&0x1f],ARG3_REG); emit_call((intptr_t)cvt_s_d); } if(opcode2[i]==0x11&&(source[i]&0x3f)==0x24) { - if(fs>=0) emit_mov(fs,ARG1_REG); - else emit_loadreg(FSREG,ARG1_REG); + emit_lea_rip((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_fcr31,ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>>11)&0x1f],ARG2_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>> 6)&0x1f],ARG3_REG); emit_call((intptr_t)cvt_w_d); } if(opcode2[i]==0x11&&(source[i]&0x3f)==0x25) { - if(fs>=0) emit_mov(fs,ARG1_REG); - else emit_loadreg(FSREG,ARG1_REG); + emit_lea_rip((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_fcr31,ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>>11)&0x1f],ARG2_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>> 6)&0x1f],ARG3_REG); emit_call((intptr_t)cvt_l_d); @@ -3866,7 +3860,7 @@ static void fcomp_assemble(int i,struct regstat *i_regs) emit_storereg(FSREG, fs); save_regs(reglist); if(opcode2[i]==0x10) { - emit_lea_rip((intptr_t)&g_dev.r4300.new_dynarec_hot_state.fcr31,ARG1_REG); + emit_lea_rip((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_fcr31,ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>>11)&0x1f],ARG2_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>>16)&0x1f],ARG3_REG); if((source[i]&0x3f)==0x30) emit_call((intptr_t)c_f_s); @@ -3887,7 +3881,7 @@ static void fcomp_assemble(int i,struct regstat *i_regs) if((source[i]&0x3f)==0x3f) emit_call((intptr_t)c_ngt_s); } if(opcode2[i]==0x11) { - emit_lea_rip((intptr_t)&g_dev.r4300.new_dynarec_hot_state.fcr31,ARG1_REG); + emit_lea_rip((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_fcr31,ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>>11)&0x1f],ARG2_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>>16)&0x1f],ARG3_REG); if((source[i]&0x3f)==0x30) emit_call((intptr_t)c_f_d); @@ -4034,19 +4028,18 @@ static void float_assemble(int i,struct regstat *i_regs) switch(source[i]&0x3f) { case 0x00: case 0x01: case 0x02: case 0x03: - if(fs>=0) emit_mov(fs,ARG1_REG); - else emit_loadreg(FSREG,ARG1_REG); + emit_lea_rip((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_fcr31,ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>>11)&0x1f],ARG2_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>>16)&0x1f],ARG3_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>> 6)&0x1f],ARG4_REG); break; case 0x04: - if(fs>=0) emit_mov(fs,ARG1_REG); - else emit_loadreg(FSREG,ARG1_REG); + emit_lea_rip((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_fcr31,ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>>11)&0x1f],ARG2_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>> 6)&0x1f],ARG3_REG); break; case 0x05: case 0x06: case 0x07: + emit_lea_rip((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_fcr31,ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>>11)&0x1f],ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>> 6)&0x1f],ARG2_REG); break; @@ -4070,19 +4063,18 @@ static void float_assemble(int i,struct regstat *i_regs) switch(source[i]&0x3f) { case 0x00: case 0x01: case 0x02: case 0x03: - if(fs>=0) emit_mov(fs,ARG1_REG); - else emit_loadreg(FSREG,ARG1_REG); + emit_lea_rip((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_fcr31,ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>>11)&0x1f],ARG2_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>>16)&0x1f],ARG3_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>> 6)&0x1f],ARG4_REG); break; case 0x04: - if(fs>=0) emit_mov(fs,ARG1_REG); - else emit_loadreg(FSREG,ARG1_REG); + emit_lea_rip((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_fcr31,ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>>11)&0x1f],ARG2_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>> 6)&0x1f],ARG3_REG); break; case 0x05: case 0x06: case 0x07: + emit_lea_rip((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_fcr31,ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>>11)&0x1f],ARG1_REG); emit_readptr((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>> 6)&0x1f],ARG2_REG); break; diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/x86/assem_x86.c b/mupen64plus-core/src/device/r4300/new_dynarec/x86/assem_x86.c index 51ffe863..ea5ab130 100644 --- a/mupen64plus-core/src/device/r4300/new_dynarec/x86/assem_x86.c +++ b/mupen64plus-core/src/device/r4300/new_dynarec/x86/assem_x86.c @@ -877,7 +877,7 @@ static void emit_loadreg(int r, int hr) if((r&63)==LOREG) addr=(int)&g_dev.r4300.new_dynarec_hot_state.lo+((r&64)>>4); if(r==CCREG) addr=(int)&g_dev.r4300.new_dynarec_hot_state.cycle_count; if(r==CSREG) addr=(int)&g_dev.r4300.new_dynarec_hot_state.cp0_regs[CP0_STATUS_REG]; - if(r==FSREG) addr=(int)&g_dev.r4300.new_dynarec_hot_state.fcr31; + if(r==FSREG) addr=(int)&g_dev.r4300.new_dynarec_hot_state.cp1_fcr31; assem_debug("mov %x+%d,%%%s",addr,r,regname[hr]); output_byte(0x8B); output_modrm(0,5,hr); @@ -890,7 +890,7 @@ static void emit_storereg(int r, int hr) if((r&63)==HIREG) addr=(int)&g_dev.r4300.new_dynarec_hot_state.hi+((r&64)>>4); if((r&63)==LOREG) addr=(int)&g_dev.r4300.new_dynarec_hot_state.lo+((r&64)>>4); if(r==CCREG) addr=(int)&g_dev.r4300.new_dynarec_hot_state.cycle_count; - if(r==FSREG) addr=(int)&g_dev.r4300.new_dynarec_hot_state.fcr31; + if(r==FSREG) addr=(int)&g_dev.r4300.new_dynarec_hot_state.cp1_fcr31; assert((r&63)!=CSREG); assert((r&63)!=0); assert((r&63)<=CCREG); @@ -3200,8 +3200,7 @@ static void fconv_assemble_x86(int i,struct regstat *i_regs) if(opcode2[i]==0x14&&(source[i]&0x3f)==0x20) { emit_pushmem((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>> 6)&0x1f]); emit_pushmem((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>>11)&0x1f]); - if(fs>=0) emit_pushreg(fs); - else emit_pushmem((int)&g_dev.r4300.new_dynarec_hot_state.fcr31); + emit_pushimm((int)&g_dev.r4300.new_dynarec_hot_state.cp1_fcr31); emit_call((int)cvt_s_w); emit_addimm(ESP,12,ESP); } @@ -3214,16 +3213,14 @@ static void fconv_assemble_x86(int i,struct regstat *i_regs) if(opcode2[i]==0x15&&(source[i]&0x3f)==0x20) { emit_pushmem((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>> 6)&0x1f]); emit_pushmem((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>>11)&0x1f]); - if(fs>=0) emit_pushreg(fs); - else emit_pushmem((int)&g_dev.r4300.new_dynarec_hot_state.fcr31); + emit_pushimm((int)&g_dev.r4300.new_dynarec_hot_state.cp1_fcr31); emit_call((int)cvt_s_l); emit_addimm(ESP,12,ESP); } if(opcode2[i]==0x15&&(source[i]&0x3f)==0x21) { emit_pushmem((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>> 6)&0x1f]); emit_pushmem((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>>11)&0x1f]); - if(fs>=0) emit_pushreg(fs); - else emit_pushmem((int)&g_dev.r4300.new_dynarec_hot_state.fcr31); + emit_pushimm((int)&g_dev.r4300.new_dynarec_hot_state.cp1_fcr31); emit_call((int)cvt_d_l); emit_addimm(ESP,12,ESP); } @@ -3237,16 +3234,14 @@ static void fconv_assemble_x86(int i,struct regstat *i_regs) if(opcode2[i]==0x10&&(source[i]&0x3f)==0x24) { emit_pushmem((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>> 6)&0x1f]); emit_pushmem((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>>11)&0x1f]); - if(fs>=0) emit_pushreg(fs); - else emit_pushmem((int)&g_dev.r4300.new_dynarec_hot_state.fcr31); + emit_pushimm((int)&g_dev.r4300.new_dynarec_hot_state.cp1_fcr31); emit_call((int)cvt_w_s); emit_addimm(ESP,12,ESP); } if(opcode2[i]==0x10&&(source[i]&0x3f)==0x25) { emit_pushmem((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>> 6)&0x1f]); emit_pushmem((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>>11)&0x1f]); - if(fs>=0) emit_pushreg(fs); - else emit_pushmem((int)&g_dev.r4300.new_dynarec_hot_state.fcr31); + emit_pushimm((int)&g_dev.r4300.new_dynarec_hot_state.cp1_fcr31); emit_call((int)cvt_l_s); emit_addimm(ESP,12,ESP); } @@ -3254,24 +3249,21 @@ static void fconv_assemble_x86(int i,struct regstat *i_regs) if(opcode2[i]==0x11&&(source[i]&0x3f)==0x20) { emit_pushmem((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>> 6)&0x1f]); emit_pushmem((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>>11)&0x1f]); - if(fs>=0) emit_pushreg(fs); - else emit_pushmem((int)&g_dev.r4300.new_dynarec_hot_state.fcr31); + emit_pushimm((int)&g_dev.r4300.new_dynarec_hot_state.cp1_fcr31); emit_call((int)cvt_s_d); emit_addimm(ESP,12,ESP); } if(opcode2[i]==0x11&&(source[i]&0x3f)==0x24) { emit_pushmem((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>> 6)&0x1f]); emit_pushmem((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>>11)&0x1f]); - if(fs>=0) emit_pushreg(fs); - else emit_pushmem((int)&g_dev.r4300.new_dynarec_hot_state.fcr31); + emit_pushimm((int)&g_dev.r4300.new_dynarec_hot_state.cp1_fcr31); emit_call((int)cvt_w_d); emit_addimm(ESP,12,ESP); } if(opcode2[i]==0x11&&(source[i]&0x3f)==0x25) { emit_pushmem((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>> 6)&0x1f]); emit_pushmem((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>>11)&0x1f]); - if(fs>=0) emit_pushreg(fs); - else emit_pushmem((int)&g_dev.r4300.new_dynarec_hot_state.fcr31); + emit_pushimm((int)&g_dev.r4300.new_dynarec_hot_state.cp1_fcr31); emit_call((int)cvt_l_d); emit_addimm(ESP,12,ESP); } @@ -3465,7 +3457,7 @@ static void fcomp_assemble(int i,struct regstat *i_regs) if(opcode2[i]==0x10) { emit_pushmem((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>>16)&0x1f]); emit_pushmem((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>>11)&0x1f]); - emit_pushimm((int)&g_dev.r4300.new_dynarec_hot_state.fcr31); + emit_pushimm((int)&g_dev.r4300.new_dynarec_hot_state.cp1_fcr31); if((source[i]&0x3f)==0x30) emit_call((int)c_f_s); if((source[i]&0x3f)==0x31) emit_call((int)c_un_s); if((source[i]&0x3f)==0x32) emit_call((int)c_eq_s); @@ -3486,7 +3478,7 @@ static void fcomp_assemble(int i,struct regstat *i_regs) if(opcode2[i]==0x11) { emit_pushmem((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>>16)&0x1f]); emit_pushmem((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>>11)&0x1f]); - emit_pushimm((int)&g_dev.r4300.new_dynarec_hot_state.fcr31); + emit_pushimm((int)&g_dev.r4300.new_dynarec_hot_state.cp1_fcr31); if((source[i]&0x3f)==0x30) emit_call((int)c_f_d); if((source[i]&0x3f)==0x31) emit_call((int)c_un_d); if((source[i]&0x3f)==0x32) emit_call((int)c_eq_d); @@ -3630,10 +3622,7 @@ static void float_assemble(int i,struct regstat *i_regs) if((source[i]&0x3f)<4) emit_pushmem((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>>16)&0x1f]); emit_pushmem((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_simple[(source[i]>>11)&0x1f]); - if((source[i]&0x3f)<=4) { - if(fs>=0) emit_pushreg(fs); - else emit_pushmem((int)&g_dev.r4300.new_dynarec_hot_state.fcr31); - } + emit_pushimm((int)&g_dev.r4300.new_dynarec_hot_state.cp1_fcr31); switch(source[i]&0x3f) { case 0x00: @@ -3677,11 +3666,7 @@ static void float_assemble(int i,struct regstat *i_regs) if((source[i]&0x3f)<4) emit_pushmem((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>>16)&0x1f]); emit_pushmem((intptr_t)&g_dev.r4300.new_dynarec_hot_state.cp1_regs_double[(source[i]>>11)&0x1f]); - if((source[i]&0x3f)<=4) - { - if(fs>=0) emit_pushreg(fs); - else emit_pushmem((int)&g_dev.r4300.new_dynarec_hot_state.fcr31); - } + emit_pushimm((int)&g_dev.r4300.new_dynarec_hot_state.cp1_fcr31); switch(source[i]&0x3f) { case 0x00: diff --git a/mupen64plus-core/src/device/r4300/opcodes.md b/mupen64plus-core/src/device/r4300/opcodes.md index a0319047..02e9cd66 100644 --- a/mupen64plus-core/src/device/r4300/opcodes.md +++ b/mupen64plus-core/src/device/r4300/opcodes.md @@ -38,7 +38,9 @@ X(CACHE), X(CFC0), X(CFC1), + X(DCFC1), X(CFC2), + X(DCFC2), X(CP1_ABS), X(CP1_ADD), X(CP1_CEIL_L), @@ -77,7 +79,9 @@ X(CP1_TRUNC_W), X(CTC0), X(CTC1), + X(DCTC1), X(CTC2), + X(DCTC2), X(DADD), X(DADDI), X(DADDIU), diff --git a/mupen64plus-core/src/device/r4300/pure_interp.c b/mupen64plus-core/src/device/r4300/pure_interp.c index 09d1a4c6..6456693f 100644 --- a/mupen64plus-core/src/device/r4300/pure_interp.c +++ b/mupen64plus-core/src/device/r4300/pure_interp.c @@ -204,8 +204,8 @@ void InterpretOpcode(struct r4300_core* r4300) JALR(r4300, op); break; case 12: SYSCALL(r4300, op); break; - case 13: /* SPECIAL opcode 13: BREAK (Not implemented) */ - NI(r4300, op); + case 13: /* SPECIAL opcode 13: BREAK */ + BREAK(r4300, op); break; case 15: SYNC(r4300, op); break; case 16: /* SPECIAL opcode 16: MFHI */ @@ -434,11 +434,18 @@ void InterpretOpcode(struct r4300_core* r4300) break; case 16: /* Coprocessor 0 prefix */ switch ((op >> 21) & 0x1F) { - case 0: /* Coprocessor 0 opcode 0: MFC0 */ + case 0: /* Coprocessor 0 opcode 0: MFC0 */ if (RT_OF(op) != 0) MFC0(r4300, op); else NOP(r4300, 0); break; - case 4: MTC0(r4300, op); break; + case 1: /* Coprocessor 0 opcode 1: DMFC0 */ + if (RT_OF(op) != 0) DMFC0(r4300, op); + else NOP(r4300, 0); + break; + case 4: /* Coprocessor 0 opcode 4: MTC0 */ + case 5: /* Coprocessor 0 opcode 5: DMTC0 */ + MTC0(r4300, op); + break; case 16: /* Coprocessor 0 opcode 16: TLB */ switch (op & 0x3F) { case 1: TLBR(r4300, op); break; @@ -452,7 +459,7 @@ void InterpretOpcode(struct r4300_core* r4300) break; } /* switch (op & 0x3F) for Coprocessor 0 TLB opcodes */ break; - default: /* Coprocessor 0 opcodes 1..3, 4..15, 17..31: + default: /* Coprocessor 0 opcodes 2..3, 5..15, 17..31: Reserved Instructions */ RESERVED(r4300, op); break; @@ -472,9 +479,14 @@ void InterpretOpcode(struct r4300_core* r4300) if (RT_OF(op) != 0) CFC1(r4300, op); else NOP(r4300, 0); break; + case 3: /* Coprocessor 1 opcode 2: DCFC1 */ + if (RT_OF(op) != 0) DCFC1(r4300, op); + else NOP(r4300, 0); + break; case 4: MTC1(r4300, op); break; case 5: DMTC1(r4300, op); break; case 6: CTC1(r4300, op); break; + case 7: DCTC1(r4300, op); break; case 8: /* Coprocessor 1 opcode 8: Branch on C1 condition... */ switch ((op >> 16) & 0x3) { case 0: /* opcode 0: BC1F */ @@ -601,12 +613,34 @@ void InterpretOpcode(struct r4300_core* r4300) break; } break; - default: /* Coprocessor 1 opcodes 3, 7, 9..15, 18..19, 22..31: + default: /* Coprocessor 1 opcodes 9..15, 18..19, 22..31: Reserved Instructions */ RESERVED(r4300, op); break; } /* switch ((op >> 21) & 0x1F) for the Coprocessor 1 prefix */ break; + case 18: /* Coprocessor 2 prefix */ + switch ((op >> 21) & 0x1F) { + case 0: /* Coprocessor 2 opcode 0: MFC2 */ + if (RT_OF(op) != 0) MFC2(r4300, op); + else NOP(r4300, 0); + break; + case 1: /* Coprocessor 2 opcode 1: DMFC2 */ + if (RT_OF(op) != 0) DMFC2(r4300, op); + else NOP(r4300, 0); + break; + case 2: /* Coprocessor 2 opcode 2: CFC2 */ + if (RT_OF(op) != 0) CFC2(r4300, op); + else NOP(r4300, 0); + break; + case 4: MTC2(r4300, op); break; + case 5: DMTC2(r4300, op); break; + case 6: CTC2(r4300, op); break; + default: + RESERVED_COP2(r4300, op); + break; + } + break; case 20: /* Major opcode 20: BEQL */ if (IS_RELATIVE_IDLE_LOOP(r4300, op, *r4300_pc(r4300))) BEQL_IDLE(r4300, op); else BEQL(r4300, op); diff --git a/mupen64plus-core/src/device/r4300/r4300_core.c b/mupen64plus-core/src/device/r4300/r4300_core.c index e87a3d8b..76e9f50d 100644 --- a/mupen64plus-core/src/device/r4300/r4300_core.c +++ b/mupen64plus-core/src/device/r4300/r4300_core.c @@ -53,6 +53,7 @@ void init_r4300(struct r4300_core* r4300, struct memory* mem, struct mi_controll r4300->emumode = emumode; init_cp0(&r4300->cp0, count_per_op, count_per_op_denom_pot, new_dynarec_hot_state, interrupt_handlers); init_cp1(&r4300->cp1, new_dynarec_hot_state); + init_cp2(&r4300->cp2, new_dynarec_hot_state); #ifndef NEW_DYNAREC r4300->recomp.no_compiled_jump = no_compiled_jump; @@ -117,6 +118,9 @@ void poweron_r4300(struct r4300_core* r4300) /* setup CP1 registers */ poweron_cp1(&r4300->cp1); + + /* setup CP2 registers */ + poweron_cp2(&r4300->cp2); } diff --git a/mupen64plus-core/src/device/r4300/r4300_core.h b/mupen64plus-core/src/device/r4300/r4300_core.h index 335b10e1..5b5b7ea5 100644 --- a/mupen64plus-core/src/device/r4300/r4300_core.h +++ b/mupen64plus-core/src/device/r4300/r4300_core.h @@ -31,6 +31,7 @@ #include "cp0.h" #include "cp1.h" +#include "cp2.h" #include "recomp_types.h" /* for precomp_instr, regcache_state */ @@ -190,6 +191,8 @@ struct r4300_core struct cp1 cp1; + struct cp2 cp2; + struct memory* mem; struct mi_controller* mi; struct rdram* rdram; diff --git a/mupen64plus-core/src/device/r4300/recomp.c b/mupen64plus-core/src/device/r4300/recomp.c index f4f0008f..55aa9c1c 100644 --- a/mupen64plus-core/src/device/r4300/recomp.c +++ b/mupen64plus-core/src/device/r4300/recomp.c @@ -437,6 +437,10 @@ static void gen_CP1_CVT_S(struct r4300_core* r4300) #define gen_SCD genni #define gen_SDC2 genni #define gen_SWC2 genni +#define gen_DCFC1 genni +#define gen_DCFC2 genni +#define gen_DCTC1 genni +#define gen_DCTC2 genni #define X(op) gen_##op static void (*const recomp_funcs[R4300_OPCODES_COUNT])(struct r4300_core* r4300) = diff --git a/mupen64plus-core/src/device/rcp/ai/ai_controller.c b/mupen64plus-core/src/device/rcp/ai/ai_controller.c index 86d41ee5..bc46a5f7 100644 --- a/mupen64plus-core/src/device/rcp/ai/ai_controller.c +++ b/mupen64plus-core/src/device/rcp/ai/ai_controller.c @@ -100,19 +100,19 @@ static void do_dma(struct ai_controller* ai, struct ai_dma* dma) static void fifo_push(struct ai_controller* ai) { - unsigned int duration = get_dma_duration(ai); + unsigned int duration = get_dma_duration(ai) * ai->dma_modifier; if (ai->regs[AI_STATUS_REG] & AI_STATUS_BUSY) { ai->fifo[1].address = ai->regs[AI_DRAM_ADDR_REG]; - ai->fifo[1].length = ai->regs[AI_LEN_REG]; + ai->fifo[1].length = ai->regs[AI_LEN_REG] & ~UINT32_C(7); ai->fifo[1].duration = duration; ai->regs[AI_STATUS_REG] |= AI_STATUS_FULL; } else { ai->fifo[0].address = ai->regs[AI_DRAM_ADDR_REG]; - ai->fifo[0].length = ai->regs[AI_LEN_REG]; + ai->fifo[0].length = ai->regs[AI_LEN_REG] & ~UINT32_C(7); ai->fifo[0].duration = duration; ai->regs[AI_STATUS_REG] |= AI_STATUS_BUSY; @@ -144,13 +144,15 @@ void init_ai(struct ai_controller* ai, struct ri_controller* ri, struct vi_controller* vi, void* aout, - const struct audio_out_backend_interface* iaout) + const struct audio_out_backend_interface* iaout, + float dma_modifier) { ai->mi = mi; ai->ri = ri; ai->vi = vi; ai->aout = aout; ai->iaout = iaout; + ai->dma_modifier = dma_modifier; } void poweron_ai(struct ai_controller* ai) diff --git a/mupen64plus-core/src/device/rcp/ai/ai_controller.h b/mupen64plus-core/src/device/rcp/ai/ai_controller.h index 337c2d80..6b2217cf 100644 --- a/mupen64plus-core/src/device/rcp/ai/ai_controller.h +++ b/mupen64plus-core/src/device/rcp/ai/ai_controller.h @@ -59,6 +59,7 @@ struct ai_controller unsigned int samples_format_changed; uint32_t last_read; uint32_t delayed_carry; + float dma_modifier; struct mi_controller* mi; struct ri_controller* ri; @@ -78,7 +79,8 @@ void init_ai(struct ai_controller* ai, struct ri_controller* ri, struct vi_controller* vi, void* aout, - const struct audio_out_backend_interface* iaout); + const struct audio_out_backend_interface* iaout, + float dma_modifier); void poweron_ai(struct ai_controller* ai); diff --git a/mupen64plus-core/src/device/rcp/pi/pi_controller.c b/mupen64plus-core/src/device/rcp/pi/pi_controller.c index c78a8407..0fc36bf3 100644 --- a/mupen64plus-core/src/device/rcp/pi/pi_controller.c +++ b/mupen64plus-core/src/device/rcp/pi/pi_controller.c @@ -173,7 +173,6 @@ void write_pi_regs(void* opaque, uint32_t address, uint32_t value, uint32_t mask case PI_CART_ADDR_REG: if (pi->dd != NULL) { masked_write(&pi->regs[PI_CART_ADDR_REG], value, mask); - dd_on_pi_cart_addr_write(pi->dd, pi->regs[PI_CART_ADDR_REG]); return; } break; @@ -219,12 +218,5 @@ void pi_end_of_dma_event(void* opaque) pi->regs[PI_STATUS_REG] &= ~(PI_STATUS_DMA_BUSY | PI_STATUS_IO_BUSY); pi->regs[PI_STATUS_REG] |= PI_STATUS_INTERRUPT; - if (pi->dd != NULL) { - if (((pi->regs[PI_CART_ADDR_REG] >= MM_DD_C2S_BUFFER) && (pi->regs[PI_CART_ADDR_REG] < MM_DD_DS_BUFFER)) || - ((pi->regs[PI_CART_ADDR_REG] >= MM_DD_DS_BUFFER) && (pi->regs[PI_CART_ADDR_REG] < MM_DD_REGS))) { - dd_update_bm(pi->dd); - } - } - raise_rcp_interrupt(pi->mi, MI_INTR_PI); } diff --git a/mupen64plus-core/src/device/rcp/rsp/rsp_core.c b/mupen64plus-core/src/device/rcp/rsp/rsp_core.c index cbdfa32f..550d9bb0 100644 --- a/mupen64plus-core/src/device/rcp/rsp/rsp_core.c +++ b/mupen64plus-core/src/device/rcp/rsp/rsp_core.c @@ -56,27 +56,36 @@ static void do_sp_dma(struct rsp_core* sp, const struct sp_dma* dma) { for(j=0; jdp->fb, dramaddr - length, length); + if (dramaddr <= 0x800000) + post_framebuffer_write(&sp->dp->fb, dramaddr - length, length); dramaddr+=skip; } + + sp->regs[SP_MEM_ADDR_REG] = memaddr & 0xfff; + sp->regs[SP_DRAM_ADDR_REG] = dramaddr & 0xffffff; + sp->regs[SP_RD_LEN_REG] = 0xff8; } else { for(j=0; jdp->fb, dramaddr); + if (dramaddr < 0x800000) + pre_framebuffer_read(&sp->dp->fb, dramaddr); for(i=0; iregs[SP_MEM_ADDR_REG] = memaddr & 0xfff; + sp->regs[SP_DRAM_ADDR_REG] = dramaddr & 0xffffff; + sp->regs[SP_RD_LEN_REG] = 0xff8; } /* schedule end of dma event */ @@ -137,68 +146,68 @@ static void fifo_pop(struct rsp_core* sp) static void update_sp_status(struct rsp_core* sp, uint32_t w) { /* clear / set halt */ - if (w & 0x1) sp->regs[SP_STATUS_REG] &= ~SP_STATUS_HALT; - if (w & 0x2) sp->regs[SP_STATUS_REG] |= SP_STATUS_HALT; + if ((w & 0x3) == 0x1) sp->regs[SP_STATUS_REG] &= ~SP_STATUS_HALT; + if ((w & 0x3) == 0x2) sp->regs[SP_STATUS_REG] |= SP_STATUS_HALT; /* clear broke */ if (w & 0x4) sp->regs[SP_STATUS_REG] &= ~SP_STATUS_BROKE; /* clear SP interrupt */ - if (w & 0x8) + if ((w & 0x18) == 0x8) { clear_rcp_interrupt(sp->mi, MI_INTR_SP); } /* set SP interrupt */ - if (w & 0x10) + if ((w & 0x18) == 0x10) { signal_rcp_interrupt(sp->mi, MI_INTR_SP); } /* clear / set single step */ - if (w & 0x20) sp->regs[SP_STATUS_REG] &= ~SP_STATUS_SSTEP; - if (w & 0x40) sp->regs[SP_STATUS_REG] |= SP_STATUS_SSTEP; + if ((w & 0x60) == 0x20) sp->regs[SP_STATUS_REG] &= ~SP_STATUS_SSTEP; + if ((w & 0x60) == 0x40) sp->regs[SP_STATUS_REG] |= SP_STATUS_SSTEP; /* clear / set interrupt on break */ - if (w & 0x80) sp->regs[SP_STATUS_REG] &= ~SP_STATUS_INTR_BREAK; - if (w & 0x100) sp->regs[SP_STATUS_REG] |= SP_STATUS_INTR_BREAK; + if ((w & 0x180) == 0x80) sp->regs[SP_STATUS_REG] &= ~SP_STATUS_INTR_BREAK; + if ((w & 0x180) == 0x100) sp->regs[SP_STATUS_REG] |= SP_STATUS_INTR_BREAK; /* clear / set signal 0 */ - if (w & 0x200) sp->regs[SP_STATUS_REG] &= ~SP_STATUS_SIG0; - if (w & 0x400) sp->regs[SP_STATUS_REG] |= SP_STATUS_SIG0; + if ((w & 0x600) == 0x200) sp->regs[SP_STATUS_REG] &= ~SP_STATUS_SIG0; + if ((w & 0x600) == 0x400) sp->regs[SP_STATUS_REG] |= SP_STATUS_SIG0; /* clear / set signal 1 */ - if (w & 0x800) sp->regs[SP_STATUS_REG] &= ~SP_STATUS_SIG1; - if (w & 0x1000) sp->regs[SP_STATUS_REG] |= SP_STATUS_SIG1; + if ((w & 0x1800) == 0x800) sp->regs[SP_STATUS_REG] &= ~SP_STATUS_SIG1; + if ((w & 0x1800) == 0x1000) sp->regs[SP_STATUS_REG] |= SP_STATUS_SIG1; /* clear / set signal 2 */ - if (w & 0x2000) sp->regs[SP_STATUS_REG] &= ~SP_STATUS_SIG2; - if (w & 0x4000) sp->regs[SP_STATUS_REG] |= SP_STATUS_SIG2; + if ((w & 0x6000) == 0x2000) sp->regs[SP_STATUS_REG] &= ~SP_STATUS_SIG2; + if ((w & 0x6000) == 0x4000) sp->regs[SP_STATUS_REG] |= SP_STATUS_SIG2; /* clear / set signal 3 */ - if (w & 0x8000) sp->regs[SP_STATUS_REG] &= ~SP_STATUS_SIG3; - if (w & 0x10000) sp->regs[SP_STATUS_REG] |= SP_STATUS_SIG3; + if ((w & 0x18000) == 0x8000) sp->regs[SP_STATUS_REG] &= ~SP_STATUS_SIG3; + if ((w & 0x18000) == 0x10000) sp->regs[SP_STATUS_REG] |= SP_STATUS_SIG3; /* clear / set signal 4 */ - if (w & 0x20000) sp->regs[SP_STATUS_REG] &= ~SP_STATUS_SIG4; - if (w & 0x40000) sp->regs[SP_STATUS_REG] |= SP_STATUS_SIG4; + if ((w & 0x60000) == 0x20000) sp->regs[SP_STATUS_REG] &= ~SP_STATUS_SIG4; + if ((w & 0x60000) == 0x40000) sp->regs[SP_STATUS_REG] |= SP_STATUS_SIG4; /* clear / set signal 5 */ - if (w & 0x80000) sp->regs[SP_STATUS_REG] &= ~SP_STATUS_SIG5; - if (w & 0x100000) sp->regs[SP_STATUS_REG] |= SP_STATUS_SIG5; + if ((w & 0x180000) == 0x80000) sp->regs[SP_STATUS_REG] &= ~SP_STATUS_SIG5; + if ((w & 0x180000) == 0x100000) sp->regs[SP_STATUS_REG] |= SP_STATUS_SIG5; /* clear / set signal 6 */ - if (w & 0x200000) sp->regs[SP_STATUS_REG] &= ~SP_STATUS_SIG6; - if (w & 0x400000) sp->regs[SP_STATUS_REG] |= SP_STATUS_SIG6; + if ((w & 0x600000) == 0x200000) sp->regs[SP_STATUS_REG] &= ~SP_STATUS_SIG6; + if ((w & 0x600000) == 0x400000) sp->regs[SP_STATUS_REG] |= SP_STATUS_SIG6; /* clear / set signal 7 */ - if (w & 0x800000) sp->regs[SP_STATUS_REG] &= ~SP_STATUS_SIG7; - if (w & 0x1000000) sp->regs[SP_STATUS_REG] |= SP_STATUS_SIG7; + if ((w & 0x1800000) == 0x800000) sp->regs[SP_STATUS_REG] &= ~SP_STATUS_SIG7; + if ((w & 0x1800000) == 0x1000000) sp->regs[SP_STATUS_REG] |= SP_STATUS_SIG7; if (sp->rsp_task_locked && (get_event(&sp->mi->r4300->cp0.q, SP_INT))) return; - if (!(w & 0x1) && !(w & 0x4) && !sp->rsp_task_locked) + if (!((w & 0x3) == 1) && !(w & 0x4) && !sp->rsp_task_locked) return; - if (!(sp->regs[SP_STATUS_REG] & (SP_STATUS_HALT | SP_STATUS_BROKE))) + if (!(sp->regs[SP_STATUS_REG] & SP_STATUS_HALT)) do_SP_Task(sp); } @@ -224,6 +233,8 @@ void poweron_rsp(struct rsp_core* sp) sp->rsp_task_locked = 0; sp->mi->r4300->cp0.interrupt_unsafe_state &= ~INTR_UNSAFE_RSP; sp->regs[SP_STATUS_REG] = 1; + sp->regs[SP_RD_LEN_REG] = 0xff8; + sp->regs[SP_WR_LEN_REG] = 0xff8; } @@ -294,6 +305,10 @@ void read_rsp_regs2(void* opaque, uint32_t address, uint32_t* value) uint32_t reg = rsp_reg2(address); *value = sp->regs2[reg]; + + if (reg == SP_PC_REG) + *value &= 0xffc; + } void write_rsp_regs2(void* opaque, uint32_t address, uint32_t value, uint32_t mask) @@ -301,6 +316,9 @@ void write_rsp_regs2(void* opaque, uint32_t address, uint32_t value, uint32_t ma struct rsp_core* sp = (struct rsp_core*)opaque; uint32_t reg = rsp_reg2(address); + if (reg == SP_PC_REG) + mask &= 0xffc; + masked_write(&sp->regs2[reg], value, mask); } diff --git a/mupen64plus-core/src/device/rdram/rdram.c b/mupen64plus-core/src/device/rdram/rdram.c index 390577a9..28065eaf 100644 --- a/mupen64plus-core/src/device/rdram/rdram.c +++ b/mupen64plus-core/src/device/rdram/rdram.c @@ -206,7 +206,7 @@ void write_rdram_regs(void* opaque, uint32_t address, uint32_t value, uint32_t m * the amount of detected memory can be found in s4 */ size_t ipl3_rdram_size = r4300_regs(rdram->r4300)[20] & UINT32_C(0x0fffffff); if (ipl3_rdram_size != rdram->dram_size) { - DebugMessage(M64MSG_ERROR, "IPL3 detected %u MB of RDRAM != %u MB", + DebugMessage(M64MSG_WARNING, "IPL3 detected %u MB of RDRAM != %u MB", (uint32_t) ipl3_rdram_size / (1024*1024), (uint32_t) rdram->dram_size / (1024*1024)); } } @@ -231,7 +231,14 @@ void read_rdram_dram(void* opaque, uint32_t address, uint32_t* value) struct rdram* rdram = (struct rdram*)opaque; uint32_t addr = rdram_dram_address(address); - *value = rdram->dram[addr]; + if (address < rdram->dram_size) + { + *value = rdram->dram[addr]; + } + else + { + *value = 0; + } } void write_rdram_dram(void* opaque, uint32_t address, uint32_t value, uint32_t mask) @@ -239,5 +246,8 @@ void write_rdram_dram(void* opaque, uint32_t address, uint32_t value, uint32_t m struct rdram* rdram = (struct rdram*)opaque; uint32_t addr = rdram_dram_address(address); - masked_write(&rdram->dram[addr], value, mask); + if (address < rdram->dram_size) + { + masked_write(&rdram->dram[addr], value, mask); + } } diff --git a/mupen64plus-core/src/main/eventloop.c b/mupen64plus-core/src/main/eventloop.c index a5cdf531..8f1e70ac 100644 --- a/mupen64plus-core/src/main/eventloop.c +++ b/mupen64plus-core/src/main/eventloop.c @@ -61,6 +61,7 @@ static m64p_handle l_CoreEventsConfig = NULL; #define kbdForward "Kbd Mapping Fast Forward" #define kbdAdvance "Kbd Mapping Frame Advance" #define kbdGameshark "Kbd Mapping Gameshark" +#define kbdSpeedtoggle "Kbd Mapping Speed Limiter Toggle" typedef enum {joyFullscreen, joyStop, @@ -542,6 +543,7 @@ int event_set_core_defaults(void) ConfigSetDefaultInt(l_CoreEventsConfig, kbdIncrease, sdl_native2keysym(SDL_SCANCODE_RIGHTBRACKET),"SDL keysym for increasing the volume"); ConfigSetDefaultInt(l_CoreEventsConfig, kbdDecrease, sdl_native2keysym(SDL_SCANCODE_LEFTBRACKET), "SDL keysym for decreasing the volume"); ConfigSetDefaultInt(l_CoreEventsConfig, kbdForward, sdl_native2keysym(SDL_SCANCODE_F), "SDL keysym for temporarily going really fast"); + ConfigSetDefaultInt(l_CoreEventsConfig, kbdSpeedtoggle, sdl_native2keysym(SDL_SCANCODE_Y), "SDL keysym for toggling the framerate limiter"); ConfigSetDefaultInt(l_CoreEventsConfig, kbdAdvance, sdl_native2keysym(SDL_SCANCODE_SLASH), "SDL keysym for advancing by one frame when paused"); ConfigSetDefaultInt(l_CoreEventsConfig, kbdGameshark, sdl_native2keysym(SDL_SCANCODE_G), "SDL keysym for pressing the game shark button"); /* Joystick events mapped to core functions */ @@ -611,6 +613,8 @@ void event_sdl_keydown(int keysym, int keymod) main_reset(0); else if (keysym == sdl_keysym2native(ConfigGetParamInt(l_CoreEventsConfig, kbdSpeeddown))) main_speeddown(5); + else if (keysym == sdl_keysym2native(ConfigGetParamInt(l_CoreEventsConfig, kbdSpeedtoggle))) + main_speedlimiter_toggle(); else if (keysym == sdl_keysym2native(ConfigGetParamInt(l_CoreEventsConfig, kbdSpeedup))) main_speedup(5); else if (keysym == sdl_keysym2native(ConfigGetParamInt(l_CoreEventsConfig, kbdScreenshot))) diff --git a/mupen64plus-core/src/main/main.c b/mupen64plus-core/src/main/main.c index 2852e357..1318243f 100644 --- a/mupen64plus-core/src/main/main.c +++ b/mupen64plus-core/src/main/main.c @@ -56,6 +56,7 @@ #include "cheat.h" #include "device/device.h" #include "device/dd/disk.h" +#include "device/controllers/vru_controller.h" #include "device/controllers/paks/biopak.h" #include "device/controllers/paks/mempak.h" #include "device/controllers/paks/rumblepak.h" @@ -146,6 +147,9 @@ static void* l_paks[GAME_CONTROLLERS_COUNT][PAK_MAX_SIZE]; static const struct pak_interface* l_ipaks[PAK_MAX_SIZE]; static size_t l_pak_type_idx[6]; +/* PRNG state - used for Mempaks ID generation */ +static struct xoshiro256pp_state l_mpk_idgen; + /********************************************************************************************************* * static functions */ @@ -155,6 +159,30 @@ static const char *get_savepathdefault(const char *configpath) return ""; } +static char *get_save_filename(void) +{ + static char filename[256]; + + int format = ConfigGetParamInt(g_CoreConfig, "SaveFilenameFormat"); + + if (format == 0) { + snprintf(filename, 256, "%s", ROM_PARAMS.headername); + } else /* if (format == 1) */ { + if (strstr(ROM_SETTINGS.goodname, "(unknown rom)") == NULL) { + snprintf(filename, 256, "%.32s-%.8s", ROM_SETTINGS.goodname, ROM_SETTINGS.MD5); + } else if (ROM_HEADER.Name[0] != 0) { + snprintf(filename, 256, "%s-%.8s", ROM_PARAMS.headername, ROM_SETTINGS.MD5); + } else { + snprintf(filename, 256, "unknown-%.8s", ROM_SETTINGS.MD5); + } + } + + /* sanitize filename */ + string_replace_chars(filename, ":<>\"/\\|?*", '_'); + + return filename; +} + static char *get_mempaks_path(void) { return ""; @@ -203,6 +231,12 @@ const char *get_dd_disk_save_path(const char* diskname, int save_format) return newPath; } +const char *get_savestatefilename(void) +{ + /* return same file name as save files */ + return get_save_filename(); +} + void main_message(m64p_msg_level level, unsigned int corner, const char *format, ...) { va_list ap; @@ -317,6 +351,26 @@ static void main_set_speedlimiter(int enable) l_MainSpeedLimit = enable ? 1 : 0; } +void main_speedlimiter_toggle(void) +{ + if (netplay_is_init()) + return; + + l_MainSpeedLimit = !l_MainSpeedLimit; + main_set_speedlimiter(l_MainSpeedLimit); + + if (l_MainSpeedLimit) /* fix naturally occuring audio desync */ + { + main_toggle_pause(); + SDL_Delay(1000); + main_toggle_pause(); + main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "Speed limiter enabled"); + } + + else + main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "Speed limiter disabled"); +} + static int main_is_paused(void) { return (g_EmulatorRunning && g_rom_pause); @@ -448,6 +502,7 @@ m64p_error main_core_state_query(m64p_core_param param, int *rval) *rval = event_gameshark_active(); break; // these are only used for callbacks; they cannot be queried or set + case M64CORE_SCREENSHOT_CAPTURED: case M64CORE_STATE_LOADCOMPLETE: case M64CORE_STATE_SAVECOMPLETE: return M64ERR_INPUT_INVALID; @@ -880,8 +935,14 @@ static void open_eep_file(struct file_storage* storage) } } -static void load_dd_rom(uint8_t* rom, size_t* rom_size) +static void load_dd_rom(uint8_t* rom, size_t* rom_size, uint8_t* disk_region) { + /* set the DD rom region */ + if (g_media_loader.set_dd_rom_region != NULL) + { + g_media_loader.set_dd_rom_region(g_media_loader.cb_data, *disk_region); + } + /* ask the core loader for DD disk filename */ char* dd_ipl_rom_filename = (g_media_loader.get_dd_rom == NULL) ? NULL @@ -962,7 +1023,7 @@ static void load_dd_rom(uint8_t* rom, size_t* rom_size) extern char* retro_dd_path_img; extern char* retro_dd_path_rom; -static void load_dd_disk(struct dd_disk* dd_disk, const struct storage_backend_interface** dd_idisk) +static int load_dd_disk(struct dd_disk* dd_disk, const struct storage_backend_interface** dd_idisk) { /* ask the core loader for DD disk filename */ char* dd_disk_filename = (g_media_loader.get_dd_disk == NULL) @@ -1012,7 +1073,7 @@ static void load_dd_disk(struct dd_disk* dd_disk, const struct storage_backend_i if (save_format == 0) { if (open_rom_file_storage(fstorage, save_filename) != file_ok) { - DebugMessage(M64MSG_ERROR, "Failed to load DD Disk save: %s.", save_filename); + DebugMessage(M64MSG_WARNING, "Failed to load DD Disk save: %s.", save_filename); /* Try loading regular disk file */ if (open_rom_file_storage(fstorage, dd_disk_filename) != file_ok) { @@ -1058,7 +1119,7 @@ static void load_dd_disk(struct dd_disk* dd_disk, const struct storage_backend_i { if (read_from_file(save_filename, &fstorage->data[offset_ram], size_ram) != file_ok) { - DebugMessage(M64MSG_ERROR, "Failed to load DD Disk RAM area (*.ram): %s.", save_filename); + DebugMessage(M64MSG_WARNING, "Failed to load DD Disk RAM area (*.ram): %s.", save_filename); } } @@ -1091,6 +1152,7 @@ static void load_dd_disk(struct dd_disk* dd_disk, const struct storage_backend_i dd_disk->isave_storage = (save_format >= 0) ? &g_ifile_storage : NULL; dd_disk->format = format; dd_disk->development = development; + dd_disk->region = DDREGION_UNKNOWN; dd_disk->offset_sys = offset_sys; dd_disk->offset_id = offset_id; dd_disk->offset_ram = offset_ram; @@ -1103,13 +1165,27 @@ static void load_dd_disk(struct dd_disk* dd_disk, const struct storage_backend_i (*dd_idisk)->size(dd_disk), get_disk_format_name(format)); + /* Get region from disk and byteswap it as needed */ uint32_t w = *(uint32_t*)(*dd_idisk)->data(dd_disk); + if (dd_disk->format == DISK_FORMAT_SDK) { + swap_buffer(&w, sizeof(w), 1); + } + + /* Set region in dd_disk */ + if (w == DD_REGION_DV || development) { + dd_disk->region = DDREGION_DEV; + } else if (w == DD_REGION_JP) { + dd_disk->region = DDREGION_JAPAN; + } else if (w == DD_REGION_US) { + dd_disk->region = DDREGION_US; + } + if (w == DD_REGION_JP || w == DD_REGION_US || w == DD_REGION_DV) { DebugMessage(M64MSG_WARNING, "Loading a saved disk"); } free(dd_disk_filename); - return; + return 1; wrong_disk_format: /* no need to close save_storage as it is a child of disk->storage */ @@ -1120,6 +1196,8 @@ static void load_dd_disk(struct dd_disk* dd_disk, const struct storage_backend_i no_disk: free(dd_disk_filename); *dd_idisk = NULL; + + return 0; } static void close_dd_disk(struct dd_disk* disk) @@ -1298,6 +1376,7 @@ m64p_error main_run(void) struct file_storage sra; size_t dd_rom_size; struct dd_disk dd_disk; + m64p_error failure_rval; struct audio_out_backend_interface audio_out_backend_libretro; int control_ids[GAME_CONTROLLERS_COUNT]; @@ -1329,22 +1408,32 @@ m64p_error main_run(void) break; } - if (ForceDisableExtraMem == 1) - disable_extra_mem = 1; + /* Seed MPK ID gen using current time */ + uint64_t mpk_seed = !netplay_is_init() ? (uint64_t)time(NULL) : 0; + l_mpk_idgen = xoshiro256pp_seed(mpk_seed); - rdram_size = (disable_extra_mem == 0) ? 0x800000 : 0x400000; + no_compiled_jump = ConfigGetParamBool(g_CoreConfig, "NoCompiledJump"); + //We disable any randomness for netplay + //randomize_interrupt = !netplay_is_init() ? ConfigGetParamBool(g_CoreConfig, "RandomizeInterrupt") : 0; + + if (ROM_SETTINGS.disableextramem) + disable_extra_mem = ROM_SETTINGS.disableextramem; + else + disable_extra_mem = ForceDisableExtraMem; if (count_per_op <= 0) count_per_op = ROM_SETTINGS.countperop; - if (count_per_op_denom_pot > 11) - count_per_op_denom_pot = 11; + if (count_per_op_denom_pot > 20) + count_per_op_denom_pot = 20; si_dma_duration = ROM_SETTINGS.sidmaduration; //During netplay, player 1 is the source of truth for these settings netplay_sync_settings(&count_per_op, &count_per_op_denom_pot, &disable_extra_mem, &si_dma_duration, &emumode, &no_compiled_jump); + rdram_size = (disable_extra_mem == 0) ? 0x800000 : 0x400000; + cheat_add_hacks(&g_cheat_ctx, ROM_PARAMS.cheats); /* do byte-swapping if it hasn't been done yet */ @@ -1407,10 +1496,22 @@ m64p_error main_run(void) const struct storage_backend_interface* dd_idisk = NULL; memset(&dd_disk, 0, sizeof(dd_disk)); - load_dd_rom((uint8_t*)mem_base_u32(g_mem_base, MM_DD_ROM), &dd_rom_size); - if (dd_rom_size > 0) { + /* try to load DD disk first, if that succeeds, pass the region to load_dd_rom */ + if (load_dd_disk(&dd_disk, &dd_idisk)) + { dd_rtc_iclock = &g_iclock_ctime_plus_delta; - load_dd_disk(&dd_disk, &dd_idisk); + load_dd_rom((uint8_t*)mem_base_u32(g_mem_base, MM_DD_ROM), &dd_rom_size, &dd_disk.region); + } + else + { + dd_rom_size = 0; + } + + /* ensure the 64DD rom & disk are loaded, + * otherwise we have to bail right now */ + if (g_rom_size == 0 && dd_rom_size == 0) + { + goto on_disk_failure; } /* setup pif channel devices */ @@ -1434,6 +1535,27 @@ m64p_error main_run(void) joybus_devices[i] = &control_ids[i]; ijoybus_devices[i] = &g_ijoybus_device_plugin_compat; } + else if (Controls[i].Type == CONT_TYPE_VRU) { + const struct game_controller_flavor* cont_flavor = + &g_vru_controller_flavor; + joybus_devices[i] = &g_dev.controllers[i]; + ijoybus_devices[i] = &g_ijoybus_vru_controller; + + cin_compats[i].control_id = (int)i; + cin_compats[i].cont = &g_dev.controllers[i]; + cin_compats[i].last_pak_type = Controls[i].Plugin; + cin_compats[i].last_input = 0; + cin_compats[i].netplay_count = 0; + cin_compats[i].event_first = NULL; + + Controls[i].Plugin = PLUGIN_NONE; + + /* init vru_controller */ + init_game_controller(&g_dev.controllers[i], + cont_flavor, + &cin_compats[i], &g_icontroller_input_backend_plugin_compat, + NULL, NULL); + } /* otherwise let the core do the processing */ else { /* select appropriate controller @@ -1563,7 +1685,7 @@ m64p_error main_run(void) no_compiled_jump, randomize_interrupt, g_start_address, - &g_dev.ai, &audio_out_backend_libretro, + &g_dev.ai, &audio_out_backend_libretro, ((float)ROM_SETTINGS.aidmamodifier / 100.0), si_dma_duration, rdram_size, joybus_devices, ijoybus_devices, @@ -1580,6 +1702,7 @@ m64p_error main_run(void) &dd_disk, dd_idisk); // Attach rom to plugins + failure_rval = M64ERR_PLUGIN_FAIL; if (!gfx.romOpen()) { goto on_gfx_open_failure; @@ -1606,7 +1729,7 @@ m64p_error main_run(void) /* release gb_carts */ for(i = 0; i < GAME_CONTROLLERS_COUNT; ++i) { - if (!Controls[i].RawData && g_dev.gb_carts[i].read_gb_cart != NULL) { + if (!Controls[i].RawData && (Controls[i].Type == CONT_TYPE_STANDARD) && g_dev.gb_carts[i].read_gb_cart != NULL) { release_gb_rom(&l_gb_carts_data[i]); release_gb_ram(&l_gb_carts_data[i]); } @@ -1622,6 +1745,8 @@ m64p_error main_run(void) close_file_storage(&mpk); #endif + /* reset pif */ + close_pif(); close_dd_disk(&dd_disk); /* Emulation stopped */ @@ -1648,6 +1773,10 @@ m64p_error main_run(void) return M64ERR_SUCCESS; +on_disk_failure: + failure_rval = M64ERR_INVALID_STATE; + rsp.romClosed(); + input.romClosed(); on_input_open_failure: audio.romClosed(); on_audio_open_failure: @@ -1655,7 +1784,7 @@ m64p_error main_run(void) on_gfx_open_failure: /* release gb_carts */ for(i = 0; i < GAME_CONTROLLERS_COUNT; ++i) { - if (!Controls[i].RawData && g_dev.gb_carts[i].read_gb_cart != NULL) { + if (!Controls[i].RawData && (Controls[i].Type == CONT_TYPE_STANDARD) && g_dev.gb_carts[i].read_gb_cart != NULL) { release_gb_rom(&l_gb_carts_data[i]); release_gb_ram(&l_gb_carts_data[i]); } @@ -1673,7 +1802,10 @@ m64p_error main_run(void) close_dd_disk(&dd_disk); #endif - return M64ERR_PLUGIN_FAIL; + /* reset pif */ + close_pif(); + + return failure_rval; } void main_stop(void) @@ -1696,8 +1828,11 @@ void main_stop(void) m64p_error open_pif(const unsigned char* pifimage, unsigned int size) { - md5_byte_t pif_ntsc_md5[] = {0x49, 0x21, 0xD5, 0xF2, 0x16, 0x5D, 0xEE, 0x6E, 0x24, 0x96, 0xF4, 0x38, 0x8C, 0x4C, 0x81, 0xDA}; - md5_byte_t pif_pal_md5[] = {0x2B, 0x6E, 0xEC, 0x58, 0x6F, 0xAA, 0x43, 0xF3, 0x46, 0x23, 0x33, 0xB8, 0x44, 0x83, 0x45, 0x54}; + md5_byte_t old_pif_ntsc_md5[] = {0x49, 0x21, 0xD5, 0xF2, 0x16, 0x5D, 0xEE, 0x6E, 0x24, 0x96, 0xF4, 0x38, 0x8C, 0x4C, 0x81, 0xDA}; + md5_byte_t old_pif_pal_md5[] = {0x2B, 0x6E, 0xEC, 0x58, 0x6F, 0xAA, 0x43, 0xF3, 0x46, 0x23, 0x33, 0xB8, 0x44, 0x83, 0x45, 0x54}; + + md5_byte_t pif_ntsc_md5[] = {0x5C, 0x12, 0x4E, 0x79, 0x48, 0xAD, 0xA8, 0x5D, 0xA6, 0x03, 0xA5, 0x22, 0x78, 0x29, 0x40, 0xD0}; + md5_byte_t pif_pal_md5[] = {0xD4, 0x23, 0x2D, 0xC9, 0x35, 0xCA, 0xD0, 0x65, 0x0A, 0xC2, 0x66, 0x4D, 0x52, 0x28, 0x1F, 0x3A}; uint32_t *dst32 = mem_base_u32(g_mem_base, MM_PIF_MEM); uint32_t *src32 = (uint32_t*) pifimage; @@ -1708,10 +1843,16 @@ m64p_error open_pif(const unsigned char* pifimage, unsigned int size) md5_append(&state, (const md5_byte_t*)pifimage, size); md5_finish(&state, digest); - if (memcmp(digest, pif_ntsc_md5, 16) == 0) + if (memcmp(digest, old_pif_ntsc_md5, 16) == 0 || + memcmp(digest, pif_ntsc_md5, 16) == 0) + { DebugMessage(M64MSG_INFO, "Using NTSC PIF ROM"); - else if (memcmp(digest, pif_pal_md5, 16) == 0) + } + else if (memcmp(digest, old_pif_pal_md5, 16) == 0 || + memcmp(digest, pif_pal_md5, 16) == 0) + { DebugMessage(M64MSG_INFO, "Using PAL PIF ROM"); + } else { DebugMessage(M64MSG_ERROR, "Invalid PIF ROM"); @@ -1724,3 +1865,9 @@ m64p_error open_pif(const unsigned char* pifimage, unsigned int size) g_start_address = UINT32_C(0xbfc00000); return M64ERR_SUCCESS; } + +m64p_error close_pif(void) +{ + g_start_address = UINT32_C(0xa4000040); + return M64ERR_SUCCESS; +} diff --git a/mupen64plus-core/src/main/main.h b/mupen64plus-core/src/main/main.h index 68efd5ee..41f452af 100644 --- a/mupen64plus-core/src/main/main.h +++ b/mupen64plus-core/src/main/main.h @@ -57,6 +57,7 @@ extern int g_gs_vi_counter; const char* get_savestatepath(void); const char* get_savesrampath(void); +const char* get_savestatefilename(void); void new_frame(void); void new_vi(void); @@ -76,6 +77,7 @@ void main_advance_one(void); void main_speedup(int percent); void main_speeddown(int percent); void main_set_fastforward(int enable); +void main_speedlimiter_toggle(void); void main_take_next_screenshot(void); @@ -100,6 +102,7 @@ int main_volume_get_muted(void); m64p_error main_reset(int do_hard_reset); m64p_error open_pif(const unsigned char* pifimage, unsigned int size); +m64p_error close_pif(void); #endif /* __MAIN_H__ */ diff --git a/mupen64plus-core/src/main/netplay.c b/mupen64plus-core/src/main/netplay.c index 8d856cb6..6cc6d664 100644 --- a/mupen64plus-core/src/main/netplay.c +++ b/mupen64plus-core/src/main/netplay.c @@ -19,6 +19,8 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +#define SETTINGS_SIZE 24 + #define M64P_CORE_PROTOTYPES 1 #include "api/callbacks.h" #include "main.h" @@ -32,13 +34,14 @@ #include #if !defined(WIN32) #include +#endif + #ifndef HAVE_LIBNX #include #else #include #include #endif // HAVE_LIBNX -#endif static int l_canFF; static int l_netplay_controller; @@ -60,6 +63,7 @@ static uint8_t l_player_lag[4]; #define UDP_SEND_KEY_INFO 0 #define UDP_RECEIVE_KEY_INFO 1 #define UDP_REQUEST_KEY_INFO 2 +#define UDP_RECEIVE_KEY_INFO_GRATUITOUS 3 #define UDP_SYNC_DATA 4 //TCP packet formats @@ -76,7 +80,7 @@ struct __UDPSocket { int channel; }; -#define EF 46 +#define CS4 32 m64p_error netplay_start(const char* host, int port) { @@ -94,7 +98,7 @@ m64p_error netplay_start(const char* host, int port) } #if !defined(WIN32) - const char tos_local = EF << 2; + const char tos_local = CS4 << 2; struct __UDPSocket* socket = (struct __UDPSocket*) l_udpSocket; setsockopt(socket->channel, IPPROTO_IP, IP_TOS, &tos_local, sizeof(tos_local)); #endif @@ -253,11 +257,13 @@ static void netplay_process() switch (packet->data[0]) { case UDP_RECEIVE_KEY_INFO: + case UDP_RECEIVE_KEY_INFO_GRATUITOUS: player = packet->data[1]; //current_status is a status update from the server //it will let us know if another player has disconnected, or the games have desynced current_status = packet->data[2]; - l_player_lag[player] = packet->data[3]; + if (packet->data[0] == UDP_RECEIVE_KEY_INFO) + l_player_lag[player] = packet->data[3]; if (current_status != l_status) { if (((current_status & 0x1) ^ (l_status & 0x1)) != 0) @@ -461,10 +467,11 @@ file_status_t netplay_read_storage(const char *filename, void *data, size_t size //This function syncs save games. //If the client is controlling player 1, it sends its save game to the server //All other players receive save files from the server - const char *short_filename = filename; + const char *file_extension = strrchr(filename, '.'); + file_extension += 1; uint32_t buffer_pos = 0; - char *output_data = malloc(size + strlen(short_filename) + 6); + char *output_data = malloc(size + strlen(file_extension) + 6); file_status_t ret; uint8_t request; @@ -474,9 +481,9 @@ file_status_t netplay_read_storage(const char *filename, void *data, size_t size memcpy(&output_data[buffer_pos], &request, 1); ++buffer_pos; - //send file name - memcpy(&output_data[buffer_pos], short_filename, strlen(short_filename) + 1); - buffer_pos += strlen(short_filename) + 1; + //send file extension + memcpy(&output_data[buffer_pos], file_extension, strlen(file_extension) + 1); + buffer_pos += strlen(file_extension) + 1; // Not wanted for libretro //ret = read_from_file(filename, data, size); @@ -495,9 +502,9 @@ file_status_t netplay_read_storage(const char *filename, void *data, size_t size memcpy(&output_data[buffer_pos], &request, 1); ++buffer_pos; - //name of the file we are requesting - memcpy(&output_data[buffer_pos], short_filename, strlen(short_filename) + 1); - buffer_pos += strlen(short_filename) + 1; + //extension of the file we are requesting + memcpy(&output_data[buffer_pos], file_extension, strlen(file_extension) + 1); + buffer_pos += strlen(file_extension) + 1; SDLNet_TCP_Send(l_tcpSocket, &output_data[0], buffer_pos); size_t recv = 0; @@ -523,7 +530,7 @@ void netplay_sync_settings(uint32_t *count_per_op, uint32_t *count_per_op_denom_ if (!netplay_is_init()) return; - char output_data[25]; + char output_data[SETTINGS_SIZE + 1]; uint8_t request; if (l_netplay_control[0] != -1) //player 1 is the source of truth for settings { @@ -535,7 +542,7 @@ void netplay_sync_settings(uint32_t *count_per_op, uint32_t *count_per_op_denom_ SDLNet_Write32(*si_dma_duration, &output_data[13]); SDLNet_Write32(*emumode, &output_data[17]); SDLNet_Write32(*no_compiled_jump, &output_data[21]); - SDLNet_TCP_Send(l_tcpSocket, &output_data[0], 25); + SDLNet_TCP_Send(l_tcpSocket, &output_data[0], SETTINGS_SIZE + 1); } else { @@ -543,8 +550,8 @@ void netplay_sync_settings(uint32_t *count_per_op, uint32_t *count_per_op_denom_ memcpy(&output_data[0], &request, 1); SDLNet_TCP_Send(l_tcpSocket, &output_data[0], 1); int32_t recv = 0; - while (recv < 24) - recv += SDLNet_TCP_Recv(l_tcpSocket, &output_data[recv], 24 - recv); + while (recv < SETTINGS_SIZE) + recv += SDLNet_TCP_Recv(l_tcpSocket, &output_data[recv], SETTINGS_SIZE - recv); *count_per_op = SDLNet_Read32(&output_data[0]); *count_per_op_denom_pot = SDLNet_Read32(&output_data[4]); *disable_extra_mem = SDLNet_Read32(&output_data[8]); @@ -557,14 +564,14 @@ void netplay_sync_settings(uint32_t *count_per_op, uint32_t *count_per_op_denom_ void netplay_check_sync(struct cp0* cp0) { //This function is used to check if games have desynced - //Every 60 VIs, it sends the value of the CP0 registers to the server + //Every 600 VIs, it sends the value of the CP0 registers to the server //The server will compare the values, and update the status byte if it detects a desync if (!netplay_is_init()) return; const uint32_t* cp0_regs = r4300_cp0_regs(cp0); - if (l_vi_counter % 60 == 0) + if (l_vi_counter % 600 == 0) { uint32_t packet_len = (CP0_REGS_COUNT * 4) + 5; UDPpacket *packet = SDLNet_AllocPacket(packet_len); @@ -605,14 +612,19 @@ void netplay_read_registration(struct controller_input_compat* cin_compats) if (reg_id == 0) //No one registered to control this player { Controls[i].Present = 0; - Controls[i].Plugin = 1; + Controls[i].Plugin = PLUGIN_NONE; Controls[i].RawData = 0; curr += 2; } else { Controls[i].Present = 1; - Controls[i].Plugin = input_data[curr]; + if (i > 0 && input_data[curr] == PLUGIN_MEMPAK) // only P1 can use mempak + Controls[i].Plugin = PLUGIN_NONE; + else if (input_data[curr] == PLUGIN_TRANSFER_PAK) // Transferpak not supported during netplay + Controls[i].Plugin = PLUGIN_NONE; + else + Controls[i].Plugin = input_data[curr]; l_plugin[i] = Controls[i].Plugin; ++curr; Controls[i].RawData = input_data[curr]; @@ -679,12 +691,6 @@ void netplay_update_input(struct pif* pif) } } -void netplay_set_plugin(uint8_t control_id, uint8_t plugin) -{ - if (!(control_id > 0 && plugin == 2)) //Only P1 can use mempak - l_plugin[control_id] = plugin; -} - m64p_error netplay_send_config(char* data, int size) { if (!netplay_is_init()) diff --git a/mupen64plus-core/src/main/netplay.h b/mupen64plus-core/src/main/netplay.h index a65b1958..92b1681e 100644 --- a/mupen64plus-core/src/main/netplay.h +++ b/mupen64plus-core/src/main/netplay.h @@ -52,7 +52,6 @@ void netplay_check_sync(struct cp0* cp0); int netplay_next_controller(); void netplay_read_registration(struct controller_input_compat* cin_compats); void netplay_update_input(struct pif* pif); -void netplay_set_plugin(uint8_t control_id, uint8_t plugin); m64p_error netplay_send_config(char* data, int size); m64p_error netplay_receive_config(char* data, int size); diff --git a/mupen64plus-core/src/main/rom.c b/mupen64plus-core/src/main/rom.c index 5862db97..ed59d011 100644 --- a/mupen64plus-core/src/main/rom.c +++ b/mupen64plus-core/src/main/rom.c @@ -35,6 +35,8 @@ #include "api/config.h" #include "api/m64p_config.h" #include "api/m64p_types.h" +#include "device/dd/disk.h" +#include "backends/file_storage.h" #include "device/device.h" #include "main.h" #include "md5.h" @@ -51,6 +53,8 @@ enum { DEFAULT_COUNT_PER_OP = 2 }; enum { DEFAULT_DISABLE_EXTRA_MEM = 0 }; /* Default SI DMA duration */ enum { DEFAULT_SI_DMA_DURATION = 0x900 }; +/* Default AI DMA modifier */ +enum { DEFAULT_AI_DMA_MODIFIER = 100 }; static romdatabase_entry* ini_search_by_md5(md5_byte_t* md5); @@ -65,16 +69,18 @@ m64p_rom_settings ROM_SETTINGS; static m64p_system_type rom_country_code_to_system_type(uint16_t country_code); +static unsigned char rom_homebrew_savetype_to_savetype(uint8_t save_type); + static const uint8_t Z64_SIGNATURE[4] = { 0x80, 0x37, 0x12, 0x40 }; static const uint8_t V64_SIGNATURE[4] = { 0x37, 0x80, 0x40, 0x12 }; static const uint8_t N64_SIGNATURE[4] = { 0x40, 0x12, 0x37, 0x80 }; -/* Tests if a file is a valid N64 rom by checking the first 4 bytes. */ -static int is_valid_rom(const unsigned char *buffer) +/* Tests if a file is a valid N64 rom by checking the first 4 bytes and size */ +static int is_valid_rom(const unsigned char *buffer, unsigned int size) { - if (memcmp(buffer, Z64_SIGNATURE, sizeof(Z64_SIGNATURE)) == 0 - || memcmp(buffer, V64_SIGNATURE, sizeof(V64_SIGNATURE)) == 0 - || memcmp(buffer, N64_SIGNATURE, sizeof(N64_SIGNATURE)) == 0) + if ((memcmp(buffer, Z64_SIGNATURE, sizeof(Z64_SIGNATURE)) == 0) + || (memcmp(buffer, V64_SIGNATURE, sizeof(V64_SIGNATURE)) == 0 && size % 2 == 0) + || (memcmp(buffer, N64_SIGNATURE, sizeof(N64_SIGNATURE)) == 0 && size % 4 == 0)) return 1; else return 0; @@ -139,7 +145,7 @@ m64p_error open_rom(const unsigned char* romimage, unsigned int size) int i; /* check input requirements */ - if (romimage == NULL || !is_valid_rom(romimage)) + if (romimage == NULL || !is_valid_rom(romimage, size)) { DebugMessage(M64MSG_ERROR, "open_rom(): not a valid ROM image"); return M64ERR_INPUT_INVALID; @@ -187,14 +193,13 @@ m64p_error open_rom(const unsigned char* romimage, unsigned int size) ROM_SETTINGS.countperop = entry->countperop; ROM_SETTINGS.disableextramem = entry->disableextramem; ROM_SETTINGS.sidmaduration = entry->sidmaduration; + ROM_SETTINGS.aidmamodifier = entry->aidmamodifier; ROM_PARAMS.cheats = entry->cheats; } else { strcpy(ROM_SETTINGS.goodname, ROM_PARAMS.headername); strcat(ROM_SETTINGS.goodname, " (unknown rom)"); - /* There's no way to guess the save type, but 4K EEPROM is better than nothing */ - ROM_SETTINGS.savetype = SAVETYPE_EEPROM_4K; ROM_SETTINGS.status = 0; ROM_SETTINGS.players = 4; ROM_SETTINGS.rumble = 1; @@ -204,7 +209,20 @@ m64p_error open_rom(const unsigned char* romimage, unsigned int size) ROM_SETTINGS.countperop = DEFAULT_COUNT_PER_OP; ROM_SETTINGS.disableextramem = DEFAULT_DISABLE_EXTRA_MEM; ROM_SETTINGS.sidmaduration = DEFAULT_SI_DMA_DURATION; + ROM_SETTINGS.aidmamodifier = DEFAULT_AI_DMA_MODIFIER; ROM_PARAMS.cheats = NULL; + + /* check if ROM has the Advanced Homebrew ROM Header (see https://n64brew.dev/wiki/ROM_Header) */ + if (ROM_HEADER.Cartridge_ID == 0x4445) + { + /* When current ROM has the Advanced Homebrew ROM Header, use the save type */ + ROM_SETTINGS.savetype = rom_homebrew_savetype_to_savetype(ROM_HEADER.Version >> 4); + } + else + { + /* There's no way to guess the save type, but 4K EEPROM is better than nothing */ + ROM_SETTINGS.savetype = SAVETYPE_EEPROM_4K; + } } /* print out a bunch of info about the ROM */ @@ -239,13 +257,139 @@ m64p_error close_rom(void) return M64ERR_SUCCESS; } +m64p_error open_disk(void) +{ + md5_state_t state; + md5_byte_t digest[16]; + romdatabase_entry* entry; + char buffer[256]; + int i; + + /* ask the core loader for DD disk filename */ + char* dd_disk_filename = (g_media_loader.get_dd_disk == NULL) + ? NULL + : g_media_loader.get_dd_disk(g_media_loader.cb_data); + + /* handle the no disk case */ + if (dd_disk_filename == NULL || strlen(dd_disk_filename) == 0) { + goto no_disk; + } + + /* Get DD Disk size */ + size_t dd_size = 0; + if (get_file_size(dd_disk_filename, &dd_size) != file_ok) { + goto no_disk; + } + + struct file_storage* fstorage = malloc(sizeof(struct file_storage)); + if (fstorage == NULL) { + goto no_disk; + } + + /* Try loading regular disk file */ + if (open_rom_file_storage(fstorage, dd_disk_filename) != file_ok) { + goto free_fstorage; + } + + /* Scan disk to deduce disk format and other parameters and expand its size for D64 */ + unsigned int format = 0; + unsigned int development = 0; + size_t offset_sys = 0; + size_t offset_id = 0; + size_t offset_ram = 0; + size_t size_ram = 0; + uint8_t* new_data = scan_and_expand_disk_format(fstorage->data, fstorage->size, &format, &development, &offset_sys, &offset_id, &offset_ram, &size_ram); + if (new_data == NULL) { + goto wrong_disk_format; + } + else { + fstorage->data = new_data; + } + + /* Calculate MD5 hash */ + md5_init(&state); + md5_append(&state, (const md5_byte_t*)(fstorage->data), fstorage->size); + md5_finish(&state, digest); + for ( i = 0; i < 16; ++i ) + sprintf(buffer+i*2, "%02X", digest[i]); + buffer[32] = '\0'; + strcpy(ROM_SETTINGS.MD5, buffer); + + /* Look up this disk in the .ini file and fill in goodname, etc */ + if ((entry=ini_search_by_md5(digest)) != NULL) + { + strncpy(ROM_SETTINGS.goodname, entry->goodname, 255); + ROM_SETTINGS.goodname[255] = '\0'; + ROM_SETTINGS.savetype = entry->savetype; + ROM_SETTINGS.status = entry->status; + ROM_SETTINGS.players = entry->players; + ROM_SETTINGS.rumble = entry->rumble; + ROM_SETTINGS.transferpak = entry->transferpak; + ROM_SETTINGS.mempak = entry->mempak; + ROM_SETTINGS.biopak = entry->biopak; + ROM_SETTINGS.countperop = entry->countperop; + ROM_SETTINGS.disableextramem = entry->disableextramem; + ROM_SETTINGS.sidmaduration = entry->sidmaduration; + ROM_SETTINGS.aidmamodifier = entry->aidmamodifier; + ROM_PARAMS.cheats = entry->cheats; + } + else + { + strcpy(ROM_SETTINGS.goodname, "(unknown disk)"); + /* There's no way to guess the save type, but 4K EEPROM is better than nothing */ + ROM_SETTINGS.savetype = SAVETYPE_EEPROM_4K; + ROM_SETTINGS.status = 0; + ROM_SETTINGS.players = 4; + ROM_SETTINGS.rumble = 1; + ROM_SETTINGS.transferpak = 0; + ROM_SETTINGS.mempak = 1; + ROM_SETTINGS.biopak = 0; + ROM_SETTINGS.countperop = DEFAULT_COUNT_PER_OP; + ROM_SETTINGS.disableextramem = DEFAULT_DISABLE_EXTRA_MEM; + ROM_SETTINGS.sidmaduration = DEFAULT_SI_DMA_DURATION; + ROM_SETTINGS.aidmamodifier = DEFAULT_AI_DMA_MODIFIER; + ROM_PARAMS.cheats = NULL; + } + + /* set system type */ + ROM_PARAMS.systemtype = SYSTEM_NTSC; + + /* clear rom header & size */ + memset(&ROM_HEADER, 0, sizeof(m64p_rom_header)); + memset(ROM_PARAMS.headername, 0, 20); + g_rom_size = 0; + + close_file_storage(fstorage); + free(fstorage); + return M64ERR_SUCCESS; + +wrong_disk_format: + close_file_storage(fstorage); + dd_disk_filename = NULL; /* already freed in close_file_storage */ +free_fstorage: + free(fstorage); +no_disk: + if (dd_disk_filename != NULL) { + free(dd_disk_filename); + } + + DebugMessage(M64MSG_ERROR, "open_disk(): not a valid disk image"); + return M64ERR_INPUT_INVALID; +} + +m64p_error close_disk(void) +{ + DebugMessage(M64MSG_STATUS, "Disk closed."); + return M64ERR_SUCCESS; +} + /********************************************************************************************/ /* ROM utility functions */ // Get the system type associated to a ROM country code. static m64p_system_type rom_country_code_to_system_type(uint16_t country_code) { - switch (country_code & UINT16_C(0xFF)) + switch (country_code) { // PAL codes case 0x44: @@ -268,6 +412,36 @@ static m64p_system_type rom_country_code_to_system_type(uint16_t country_code) } } +// Converts the homebrew advanced rom header savetype to a m64p_rom_save_type +static unsigned char rom_homebrew_savetype_to_savetype(uint8_t save_type) +{ + unsigned char m64p_save_type; + + switch (save_type) + { + default: + case 0: /* None */ + m64p_save_type = SAVETYPE_NONE; + break; + case 1: /* 4K EEPROM */ + m64p_save_type = SAVETYPE_EEPROM_4K; + break; + case 2: /* 16K EEPROM */ + m64p_save_type = SAVETYPE_EEPROM_16KB; + break; + case 3: /* 256K SRAM */ + case 4: /* 768K SRAM (banked) */ + case 6: /* 1M SRAM */ + m64p_save_type = SAVETYPE_SRAM; + break; + case 5: /* Flash RAM */ + m64p_save_type = SAVETYPE_FLASH_RAM; + break; + } + + return m64p_save_type; +} + static size_t romdatabase_resolve_round(void) { romdatabase_search *entry; @@ -372,6 +546,12 @@ static size_t romdatabase_resolve_round(void) entry->entry.set_flags |= ROMDATABASE_ENTRY_SIDMADURATION; } + if (!isset_bitmask(entry->entry.set_flags, ROMDATABASE_ENTRY_AIDMAMODIFIER) && + isset_bitmask(ref->set_flags, ROMDATABASE_ENTRY_AIDMAMODIFIER)) { + entry->entry.aidmamodifier = ref->aidmamodifier; + entry->entry.set_flags |= ROMDATABASE_ENTRY_AIDMAMODIFIER; + } + free(entry->entry.refmd5); entry->entry.refmd5 = NULL; } @@ -468,6 +648,7 @@ void romdatabase_open(void) search->entry.mempak = 1; search->entry.biopak = 0; search->entry.sidmaduration = DEFAULT_SI_DMA_DURATION; + search->entry.aidmamodifier = DEFAULT_AI_DMA_MODIFIER; search->entry.set_flags = ROMDATABASE_ENTRY_NONE; search->next_entry = NULL; @@ -664,6 +845,15 @@ void romdatabase_open(void) DebugMessage(M64MSG_WARNING, "ROM Database: Invalid SiDmaDuration on line %i", lineno); } } + else if(!strcmp(l.name, "AiDmaModifier")) + { + if (string_to_int(l.value, &value) && value >= 0 && value <= 200) { + search->entry.aidmamodifier = value; + search->entry.set_flags |= ROMDATABASE_ENTRY_AIDMAMODIFIER; + } else { + DebugMessage(M64MSG_WARNING, "ROM Database: Invalid AiDmaModifier on line %i", lineno); + } + } else { DebugMessage(M64MSG_WARNING, "ROM Database: Unknown property on line %i", lineno); diff --git a/mupen64plus-core/src/main/rom.h b/mupen64plus-core/src/main/rom.h index 133dc7ae..853fd3be 100644 --- a/mupen64plus-core/src/main/rom.h +++ b/mupen64plus-core/src/main/rom.h @@ -41,6 +41,9 @@ m64p_error open_rom(const unsigned char* romimage, unsigned int size); m64p_error close_rom(void); +m64p_error open_disk(void); +m64p_error close_disk(void); + extern int g_rom_size; typedef struct _rom_params @@ -113,6 +116,7 @@ typedef struct unsigned char mempak; /* 0 - No, 1 - Yes boolean for mempak support. */ unsigned char biopak; /* 0 - No, 1 - Yes boolean for biopak support. */ unsigned int sidmaduration; + unsigned int aidmamodifier; uint32_t set_flags; } romdatabase_entry; @@ -130,6 +134,7 @@ typedef struct #define ROMDATABASE_ENTRY_MEMPAK BIT(10) #define ROMDATABASE_ENTRY_BIOPAK BIT(11) #define ROMDATABASE_ENTRY_SIDMADURATION BIT(12) +#define ROMDATABASE_ENTRY_AIDMAMODIFIER BIT(13) typedef struct _romdatabase_search { diff --git a/mupen64plus-core/src/main/savestates.c b/mupen64plus-core/src/main/savestates.c index 108fb48f..c1edc627 100644 --- a/mupen64plus-core/src/main/savestates.c +++ b/mupen64plus-core/src/main/savestates.c @@ -62,7 +62,7 @@ enum { GB_CART_FINGERPRINT_OFFSET = 0x134 }; enum { DD_DISK_ID_OFFSET = 0x43670 }; static const char* savestate_magic = "M64+SAVE"; -static const int savestate_latest_version = 0x00010800; /* 1.8 */ +static const int savestate_latest_version = 0x00010900; /* 1.9 */ static const unsigned char pj64_magic[4] = { 0xC8, 0xA6, 0xD8, 0x23 }; static savestates_job job = savestates_job_nothing; @@ -100,31 +100,32 @@ static char *savestates_generate_path(savestates_type type) } else /* Use the selected savestate slot */ { - char *filename; + char *filepath; + size_t size = 0; + switch (type) { case savestates_type_m64p: - filename = formatstr("%s.st%d", ROM_SETTINGS.goodname, slot); + /* check if old file path exists, if it does then use that */ + filepath = formatstr("%s%s.st%d", get_savestatepath(), ROM_SETTINGS.goodname, slot); + if (get_file_size(filepath, &size) != file_ok || size == 0) + { + /* else use new path */ + filepath = formatstr("%s%s.st%d", get_savestatepath(), get_savestatefilename(), slot); + } break; case savestates_type_pj64_zip: - filename = formatstr("%s.pj%d.zip", ROM_PARAMS.headername, slot); + filepath = formatstr("%s%s.pj%d.zip", get_savestatepath(), ROM_PARAMS.headername, slot); break; case savestates_type_pj64_unc: - filename = formatstr("%s.pj%d", ROM_PARAMS.headername, slot); + filepath = formatstr("%s%s.pj%d", get_savestatepath(), ROM_PARAMS.headername, slot); break; default: - filename = NULL; + filepath = NULL; break; } - if (filename != NULL) - { - char *filepath = formatstr("%s%s", get_savestatepath(), filename); - free(filename); - return filepath; - } - else - return NULL; + return filepath; } } @@ -155,6 +156,7 @@ void savestates_inc_slot(void) { if(++slot>9) slot = 0; + ConfigSetParameter(g_CoreConfig, "CurrentStateSlot", M64TYPE_INT, &slot); StateChanged(M64CORE_SAVESTATE_SLOT, slot); } @@ -659,7 +661,7 @@ int savestates_load_m64p(struct device* dev, const void *data) COPYARRAY(cam_regs, curr, uint8_t, POCKET_CAM_REGS_COUNT); } - if (ROM_SETTINGS.transferpak && !Controls[i].RawData) { + if (ROM_SETTINGS.transferpak && !Controls[i].RawData && (Controls[i].Type == CONT_TYPE_STANDARD)) { /* init transferpak state if enabled and not controlled by input plugin */ dev->transferpaks[i].enabled = enabled; @@ -769,7 +771,7 @@ int savestates_load_m64p(struct device* dev, const void *data) uint8_t rpk_state = GETDATA(curr, uint8_t); /* init rumble pak state if enabled and not controlled by the input plugin */ - if (ROM_SETTINGS.rumble && !Controls[i].RawData) { + if (ROM_SETTINGS.rumble && !Controls[i].RawData && (Controls[i].Type == CONT_TYPE_STANDARD)) { set_rumble_reg(&dev->rumblepaks[i], rpk_state); } } @@ -804,7 +806,7 @@ int savestates_load_m64p(struct device* dev, const void *data) COPYARRAY(cam_regs, curr, uint8_t, POCKET_CAM_REGS_COUNT); } - if (ROM_SETTINGS.transferpak && !Controls[i].RawData) { + if (ROM_SETTINGS.transferpak && !Controls[i].RawData && (Controls[i].Type == CONT_TYPE_STANDARD)) { /* init transferpak state if enabled and not controlled by input plugin */ dev->transferpaks[i].enabled = enabled; @@ -963,6 +965,13 @@ int savestates_load_m64p(struct device* dev, const void *data) dev->cart.flashram.erase_page = GETDATA(curr, uint16_t); dev->cart.flashram.mode = GETDATA(curr, uint16_t); } + + if (version >= 0x00010900) + { + /* extra cp0 and cp2 state */ + *r4300_cp0_latch(&dev->r4300.cp0) = GETDATA(curr, uint64_t); + *r4300_cp2_latch(&dev->r4300.cp2) = GETDATA(curr, uint64_t); + } } else { @@ -986,10 +995,10 @@ int savestates_load_m64p(struct device* dev, const void *data) dev->controllers[i].flavor->reset(&dev->controllers[i]); - if (ROM_SETTINGS.rumble) { + if (ROM_SETTINGS.rumble && (Controls[i].Type == CONT_TYPE_STANDARD)) { poweron_rumblepak(&dev->rumblepaks[i]); } - if (ROM_SETTINGS.transferpak) { + if (ROM_SETTINGS.transferpak && (Controls[i].Type == CONT_TYPE_STANDARD)) { poweron_transferpak(&dev->transferpaks[i]); } } @@ -1360,10 +1369,10 @@ static int savestates_load_pj64(struct device* dev, dev->controllers[i].flavor->reset(&dev->controllers[i]); - if (ROM_SETTINGS.rumble) { + if (ROM_SETTINGS.rumble && (Controls[i].Type == CONT_TYPE_STANDARD)) { poweron_rumblepak(&dev->rumblepaks[i]); } - if (ROM_SETTINGS.transferpak) { + if (ROM_SETTINGS.transferpak && (Controls[i].Type == CONT_TYPE_STANDARD)) { poweron_transferpak(&dev->transferpaks[i]); } } @@ -1596,6 +1605,7 @@ static void savestates_save_m64p_work(struct work_struct *work) { main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "Could not open state file: %s", save->filepath); free(save->data); + StateChanged(M64CORE_STATE_SAVECOMPLETE, 0); return; } @@ -1605,6 +1615,7 @@ static void savestates_save_m64p_work(struct work_struct *work) main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "Could not write data to state file: %s", save->filepath); gzclose(f); free(save->data); + StateChanged(M64CORE_STATE_SAVECOMPLETE, 0); return; } @@ -1622,6 +1633,7 @@ static void savestates_save_m64p_work(struct work_struct *work) #ifdef USE_SDL SDL_UnlockMutex(savestates_lock); #else + StateChanged(M64CORE_STATE_SAVECOMPLETE, 1); pthread_mutex_unlock(&savestates_lock); #endif } @@ -1646,6 +1658,7 @@ int savestates_save_m64p(const struct device* dev, void *data) save = malloc(sizeof(*save)); if (!save) { main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "Insufficient memory to save state."); + StateChanged(M64CORE_STATE_SAVECOMPLETE, 0); return 0; } @@ -1668,6 +1681,7 @@ int savestates_save_m64p(const struct device* dev, void *data) free(save->filepath); free(save); main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "Insufficient memory to save state."); + StateChanged(M64CORE_STATE_SAVECOMPLETE, 0); return 0; } @@ -2026,6 +2040,10 @@ int savestates_save_m64p(const struct device* dev, void *data) PUTDATA(curr, uint16_t, dev->cart.flashram.erase_page); PUTDATA(curr, uint16_t, dev->cart.flashram.mode); + /* cp0 and cp2 latch (since 1.9) */ + PUTDATA(curr, uint64_t, *r4300_cp0_latch((struct cp0*)&dev->r4300.cp0)); + PUTDATA(curr, uint64_t, *r4300_cp2_latch((struct cp2*)&dev->r4300.cp2)); + init_work(&save->work, savestates_save_m64p_work); queue_work(&save->work); @@ -2237,6 +2255,7 @@ static int savestates_save_pj64_zip(const struct device* dev, char *filepath) zipCloseFileInZip(zipfile); // This may fail, but we don't care zipClose(zipfile, ""); } + StateChanged(M64CORE_STATE_SAVECOMPLETE, 1); return 1; } @@ -2253,17 +2272,20 @@ static int savestates_save_pj64_unc(const struct device* dev, char *filepath) if (f == NULL) { main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "Could not create PJ64 state file: %s", filepath); + StateChanged(M64CORE_STATE_SAVECOMPLETE, 0); return 0; } if (!savestates_save_pj64(dev, filepath, f, write_data_to_file)) { fclose(f); + StateChanged(M64CORE_STATE_SAVECOMPLETE, 0); return 0; } main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "Saved state to: %s", namefrompath(filepath)); fclose(f); + StateChanged(M64CORE_STATE_SAVECOMPLETE, 1); return 1; } @@ -2295,10 +2317,14 @@ int savestates_save(void) case savestates_type_m64p: ret = savestates_save_m64p(dev, filepath); break; case savestates_type_pj64_zip: ret = savestates_save_pj64_zip(dev, filepath); break; case savestates_type_pj64_unc: ret = savestates_save_pj64_unc(dev, filepath); break; - default: ret = 0; break; + default: ret = 0; StateChanged(M64CORE_STATE_SAVECOMPLETE, ret); break; } free(filepath); } + else + { + StateChanged(M64CORE_STATE_SAVECOMPLETE, ret); + } #else if(fname) { @@ -2307,10 +2333,10 @@ int savestates_save(void) } else { ret = 0; } -#endif // __LIBRETRO__ // deliver callback to indicate completion of state saving operation StateChanged(M64CORE_STATE_SAVECOMPLETE, ret); +#endif // __LIBRETRO__ savestates_clear_job(); diff --git a/mupen64plus-core/src/main/screenshot.c b/mupen64plus-core/src/main/screenshot.c index 79a56df1..676e7e61 100644 --- a/mupen64plus-core/src/main/screenshot.c +++ b/mupen64plus-core/src/main/screenshot.c @@ -222,7 +222,10 @@ void TakeScreenshot(int iFrameNumber) // look for an unused screenshot filename filename = GetNextScreenshotPath(); if (filename == NULL) + { + StateChanged(M64CORE_SCREENSHOT_CAPTURED, 0); return; + } // get the width and height int width = 640; @@ -233,6 +236,7 @@ void TakeScreenshot(int iFrameNumber) unsigned char *pucFrame = (unsigned char *) malloc(width * height * 3); if (pucFrame == NULL) { + StateChanged(M64CORE_SCREENSHOT_CAPTURED, 0); free(filename); return; } @@ -241,11 +245,19 @@ void TakeScreenshot(int iFrameNumber) gfx.readScreen(pucFrame, &width, &height, 0); // write the image to a PNG - SaveRGBBufferToFile(filename, pucFrame, width, height, width * 3); + int rval = SaveRGBBufferToFile(filename, pucFrame, width, height, width * 3); // free the memory free(pucFrame); free(filename); // print message -- this allows developers to capture frames and use them in the regression test - main_message(M64MSG_INFO, OSD_BOTTOM_LEFT, "Captured screenshot for frame %i.", iFrameNumber); + if (rval != 0) + { + StateChanged(M64CORE_SCREENSHOT_CAPTURED, 0); + } + else + { + main_message(M64MSG_INFO, OSD_BOTTOM_LEFT, "Captured screenshot for frame %i.", iFrameNumber); + StateChanged(M64CORE_SCREENSHOT_CAPTURED, 1); + } } diff --git a/mupen64plus-core/src/main/util.c b/mupen64plus-core/src/main/util.c index 2d9a1ea8..cc804d3a 100644 --- a/mupen64plus-core/src/main/util.c +++ b/mupen64plus-core/src/main/util.c @@ -257,6 +257,182 @@ void to_big_endian_buffer(void *buffer, size_t length, size_t count) #endif } +/* Simple serialization primitives, + * Use byte access to avoid alignment issues. + */ +uint8_t load_beu8(const unsigned char *ptr) +{ + return (uint8_t)ptr[0]; +} + +uint16_t load_beu16(const unsigned char *ptr) +{ + return ((uint16_t)ptr[0] << 8) + | ((uint16_t)ptr[1] << 0); +} + +uint32_t load_beu32(const unsigned char *ptr) +{ + return ((uint32_t)ptr[0] << 24) + | ((uint32_t)ptr[1] << 16) + | ((uint32_t)ptr[2] << 8) + | ((uint32_t)ptr[3] << 0); +} + +uint64_t load_beu64(const unsigned char *ptr) +{ + return ((uint64_t)ptr[0] << 56) + | ((uint64_t)ptr[1] << 48) + | ((uint64_t)ptr[2] << 40) + | ((uint64_t)ptr[3] << 32) + | ((uint64_t)ptr[4] << 24) + | ((uint64_t)ptr[5] << 16) + | ((uint64_t)ptr[6] << 8) + | ((uint64_t)ptr[7] << 0); +} + + +uint8_t load_leu8(const unsigned char *ptr) +{ + return (uint8_t)ptr[0]; +} + +uint16_t load_leu16(const unsigned char *ptr) +{ + return ((uint16_t)ptr[0] << 0) + | ((uint16_t)ptr[1] << 8); +} + +uint32_t load_leu32(const unsigned char *ptr) +{ + return ((uint32_t)ptr[0] << 0) + | ((uint32_t)ptr[1] << 8) + | ((uint32_t)ptr[2] << 16) + | ((uint32_t)ptr[3] << 24); +} + +uint64_t load_leu64(const unsigned char *ptr) +{ + return ((uint64_t)ptr[0] << 0) + | ((uint64_t)ptr[1] << 8) + | ((uint64_t)ptr[2] << 16) + | ((uint64_t)ptr[3] << 24) + | ((uint64_t)ptr[4] << 32) + | ((uint64_t)ptr[5] << 40) + | ((uint64_t)ptr[6] << 48) + | ((uint64_t)ptr[7] << 56); +} + + + +void store_beu8(uint8_t value, unsigned char *ptr) +{ + ptr[0] = (uint8_t)value; +} + +void store_beu16(uint16_t value, unsigned char *ptr) +{ + ptr[0] = (uint8_t)(value >> 8); + ptr[1] = (uint8_t)(value >> 0); +} + +void store_beu32(uint32_t value, unsigned char *ptr) +{ + ptr[0] = (uint8_t)(value >> 24); + ptr[1] = (uint8_t)(value >> 16); + ptr[2] = (uint8_t)(value >> 8); + ptr[3] = (uint8_t)(value >> 0); +} + +void store_beu64(uint64_t value, unsigned char *ptr) +{ + ptr[0] = (uint8_t)(value >> 56); + ptr[1] = (uint8_t)(value >> 48); + ptr[2] = (uint8_t)(value >> 40); + ptr[3] = (uint8_t)(value >> 32); + ptr[4] = (uint8_t)(value >> 24); + ptr[5] = (uint8_t)(value >> 16); + ptr[6] = (uint8_t)(value >> 8); + ptr[7] = (uint8_t)(value >> 0); +} + + +void store_leu8(uint8_t value, unsigned char *ptr) +{ + ptr[0] = (uint8_t)value; +} + +void store_leu16(uint16_t value, unsigned char *ptr) +{ + ptr[0] = (uint8_t)(value >> 0); + ptr[1] = (uint8_t)(value >> 8); +} + +void store_leu32(uint32_t value, unsigned char *ptr) +{ + ptr[0] = (uint8_t)(value >> 0); + ptr[1] = (uint8_t)(value >> 8); + ptr[2] = (uint8_t)(value >> 16); + ptr[3] = (uint8_t)(value >> 24); +} + +void store_leu64(uint64_t value, unsigned char *ptr) +{ + ptr[0] = (uint8_t)(value >> 0); + ptr[1] = (uint8_t)(value >> 8); + ptr[2] = (uint8_t)(value >> 16); + ptr[3] = (uint8_t)(value >> 24); + ptr[4] = (uint8_t)(value >> 32); + ptr[5] = (uint8_t)(value >> 40); + ptr[6] = (uint8_t)(value >> 48); + ptr[7] = (uint8_t)(value >> 56); +} + + +/********************** + Random utilities + **********************/ + +static inline uint64_t rotl(uint64_t x, int k) { + return (x << k) | (x >> (64 - k)); +} + +struct splitmix64_state { uint64_t x; }; + +static uint64_t splitmix64_next(struct splitmix64_state* s) { + uint64_t z = (s->x += 0x9e3779b97f4a7c15); + z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9; + z = (z ^ (z >> 27)) * 0x94d049bb133111eb; + return z ^ (z >> 31); +} + +struct xoshiro256pp_state xoshiro256pp_seed(uint64_t seed) +{ + struct xoshiro256pp_state xs; + struct splitmix64_state ss = { seed }; + + size_t i; + for (i = 0; i < 4; ++i) { + xs.s[i] = splitmix64_next(&ss); + } + + return xs; +} + +uint64_t xoshiro256pp_next(struct xoshiro256pp_state* s) { + uint64_t result = rotl(s->s[0] + s->s[3], 23) + s->s[0]; + + uint64_t t = s->s[1] << 17; + s->s[2] ^= s->s[0]; + s->s[3] ^= s->s[1]; + s->s[1] ^= s->s[2]; + s->s[0] ^= s->s[3]; + s->s[2] ^= t; + s->s[3] = rotl(s->s[3], 45); + + return result; +} + /********************** GUI utilities **********************/ @@ -337,29 +513,9 @@ void imagestring(unsigned char imagetype, char *string) Path utilities **********************/ -/* Looks for an instance of ANY of the characters in 'needles' in 'haystack', - * starting from the end of 'haystack'. Returns a pointer to the last position - * of some character on 'needles' on 'haystack'. If not found, returns NULL. - */ -static const char* strpbrk_reverse(const char* needles, const char* haystack) -{ - size_t stringlength = strlen(haystack), counter; - - for (counter = stringlength; counter > 0; --counter) - { - if (strchr(needles, haystack[counter-1])) - break; - } - - if (counter == 0) - return NULL; - - return haystack + counter - 1; -} - const char* namefrompath(const char* path) { - const char* last_separator_ptr = strpbrk_reverse(OSAL_DIR_SEPARATORS, path); + const char* last_separator_ptr = strpbrk_reverse(OSAL_DIR_SEPARATORS, (char*)path, strlen(path)); if (last_separator_ptr != NULL) return last_separator_ptr + 1; @@ -393,6 +549,23 @@ char* combinepath(const char* first, const char *second) /********************** String utilities **********************/ + +char* strpbrk_reverse(const char* needles, char* haystack, size_t haystack_len) +{ + size_t counter; + + for (counter = haystack_len; counter > 0; --counter) + { + if (strchr(needles, haystack[counter-1])) + break; + } + + if (counter == 0) + return NULL; + + return haystack + counter - 1; +} + char *trim(char *str) { char *start = str, *end = str + strlen(str); @@ -409,6 +582,28 @@ char *trim(char *str) return str; } +int string_replace_chars(char* str, const char* chars, const char r) +{ + int i, y; + int str_size, chars_size; + int replacements = 0; + + str_size = strlen(str); + chars_size = strlen(chars); + + for (i = 0; i < str_size; i++) { + for (y = 0; y < chars_size; y++) { + if (str[i] == chars[y]) { + str[i] = r; + replacements++; + break; + } + } + } + + return replacements; +} + int string_to_int(const char *str, int *result) { char *endptr; diff --git a/mupen64plus-core/src/main/util.h b/mupen64plus-core/src/main/util.h index 2ce73a79..f66e24bb 100644 --- a/mupen64plus-core/src/main/util.h +++ b/mupen64plus-core/src/main/util.h @@ -150,6 +150,41 @@ void swap_buffer(void *buffer, size_t length, size_t count); void to_little_endian_buffer(void *buffer, size_t length, size_t count); void to_big_endian_buffer(void *buffer, size_t length, size_t count); + +/* Simple serialization primitives, + * Loosely modeled after N2827 proposal. + */ +uint8_t load_beu8(const unsigned char *ptr); +uint16_t load_beu16(const unsigned char *ptr); +uint32_t load_beu32(const unsigned char *ptr); +uint64_t load_beu64(const unsigned char *ptr); + +uint8_t load_leu8(const unsigned char *ptr); +uint16_t load_leu16(const unsigned char *ptr); +uint32_t load_leu32(const unsigned char *ptr); +uint64_t load_leu64(const unsigned char *ptr); + +void store_beu8(uint8_t value, unsigned char *ptr); +void store_beu16(uint16_t value, unsigned char *ptr); +void store_beu32(uint32_t value, unsigned char *ptr); +void store_beu64(uint64_t value, unsigned char *ptr); + +void store_leu8(uint8_t value, unsigned char *ptr); +void store_leu16(uint16_t value, unsigned char *ptr); +void store_leu32(uint32_t value, unsigned char *ptr); +void store_leu64(uint64_t value, unsigned char *ptr); + + +/********************** + Random utilities + **********************/ + +struct xoshiro256pp_state { uint64_t s[4]; }; + +struct xoshiro256pp_state xoshiro256pp_seed(uint64_t seed); + +uint64_t xoshiro256pp_next(struct xoshiro256pp_state* s); + /********************** GUI utilities **********************/ @@ -173,12 +208,24 @@ char* combinepath(const char* first, const char *second); String utilities **********************/ +/* strpbrk_reverse + * Looks for an instance of ANY of the characters in 'needles' in 'haystack', + * starting from the end of 'haystack'. Returns a pointer to the last position + * of some character on 'needles' on 'haystack'. If not found, returns NULL. + */ +char* strpbrk_reverse(const char* needles, char* haystack, size_t haystack_len); + /** trim * Removes leading and trailing whitespace from str. Function modifies str * and also returns modified string. */ char *trim(char *str); + /* Replaces all occurences of any char in chars with r in string. + * returns amount of replaced chars + */ +int string_replace_chars(char *str, const char *chars, const char r); + /* Converts an string to an integer. * Returns 1 on success, 0 on failure. 'result' is undefined on failure. * diff --git a/mupen64plus-core/src/main/version.h b/mupen64plus-core/src/main/version.h index c0478667..3e27f63a 100644 --- a/mupen64plus-core/src/main/version.h +++ b/mupen64plus-core/src/main/version.h @@ -25,12 +25,12 @@ #define __VERSION_H__ #define MUPEN_CORE_NAME "Mupen64Plus Core" -#define MUPEN_CORE_VERSION 0x020509 +#define MUPEN_CORE_VERSION 0x020600 -#define FRONTEND_API_VERSION 0x020104 +#define FRONTEND_API_VERSION 0x020106 #define CONFIG_API_VERSION 0x020302 #define DEBUG_API_VERSION 0x020001 -#define VIDEXT_API_VERSION 0x030200 +#define VIDEXT_API_VERSION 0x030300 #define NETPLAY_API_VERSION 0x010001 #define VERSION_PRINTF_SPLIT(x) (((x) >> 16) & 0xffff), (((x) >> 8) & 0xff), ((x) & 0xff) diff --git a/mupen64plus-core/src/osal/files.h b/mupen64plus-core/src/osal/files.h index c2fdc158..fa1be200 100644 --- a/mupen64plus-core/src/osal/files.h +++ b/mupen64plus-core/src/osal/files.h @@ -27,13 +27,17 @@ #define OSAL_FILES_H /* some file-related preprocessor definitions */ -#if defined(WIN32) && !defined(__MINGW32__) +#if defined(WIN32) #include // For _unlink() #define unlink _unlink #define OSAL_DIR_SEPARATORS "\\/" - #define PATH_MAX _MAX_PATH + #define WIDE_OSAL_DIR_SEPARATORS L"\\/" + + #ifndef PATH_MAX + #define PATH_MAX _MAX_PATH + #endif #else /* Not WIN32 */ #include // for PATH_MAX #include // for unlink() diff --git a/mupen64plus-core/src/plugin/plugin.c b/mupen64plus-core/src/plugin/plugin.c index daa4a77c..f7d55192 100644 --- a/mupen64plus-core/src/plugin/plugin.c +++ b/mupen64plus-core/src/plugin/plugin.c @@ -217,7 +217,7 @@ m64p_error plugin_start_gfx(void) ? NULL : g_media_loader.get_dd_rom(g_media_loader.cb_data); - uint32_t rom_base = (dd_ipl_rom_filename != NULL && strlen(dd_ipl_rom_filename) != 0 && media != 'C') + uint32_t rom_base = (g_rom_size == 0 || (dd_ipl_rom_filename != NULL && strlen(dd_ipl_rom_filename) != 0 && media != 'C')) ? MM_DD_ROM : MM_CART_ROM; @@ -309,6 +309,7 @@ static m64p_error plugin_start_input(void) Controls[i].Present = 0; Controls[i].RawData = 0; Controls[i].Plugin = PLUGIN_NONE; + Controls[i].Type = CONT_TYPE_STANDARD; } /* call the input plugin */ diff --git a/mupen64plus-core/src/plugin/plugin.h b/mupen64plus-core/src/plugin/plugin.h index 21354e46..69836d28 100644 --- a/mupen64plus-core/src/plugin/plugin.h +++ b/mupen64plus-core/src/plugin/plugin.h @@ -43,7 +43,7 @@ extern CONTROL Controls[NUM_CONTROLLER]; #define RSP_API_VERSION 0x20000 #define GFX_API_VERSION 0x20200 #define AUDIO_API_VERSION 0x20000 -#define INPUT_API_VERSION 0x20100 +#define INPUT_API_VERSION 0x20101 /* video plugin function pointers */ typedef struct _gfx_plugin_functions @@ -106,6 +106,11 @@ typedef struct _input_plugin_functions ptr_SDL_KeyDown keyDown; ptr_SDL_KeyUp keyUp; ptr_RenderCallback renderCallback; + ptr_SendVRUWord sendVRUWord; + ptr_SetMicState setMicState; + ptr_ReadVRUResults readVRUResults; + ptr_ClearVRUWords clearVRUWords; + ptr_SetVRUWordMask setVRUWordMask; } input_plugin_functions; extern input_plugin_functions input; diff --git a/mupen64plus-core/subprojects/md5/md5.c b/mupen64plus-core/subprojects/md5/md5.c index 9f2de123..8fdc6007 100644 --- a/mupen64plus-core/subprojects/md5/md5.c +++ b/mupen64plus-core/subprojects/md5/md5.c @@ -27,7 +27,7 @@ This code implements the MD5 Algorithm defined in RFC 1321, whose text is available at - http://www.ietf.org/rfc/rfc1321.txt + http://www.ietf.org/rfc/rfc1321.txt The code is derived from the text of the RFC, including the test suite (section A.5) but excluding the rest of Appendix A. It does not include any code or documentation that is identified in the RFC as being @@ -38,24 +38,24 @@ that follows (in reverse chronological order): 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order - either statically or dynamically; added missing #include - in library. + either statically or dynamically; added missing #include + in library. 2002-03-11 lpd Corrected argument list for main(), and added int return - type, in test program and T value program. + type, in test program and T value program. 2002-02-21 lpd Added missing #include in test program. 2000-07-03 lpd Patched to eliminate warnings about "constant is - unsigned in ANSI C, signed in traditional"; made test program - self-checking. + unsigned in ANSI C, signed in traditional"; made test program + self-checking. 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). 1999-05-03 lpd Original version. */ #include "md5.h" - #include +#include -#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ +#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ #ifdef ARCH_IS_BIG_ENDIAN # define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) #else @@ -133,8 +133,8 @@ static void md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) { md5_word_t - a = pms->abcd[0], b = pms->abcd[1], - c = pms->abcd[2], d = pms->abcd[3]; + a = pms->abcd[0], b = pms->abcd[1], + c = pms->abcd[2], d = pms->abcd[3]; md5_word_t t; #if BYTE_ORDER > 0 /* Define storage only for big-endian CPUs. */ @@ -147,51 +147,51 @@ md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) { #if BYTE_ORDER == 0 - /* - * Determine dynamically whether this is a big-endian or - * little-endian machine, since we can use a more efficient - * algorithm on the latter. - */ - static const int w = 1; - - if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ -#endif -#if BYTE_ORDER <= 0 /* little-endian */ - { /* - * On little-endian machines, we can process properly aligned - * data without copying it. + * Determine dynamically whether this is a big-endian or + * little-endian machine, since we can use a more efficient + * algorithm on the latter. */ - if (!((data - (const md5_byte_t *)0) & 3)) { - /* data are properly aligned */ - X = (const md5_word_t *)data; - } else { - /* not aligned */ - memcpy(xbuf, data, 64); - X = xbuf; + static const int w = 1; + + if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ +#endif +#if BYTE_ORDER <= 0 /* little-endian */ + { + /* + * On little-endian machines, we can process properly aligned + * data without copying it. + */ + if (!((data - (const md5_byte_t *)0) & 3)) { + /* data are properly aligned */ + X = (const md5_word_t *)data; + } else { + /* not aligned */ + memcpy(xbuf, data, 64); + X = xbuf; + } } - } #endif #if BYTE_ORDER == 0 - else /* dynamic big-endian */ + else /* dynamic big-endian */ #endif -#if BYTE_ORDER >= 0 /* big-endian */ - { - /* - * On big-endian machines, we must arrange the bytes in the - * right order. - */ - const md5_byte_t *xp = data; - int i; +#if BYTE_ORDER >= 0 /* big-endian */ + { + /* + * On big-endian machines, we must arrange the bytes in the + * right order. + */ + const md5_byte_t *xp = data; + int i; # if BYTE_ORDER == 0 - X = xbuf; /* (dynamic only) */ + X = xbuf; /* (dynamic only) */ # else -# define xbuf X /* (static only) */ +# define xbuf X /* (static only) */ # endif - for (i = 0; i < 16; ++i, xp += 4) - xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); - } + for (i = 0; i < 16; ++i, xp += 4) + xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); + } #endif } @@ -321,63 +321,74 @@ md5_init(md5_state_t *pms) } void -md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) +md5_append(md5_state_t *pms, const md5_byte_t *data, unsigned int nbytes) { const md5_byte_t *p = data; - int left = nbytes; - int offset = (pms->count[0] >> 3) & 63; + unsigned int left = nbytes; + unsigned int offset = (pms->count[0] >> 3) & 63; md5_word_t nbits = (md5_word_t)(nbytes << 3); if (nbytes <= 0) - return; + return; + + /* this special case is handled recursively */ + if (nbytes > INT_MAX - offset) { + unsigned int overlap; + + /* handle the append in two steps to prevent overflow */ + overlap = 64 - offset; + + md5_append(pms, data, overlap); + md5_append(pms, data + overlap, nbytes - overlap); + return; + } /* Update the message length. */ pms->count[1] += nbytes >> 29; pms->count[0] += nbits; if (pms->count[0] < nbits) - pms->count[1]++; + pms->count[1]++; /* Process an initial partial block. */ if (offset) { - int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); - - memcpy(pms->buf + offset, p, copy); - if (offset + copy < 64) - return; - p += copy; - left -= copy; - md5_process(pms, pms->buf); + unsigned int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); + + memcpy(pms->buf + offset, p, copy); + if (offset + copy < 64) + return; + p += copy; + left -= copy; + md5_process(pms, pms->buf); } /* Process full blocks. */ for (; left >= 64; p += 64, left -= 64) - md5_process(pms, p); + md5_process(pms, p); /* Process a final partial block. */ if (left) - memcpy(pms->buf, p, left); + memcpy(pms->buf, p, left); } void md5_finish(md5_state_t *pms, md5_byte_t digest[16]) { static const md5_byte_t pad[64] = { - 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; md5_byte_t data[8]; int i; /* Save the length before padding. */ for (i = 0; i < 8; ++i) - data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); + data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); /* Pad to 56 bytes mod 64. */ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); /* Append the length. */ md5_append(pms, data, 8); for (i = 0; i < 16; ++i) - digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); + digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); } - diff --git a/mupen64plus-core/subprojects/md5/md5.h b/mupen64plus-core/subprojects/md5/md5.h index 5ddb0372..0ab0e684 100644 --- a/mupen64plus-core/subprojects/md5/md5.h +++ b/mupen64plus-core/subprojects/md5/md5.h @@ -21,13 +21,13 @@ ghost@aladdin.com */ -/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */ +/* $Id$ */ /* Independent implementation of MD5 (RFC 1321). This code implements the MD5 Algorithm defined in RFC 1321, whose text is available at - http://www.ietf.org/rfc/rfc1321.txt + http://www.ietf.org/rfc/rfc1321.txt The code is derived from the text of the RFC, including the test suite (section A.5) but excluding the rest of Appendix A. It does not include any code or documentation that is identified in the RFC as being @@ -38,12 +38,12 @@ that follows (in reverse chronological order): 2002-04-13 lpd Removed support for non-ANSI compilers; removed - references to Ghostscript; clarified derivation from RFC 1321; - now handles byte order either statically or dynamically. + references to Ghostscript; clarified derivation from RFC 1321; + now handles byte order either statically or dynamically. 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); - added conditionalization for C++ compilation from Martin - Purschke . + added conditionalization for C++ compilation from Martin + Purschke . 1999-05-03 lpd Original version. */ @@ -65,9 +65,9 @@ typedef unsigned int md5_word_t; /* 32-bit word */ /* Define the state of the MD5 Algorithm. */ typedef struct md5_state_s { - md5_word_t count[2]; /* message length in bits, lsw first */ - md5_word_t abcd[4]; /* digest buffer */ - md5_byte_t buf[64]; /* accumulate block */ + md5_word_t count[2]; /* message length in bits, lsw first */ + md5_word_t abcd[4]; /* digest buffer */ + md5_byte_t buf[64]; /* accumulate block */ } md5_state_t; #ifdef __cplusplus @@ -79,7 +79,7 @@ extern "C" void md5_init(md5_state_t *pms); /* Append a string to the message. */ -void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); +void md5_append(md5_state_t *pms, const md5_byte_t *data, unsigned int nbytes); /* Finish the message and return the digest. */ void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); @@ -89,4 +89,3 @@ void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); #endif #endif /* md5_INCLUDED */ - diff --git a/mupen64plus-core/subprojects/minizip/crypt.h b/mupen64plus-core/subprojects/minizip/crypt.h index a01d08d9..f4b93b78 100644 --- a/mupen64plus-core/subprojects/minizip/crypt.h +++ b/mupen64plus-core/subprojects/minizip/crypt.h @@ -32,12 +32,12 @@ /*********************************************************************** * Return the next byte in the pseudo-random sequence */ -static int decrypt_byte(unsigned long* pkeys, const unsigned long* pcrc_32_tab) -{ +static int decrypt_byte(unsigned long* pkeys, const z_crc_t* pcrc_32_tab) { unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an * unpredictable manner on 16-bit systems; not a problem * with any known compiler so far, though */ + (void)pcrc_32_tab; temp = ((unsigned)(*(pkeys+2)) & 0xffff) | 2; return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); } @@ -45,8 +45,7 @@ static int decrypt_byte(unsigned long* pkeys, const unsigned long* pcrc_32_tab) /*********************************************************************** * Update the encryption keys with the next byte of plain text */ -static int update_keys(unsigned long* pkeys,const unsigned long* pcrc_32_tab,int c) -{ +static int update_keys(unsigned long* pkeys, const z_crc_t* pcrc_32_tab, int c) { (*(pkeys+0)) = CRC32((*(pkeys+0)), c); (*(pkeys+1)) += (*(pkeys+0)) & 0xff; (*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1; @@ -62,8 +61,7 @@ static int update_keys(unsigned long* pkeys,const unsigned long* pcrc_32_tab,int * Initialize the encryption keys and the random header according to * the given password. */ -static void init_keys(const char* passwd,unsigned long* pkeys,const unsigned long* pcrc_32_tab) -{ +static void init_keys(const char* passwd, unsigned long* pkeys, const z_crc_t* pcrc_32_tab) { *(pkeys+0) = 305419896L; *(pkeys+1) = 591751049L; *(pkeys+2) = 878082192L; @@ -77,24 +75,23 @@ static void init_keys(const char* passwd,unsigned long* pkeys,const unsigned lon (update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab))) #define zencode(pkeys,pcrc_32_tab,c,t) \ - (t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), t^(c)) + (t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), (Byte)t^(c)) #ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED #define RAND_HEAD_LEN 12 /* "last resort" source for second part of crypt seed pattern */ # ifndef ZCR_SEED2 -# define ZCR_SEED2 3141592654UL /* use PI as default pattern */ +# define ZCR_SEED2 3141592654UL /* use PI as default pattern */ # endif -static int crypthead(const char* passwd, /* password string */ - unsigned char* buf, /* where to write header */ - int bufSize, - unsigned long* pkeys, - const unsigned long* pcrc_32_tab, - unsigned long crcForCrypting) -{ - int n; /* index in random header */ +static unsigned crypthead(const char* passwd, /* password string */ + unsigned char* buf, /* where to write header */ + int bufSize, + unsigned long* pkeys, + const z_crc_t* pcrc_32_tab, + unsigned long crcForCrypting) { + unsigned n; /* index in random header */ int t; /* temporary */ int c; /* random byte */ unsigned char header[RAND_HEAD_LEN-2]; /* random header */ diff --git a/mupen64plus-core/subprojects/minizip/ioapi.c b/mupen64plus-core/subprojects/minizip/ioapi.c index 7f5c191b..782d3246 100644 --- a/mupen64plus-core/subprojects/minizip/ioapi.c +++ b/mupen64plus-core/subprojects/minizip/ioapi.c @@ -14,7 +14,7 @@ #define _CRT_SECURE_NO_WARNINGS #endif -#if defined(__APPLE__) || defined(IOAPI_NO_64) +#if defined(__APPLE__) || defined(IOAPI_NO_64) || defined(__HAIKU__) || defined(MINIZIP_FOPEN_NO_64) // In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions #define FOPEN_FUNC(filename, mode) fopen(filename, mode) #define FTELLO_FUNC(stream) ftello(stream) @@ -28,8 +28,7 @@ #include "ioapi.h" -voidpf call_zopen64 (const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode) -{ +voidpf call_zopen64 (const zlib_filefunc64_32_def* pfilefunc, const void*filename, int mode) { if (pfilefunc->zfile_func64.zopen64_file != NULL) return (*(pfilefunc->zfile_func64.zopen64_file)) (pfilefunc->zfile_func64.opaque,filename,mode); else @@ -38,8 +37,7 @@ voidpf call_zopen64 (const zlib_filefunc64_32_def* pfilefunc,const void*filename } } -long call_zseek64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin) -{ +long call_zseek64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin) { if (pfilefunc->zfile_func64.zseek64_file != NULL) return (*(pfilefunc->zfile_func64.zseek64_file)) (pfilefunc->zfile_func64.opaque,filestream,offset,origin); else @@ -52,13 +50,12 @@ long call_zseek64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZP } } -ZPOS64_T call_ztell64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream) -{ +ZPOS64_T call_ztell64 (const zlib_filefunc64_32_def* pfilefunc, voidpf filestream) { if (pfilefunc->zfile_func64.zseek64_file != NULL) return (*(pfilefunc->zfile_func64.ztell64_file)) (pfilefunc->zfile_func64.opaque,filestream); else { - uLong tell_uLong = (*(pfilefunc->ztell32_file))(pfilefunc->zfile_func64.opaque,filestream); + uLong tell_uLong = (uLong)(*(pfilefunc->ztell32_file))(pfilefunc->zfile_func64.opaque,filestream); if ((tell_uLong) == MAXU32) return (ZPOS64_T)-1; else @@ -66,11 +63,9 @@ ZPOS64_T call_ztell64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream } } -void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32) -{ +void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32, const zlib_filefunc_def* p_filefunc32) { p_filefunc64_32->zfile_func64.zopen64_file = NULL; p_filefunc64_32->zopen32_file = p_filefunc32->zopen_file; - p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file; p_filefunc64_32->zfile_func64.zread_file = p_filefunc32->zread_file; p_filefunc64_32->zfile_func64.zwrite_file = p_filefunc32->zwrite_file; p_filefunc64_32->zfile_func64.ztell64_file = NULL; @@ -84,18 +79,10 @@ void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filef -static voidpf ZCALLBACK fopen_file_func OF((voidpf opaque, const char* filename, int mode)); -static uLong ZCALLBACK fread_file_func OF((voidpf opaque, voidpf stream, void* buf, uLong size)); -static uLong ZCALLBACK fwrite_file_func OF((voidpf opaque, voidpf stream, const void* buf,uLong size)); -static ZPOS64_T ZCALLBACK ftell64_file_func OF((voidpf opaque, voidpf stream)); -static long ZCALLBACK fseek64_file_func OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); -static int ZCALLBACK fclose_file_func OF((voidpf opaque, voidpf stream)); -static int ZCALLBACK ferror_file_func OF((voidpf opaque, voidpf stream)); - -static voidpf ZCALLBACK fopen_file_func (voidpf opaque, const char* filename, int mode) -{ +static voidpf ZCALLBACK fopen_file_func(voidpf opaque, const char* filename, int mode) { FILE* file = NULL; const char* mode_fopen = NULL; + (void)opaque; if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) mode_fopen = "rb"; else @@ -110,10 +97,10 @@ static voidpf ZCALLBACK fopen_file_func (voidpf opaque, const char* filename, in return file; } -static voidpf ZCALLBACK fopen64_file_func (voidpf opaque, const void* filename, int mode) -{ +static voidpf ZCALLBACK fopen64_file_func(voidpf opaque, const void* filename, int mode) { FILE* file = NULL; const char* mode_fopen = NULL; + (void)opaque; if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) mode_fopen = "rb"; else @@ -129,39 +116,39 @@ static voidpf ZCALLBACK fopen64_file_func (voidpf opaque, const void* filename, } -static uLong ZCALLBACK fread_file_func (voidpf opaque, voidpf stream, void* buf, uLong size) -{ +static uLong ZCALLBACK fread_file_func(voidpf opaque, voidpf stream, void* buf, uLong size) { uLong ret; + (void)opaque; ret = (uLong)fread(buf, 1, (size_t)size, (FILE *)stream); return ret; } -static uLong ZCALLBACK fwrite_file_func (voidpf opaque, voidpf stream, const void* buf, uLong size) -{ +static uLong ZCALLBACK fwrite_file_func(voidpf opaque, voidpf stream, const void* buf, uLong size) { uLong ret; + (void)opaque; ret = (uLong)fwrite(buf, 1, (size_t)size, (FILE *)stream); return ret; } -static long ZCALLBACK ftell_file_func (voidpf opaque, voidpf stream) -{ +static long ZCALLBACK ftell_file_func(voidpf opaque, voidpf stream) { long ret; + (void)opaque; ret = ftell((FILE *)stream); return ret; } -static ZPOS64_T ZCALLBACK ftell64_file_func (voidpf opaque, voidpf stream) -{ +static ZPOS64_T ZCALLBACK ftell64_file_func(voidpf opaque, voidpf stream) { ZPOS64_T ret; - ret = FTELLO_FUNC((FILE *)stream); + (void)opaque; + ret = (ZPOS64_T)FTELLO_FUNC((FILE *)stream); return ret; } -static long ZCALLBACK fseek_file_func (voidpf opaque, voidpf stream, uLong offset, int origin) -{ +static long ZCALLBACK fseek_file_func(voidpf opaque, voidpf stream, uLong offset, int origin) { int fseek_origin=0; long ret; + (void)opaque; switch (origin) { case ZLIB_FILEFUNC_SEEK_CUR : @@ -176,15 +163,15 @@ static long ZCALLBACK fseek_file_func (voidpf opaque, voidpf stream, uLong offs default: return -1; } ret = 0; - if (fseek((FILE *)stream, offset, fseek_origin) != 0) + if (fseek((FILE *)stream, (long)offset, fseek_origin) != 0) ret = -1; return ret; } -static long ZCALLBACK fseek64_file_func (voidpf opaque, voidpf stream, ZPOS64_T offset, int origin) -{ +static long ZCALLBACK fseek64_file_func(voidpf opaque, voidpf stream, ZPOS64_T offset, int origin) { int fseek_origin=0; long ret; + (void)opaque; switch (origin) { case ZLIB_FILEFUNC_SEEK_CUR : @@ -200,30 +187,28 @@ static long ZCALLBACK fseek64_file_func (voidpf opaque, voidpf stream, ZPOS64_T } ret = 0; - if(FSEEKO_FUNC((FILE *)stream, offset, fseek_origin) != 0) + if(FSEEKO_FUNC((FILE *)stream, (z_off64_t)offset, fseek_origin) != 0) ret = -1; return ret; } -static int ZCALLBACK fclose_file_func (voidpf opaque, voidpf stream) -{ +static int ZCALLBACK fclose_file_func(voidpf opaque, voidpf stream) { int ret; + (void)opaque; ret = fclose((FILE *)stream); return ret; } -static int ZCALLBACK ferror_file_func (voidpf opaque, voidpf stream) -{ +static int ZCALLBACK ferror_file_func(voidpf opaque, voidpf stream) { int ret; + (void)opaque; ret = ferror((FILE *)stream); return ret; } -void fill_fopen_filefunc (pzlib_filefunc_def) - zlib_filefunc_def* pzlib_filefunc_def; -{ +void fill_fopen_filefunc(zlib_filefunc_def* pzlib_filefunc_def) { pzlib_filefunc_def->zopen_file = fopen_file_func; pzlib_filefunc_def->zread_file = fread_file_func; pzlib_filefunc_def->zwrite_file = fwrite_file_func; @@ -234,8 +219,7 @@ void fill_fopen_filefunc (pzlib_filefunc_def) pzlib_filefunc_def->opaque = NULL; } -void fill_fopen64_filefunc (zlib_filefunc64_def* pzlib_filefunc_def) -{ +void fill_fopen64_filefunc(zlib_filefunc64_def* pzlib_filefunc_def) { pzlib_filefunc_def->zopen64_file = fopen64_file_func; pzlib_filefunc_def->zread_file = fread_file_func; pzlib_filefunc_def->zwrite_file = fwrite_file_func; diff --git a/mupen64plus-core/subprojects/minizip/ioapi.h b/mupen64plus-core/subprojects/minizip/ioapi.h index bac13255..a2d2e6e6 100644 --- a/mupen64plus-core/subprojects/minizip/ioapi.h +++ b/mupen64plus-core/subprojects/minizip/ioapi.h @@ -43,19 +43,14 @@ #include #include - #include "zlib.h" -#ifndef OF -#define OF _Z_OF -#endif - #if defined(USE_FILE32API) #define fopen64 fopen #define ftello64 ftell #define fseeko64 fseek #else -#ifdef __FreeBSD__ +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__HAIKU__) || defined(MINIZIP_FOPEN_NO_64) #define fopen64 fopen #define ftello64 ftello #define fseeko64 fseeko @@ -87,18 +82,16 @@ #include "mz64conf.h" #endif -/* a type choosen by DEFINE */ +/* a type chosen by DEFINE */ #ifdef HAVE_64BIT_INT_CUSTOM typedef 64BIT_INT_CUSTOM_TYPE ZPOS64_T; #else #ifdef HAS_STDINT_H #include "stdint.h" - typedef uint64_t ZPOS64_T; #else -/* Maximum unsigned 32-bit value used as placeholder for zip64 */ -#define MAXU32 0xffffffff + #if defined(_MSC_VER) || defined(__BORLANDC__) typedef unsigned __int64 ZPOS64_T; @@ -108,7 +101,10 @@ typedef unsigned long long int ZPOS64_T; #endif #endif - +/* Maximum unsigned 32-bit value used as placeholder for zip64 */ +#ifndef MAXU32 +#define MAXU32 (0xffffffff) +#endif #ifdef __cplusplus extern "C" { @@ -138,17 +134,17 @@ extern "C" { -typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, const char* filename, int mode)); -typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size)); -typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); -typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream)); -typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream)); +typedef voidpf (ZCALLBACK *open_file_func) (voidpf opaque, const char* filename, int mode); +typedef uLong (ZCALLBACK *read_file_func) (voidpf opaque, voidpf stream, void* buf, uLong size); +typedef uLong (ZCALLBACK *write_file_func) (voidpf opaque, voidpf stream, const void* buf, uLong size); +typedef int (ZCALLBACK *close_file_func) (voidpf opaque, voidpf stream); +typedef int (ZCALLBACK *testerror_file_func) (voidpf opaque, voidpf stream); -typedef long (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream)); -typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin)); +typedef long (ZCALLBACK *tell_file_func) (voidpf opaque, voidpf stream); +typedef long (ZCALLBACK *seek_file_func) (voidpf opaque, voidpf stream, uLong offset, int origin); -/* here is the "old" 32 bits structure structure */ +/* here is the "old" 32 bits structure */ typedef struct zlib_filefunc_def_s { open_file_func zopen_file; @@ -161,9 +157,9 @@ typedef struct zlib_filefunc_def_s voidpf opaque; } zlib_filefunc_def; -typedef ZPOS64_T (ZCALLBACK *tell64_file_func) OF((voidpf opaque, voidpf stream)); -typedef long (ZCALLBACK *seek64_file_func) OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); -typedef voidpf (ZCALLBACK *open64_file_func) OF((voidpf opaque, const void* filename, int mode)); +typedef ZPOS64_T (ZCALLBACK *tell64_file_func) (voidpf opaque, voidpf stream); +typedef long (ZCALLBACK *seek64_file_func) (voidpf opaque, voidpf stream, ZPOS64_T offset, int origin); +typedef voidpf (ZCALLBACK *open64_file_func) (voidpf opaque, const void* filename, int mode); typedef struct zlib_filefunc64_def_s { @@ -177,8 +173,8 @@ typedef struct zlib_filefunc64_def_s voidpf opaque; } zlib_filefunc64_def; -void fill_fopen64_filefunc OF((zlib_filefunc64_def* pzlib_filefunc_def)); -void fill_fopen_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); +void fill_fopen64_filefunc(zlib_filefunc64_def* pzlib_filefunc_def); +void fill_fopen_filefunc(zlib_filefunc_def* pzlib_filefunc_def); /* now internal definition, only for zip.c and unzip.h */ typedef struct zlib_filefunc64_32_def_s @@ -197,11 +193,11 @@ typedef struct zlib_filefunc64_32_def_s #define ZCLOSE64(filefunc,filestream) ((*((filefunc).zfile_func64.zclose_file)) ((filefunc).zfile_func64.opaque,filestream)) #define ZERROR64(filefunc,filestream) ((*((filefunc).zfile_func64.zerror_file)) ((filefunc).zfile_func64.opaque,filestream)) -voidpf call_zopen64 OF((const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode)); -long call_zseek64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin)); -ZPOS64_T call_ztell64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream)); +voidpf call_zopen64(const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode); +long call_zseek64(const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin); +ZPOS64_T call_ztell64(const zlib_filefunc64_32_def* pfilefunc,voidpf filestream); -void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32); +void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32); #define ZOPEN64(filefunc,filename,mode) (call_zopen64((&(filefunc)),(filename),(mode))) #define ZTELL64(filefunc,filestream) (call_ztell64((&(filefunc)),(filestream))) diff --git a/mupen64plus-core/subprojects/minizip/unzip.c b/mupen64plus-core/subprojects/minizip/unzip.c index 8b9086c6..ea05b7d6 100644 --- a/mupen64plus-core/subprojects/minizip/unzip.c +++ b/mupen64plus-core/subprojects/minizip/unzip.c @@ -49,12 +49,12 @@ Copyright (C) 2007-2008 Even Rouault - Oct-2009 - Mathias Svensson - Removed cpl_* from symbol names (Even Rouault added them but since this is now moved to a new project (minizip64) I renamed them again). + Oct-2009 - Mathias Svensson - Removed cpl_* from symbol names (Even Rouault added them but since this is now moved to a new project (minizip64) I renamed them again). Oct-2009 - Mathias Svensson - Fixed problem if uncompressed size was > 4G and compressed size was <4G should only read the compressed/uncompressed size from the Zip64 format if the size from normal header was 0xFFFFFFFF - Oct-2009 - Mathias Svensson - Applied some bug fixes from paches recived from Gilles Vollant - Oct-2009 - Mathias Svensson - Applied support to unzip files with compression mathod BZIP2 (bzip2 lib is required) + Oct-2009 - Mathias Svensson - Applied some bug fixes from patches received from Gilles Vollant + Oct-2009 - Mathias Svensson - Applied support to unzip files with compression method BZIP2 (bzip2 lib is required) Patch created by Daniel Borca Jan-2010 - back to unzip and minizip 1.0 name scheme, with compatibility layer @@ -72,13 +72,11 @@ #define NOUNCRYPT #endif -#include "unzip.h" #include "zlib.h" +#include "unzip.h" #ifdef STDC # include -# include -# include #endif #ifdef NO_ERRNO_H extern int errno; @@ -111,9 +109,6 @@ #ifndef ALLOC # define ALLOC(size) (malloc(size)) #endif -#ifndef TRYFREE -# define TRYFREE(p) {if (p) free(p);} -#endif #define SIZECENTRALDIRITEM (0x2e) #define SIZEZIPLOCALHEADER (0x1e) @@ -122,7 +117,7 @@ const char unz_copyright[] = " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; -/* unz_file_info_interntal contain internal info about a file in zipfile*/ +/* unz_file_info64_internal contain internal info about a file in zipfile*/ typedef struct unz_file_info64_internal_s { ZPOS64_T offset_curfile;/* relative offset of local header 8 bytes */ @@ -153,7 +148,7 @@ typedef struct ZPOS64_T rest_read_compressed; /* number of byte to be decompressed */ ZPOS64_T rest_read_uncompressed;/*number of byte to be obtained after decomp*/ zlib_filefunc64_32_def z_filefunc; - voidpf filestream; /* io structore of the zipfile */ + voidpf filestream; /* io structure of the zipfile */ uLong compression_method; /* compression method (0==store) */ ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ int raw; @@ -166,7 +161,7 @@ typedef struct { zlib_filefunc64_32_def z_filefunc; int is64bitOpenFunction; - voidpf filestream; /* io structore of the zipfile */ + voidpf filestream; /* io structure of the zipfile */ unz_global_info64 gi; /* public global information */ ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ ZPOS64_T num_file; /* number of the current file in the zipfile*/ @@ -188,7 +183,7 @@ typedef struct # ifndef NOUNCRYPT unsigned long keys[3]; /* keys defining the pseudo-random sequence */ - const unsigned long* pcrc_32_tab; + const z_crc_t* pcrc_32_tab; # endif } unz64_s; @@ -197,29 +192,24 @@ typedef struct #include "crypt.h" #endif + /* =========================================================================== - Read a byte from a gz_stream; update next_in and avail_in. Return EOF - for end of file. - IN assertion: the stream s has been sucessfully opened for reading. + Reads a long in LSB order from the given gz_stream. Sets */ - -local int unz64local_getByte OF(( - const zlib_filefunc64_32_def* pzlib_filefunc_def, - voidpf filestream, - int *pi)); - -local int unz64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int *pi) -{ - unsigned char c; - int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,&c,1); - if (err==1) +local int unz64local_getShort(const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX) { + unsigned char c[2]; + int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,c,2); + if (err==2) { - *pi = (int)c; + *pX = c[0] | ((uLong)c[1] << 8); return UNZ_OK; } else { + *pX = 0; if (ZERROR64(*pzlib_filefunc_def,filestream)) return UNZ_ERRNO; else @@ -227,127 +217,50 @@ local int unz64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def, v } } - -/* =========================================================================== - Reads a long in LSB order from the given gz_stream. Sets -*/ -local int unz64local_getShort OF(( - const zlib_filefunc64_32_def* pzlib_filefunc_def, - voidpf filestream, - uLong *pX)); - -local int unz64local_getShort (const zlib_filefunc64_32_def* pzlib_filefunc_def, +local int unz64local_getLong(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, - uLong *pX) -{ - uLong x ; - int i = 0; - int err; - - err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); - x = (uLong)i; - - if (err==UNZ_OK) - err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); - x |= ((uLong)i)<<8; - - if (err==UNZ_OK) - *pX = x; - else - *pX = 0; - return err; -} - -local int unz64local_getLong OF(( - const zlib_filefunc64_32_def* pzlib_filefunc_def, - voidpf filestream, - uLong *pX)); - -local int unz64local_getLong (const zlib_filefunc64_32_def* pzlib_filefunc_def, - voidpf filestream, - uLong *pX) -{ - uLong x ; - int i = 0; - int err; - - err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); - x = (uLong)i; - - if (err==UNZ_OK) - err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); - x |= ((uLong)i)<<8; - - if (err==UNZ_OK) - err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); - x |= ((uLong)i)<<16; - - if (err==UNZ_OK) - err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); - x += ((uLong)i)<<24; - - if (err==UNZ_OK) - *pX = x; + uLong *pX) { + unsigned char c[4]; + int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,c,4); + if (err==4) + { + *pX = c[0] | ((uLong)c[1] << 8) | ((uLong)c[2] << 16) | ((uLong)c[3] << 24); + return UNZ_OK; + } else + { *pX = 0; - return err; + if (ZERROR64(*pzlib_filefunc_def,filestream)) + return UNZ_ERRNO; + else + return UNZ_EOF; + } } -local int unz64local_getLong64 OF(( - const zlib_filefunc64_32_def* pzlib_filefunc_def, - voidpf filestream, - ZPOS64_T *pX)); - - -local int unz64local_getLong64 (const zlib_filefunc64_32_def* pzlib_filefunc_def, - voidpf filestream, - ZPOS64_T *pX) -{ - ZPOS64_T x ; - int i = 0; - int err; - - err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); - x = (ZPOS64_T)i; - if (err==UNZ_OK) - err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); - x |= ((ZPOS64_T)i)<<8; - - if (err==UNZ_OK) - err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); - x |= ((ZPOS64_T)i)<<16; - - if (err==UNZ_OK) - err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); - x |= ((ZPOS64_T)i)<<24; - - if (err==UNZ_OK) - err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); - x |= ((ZPOS64_T)i)<<32; - - if (err==UNZ_OK) - err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); - x |= ((ZPOS64_T)i)<<40; - - if (err==UNZ_OK) - err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); - x |= ((ZPOS64_T)i)<<48; - - if (err==UNZ_OK) - err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); - x |= ((ZPOS64_T)i)<<56; - - if (err==UNZ_OK) - *pX = x; +local int unz64local_getLong64(const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + ZPOS64_T *pX) { + unsigned char c[8]; + int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,c,8); + if (err==8) + { + *pX = c[0] | ((ZPOS64_T)c[1] << 8) | ((ZPOS64_T)c[2] << 16) | ((ZPOS64_T)c[3] << 24) + | ((ZPOS64_T)c[4] << 32) | ((ZPOS64_T)c[5] << 40) | ((ZPOS64_T)c[6] << 48) | ((ZPOS64_T)c[7] << 56); + return UNZ_OK; + } else + { *pX = 0; - return err; + if (ZERROR64(*pzlib_filefunc_def,filestream)) + return UNZ_ERRNO; + else + return UNZ_EOF; + } } /* My own strcmpi / strcasecmp */ -local int strcmpcasenosensitive_internal (const char* fileName1, const char* fileName2) -{ +local int strcmpcasenosensitive_internal(const char* fileName1, const char* fileName2) { for (;;) { char c1=*(fileName1++); @@ -379,19 +292,17 @@ local int strcmpcasenosensitive_internal (const char* fileName1, const char* fil #endif /* - Compare two filename (fileName1,fileName2). - If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) - If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + Compare two filenames (fileName1,fileName2). + If iCaseSensitivity = 1, comparison is case sensitive (like strcmp) + If iCaseSensitivity = 2, comparison is not case sensitive (like strcmpi or strcasecmp) - If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + If iCaseSensitivity = 0, case sensitivity is default of your operating system (like 1 on Unix, 2 on Windows) */ extern int ZEXPORT unzStringFileNameCompare (const char* fileName1, - const char* fileName2, - int iCaseSensitivity) - -{ + const char* fileName2, + int iCaseSensitivity) { if (iCaseSensitivity==0) iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; @@ -405,21 +316,23 @@ extern int ZEXPORT unzStringFileNameCompare (const char* fileName1, #define BUFREADCOMMENT (0x400) #endif +#ifndef CENTRALDIRINVALID +#define CENTRALDIRINVALID ((ZPOS64_T)(-1)) +#endif + /* Locate the Central directory of a zipfile (at the end, just before the global comment) */ -local ZPOS64_T unz64local_SearchCentralDir OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); -local ZPOS64_T unz64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) -{ +local ZPOS64_T unz64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) { unsigned char* buf; ZPOS64_T uSizeFile; ZPOS64_T uBackRead; ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ - ZPOS64_T uPosFound=0; + ZPOS64_T uPosFound=CENTRALDIRINVALID; if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) - return 0; + return CENTRALDIRINVALID; uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); @@ -429,7 +342,7 @@ local ZPOS64_T unz64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_f buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); if (buf==NULL) - return 0; + return CENTRALDIRINVALID; uBackRead = 4; while (uBackReadz_filefunc, s->filestream); - TRYFREE(s); + free(s); return UNZ_OK; } @@ -825,8 +727,7 @@ extern int ZEXPORT unzClose (unzFile file) Write info about the ZipFile in the *pglobal_info structure. No preparation of the structure is needed return UNZ_OK if there is no problem. */ -extern int ZEXPORT unzGetGlobalInfo64 (unzFile file, unz_global_info64* pglobal_info) -{ +extern int ZEXPORT unzGetGlobalInfo64(unzFile file, unz_global_info64* pglobal_info) { unz64_s* s; if (file==NULL) return UNZ_PARAMERROR; @@ -835,8 +736,7 @@ extern int ZEXPORT unzGetGlobalInfo64 (unzFile file, unz_global_info64* pglobal_ return UNZ_OK; } -extern int ZEXPORT unzGetGlobalInfo (unzFile file, unz_global_info* pglobal_info32) -{ +extern int ZEXPORT unzGetGlobalInfo(unzFile file, unz_global_info* pglobal_info32) { unz64_s* s; if (file==NULL) return UNZ_PARAMERROR; @@ -847,46 +747,33 @@ extern int ZEXPORT unzGetGlobalInfo (unzFile file, unz_global_info* pglobal_info return UNZ_OK; } /* - Translate date/time from Dos format to tm_unz (readable more easilty) + Translate date/time from Dos format to tm_unz (readable more easily) */ -local void unz64local_DosDateToTmuDate (ZPOS64_T ulDosDate, tm_unz* ptm) -{ +local void unz64local_DosDateToTmuDate(ZPOS64_T ulDosDate, tm_unz* ptm) { ZPOS64_T uDate; uDate = (ZPOS64_T)(ulDosDate>>16); - ptm->tm_mday = (uInt)(uDate&0x1f) ; - ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; - ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + ptm->tm_mday = (int)(uDate&0x1f) ; + ptm->tm_mon = (int)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (int)(((uDate&0x0FE00)/0x0200)+1980) ; - ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); - ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; - ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; + ptm->tm_hour = (int) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (int) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (int) (2*(ulDosDate&0x1f)) ; } /* Get Info about the current file in the zipfile, with internal only info */ -local int unz64local_GetCurrentFileInfoInternal OF((unzFile file, - unz_file_info64 *pfile_info, - unz_file_info64_internal - *pfile_info_internal, - char *szFileName, - uLong fileNameBufferSize, - void *extraField, - uLong extraFieldBufferSize, - char *szComment, - uLong commentBufferSize)); - -local int unz64local_GetCurrentFileInfoInternal (unzFile file, - unz_file_info64 *pfile_info, - unz_file_info64_internal - *pfile_info_internal, - char *szFileName, - uLong fileNameBufferSize, - void *extraField, - uLong extraFieldBufferSize, - char *szComment, - uLong commentBufferSize) -{ +local int unz64local_GetCurrentFileInfoInternal(unzFile file, + unz_file_info64 *pfile_info, + unz_file_info64_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize) { unz64_s* s; unz_file_info64 file_info; unz_file_info64_internal file_info_internal; @@ -993,7 +880,7 @@ local int unz64local_GetCurrentFileInfoInternal (unzFile file, if (lSeek!=0) { - if (ZSEEK64(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + if (ZSEEK64(s->z_filefunc, s->filestream,(ZPOS64_T)lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) lSeek=0; else err=UNZ_ERRNO; @@ -1018,7 +905,7 @@ local int unz64local_GetCurrentFileInfoInternal (unzFile file, if (lSeek!=0) { - if (ZSEEK64(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + if (ZSEEK64(s->z_filefunc, s->filestream,(ZPOS64_T)lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) lSeek=0; else err=UNZ_ERRNO; @@ -1038,33 +925,31 @@ local int unz64local_GetCurrentFileInfoInternal (unzFile file, /* ZIP64 extra fields */ if (headerId == 0x0001) { - uLong uL; - - if(file_info.uncompressed_size == MAXU32) - { - if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.uncompressed_size) != UNZ_OK) - err=UNZ_ERRNO; - } - - if(file_info.compressed_size == MAXU32) - { - if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.compressed_size) != UNZ_OK) - err=UNZ_ERRNO; - } - - if(file_info_internal.offset_curfile == MAXU32) - { - /* Relative Header offset */ - if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info_internal.offset_curfile) != UNZ_OK) - err=UNZ_ERRNO; - } - - if(file_info.disk_num_start == MAXU32) - { - /* Disk Start Number */ - if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) - err=UNZ_ERRNO; - } + if(file_info.uncompressed_size == MAXU32) + { + if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.uncompressed_size) != UNZ_OK) + err=UNZ_ERRNO; + } + + if(file_info.compressed_size == MAXU32) + { + if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.compressed_size) != UNZ_OK) + err=UNZ_ERRNO; + } + + if(file_info_internal.offset_curfile == MAXU32) + { + /* Relative Header offset */ + if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info_internal.offset_curfile) != UNZ_OK) + err=UNZ_ERRNO; + } + + if(file_info.disk_num_start == 0xffff) + { + /* Disk Start Number */ + if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.disk_num_start) != UNZ_OK) + err=UNZ_ERRNO; + } } else @@ -1090,7 +975,7 @@ local int unz64local_GetCurrentFileInfoInternal (unzFile file, if (lSeek!=0) { - if (ZSEEK64(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + if (ZSEEK64(s->z_filefunc, s->filestream,(ZPOS64_T)lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) lSeek=0; else err=UNZ_ERRNO; @@ -1121,24 +1006,22 @@ local int unz64local_GetCurrentFileInfoInternal (unzFile file, No preparation of the structure is needed return UNZ_OK if there is no problem. */ -extern int ZEXPORT unzGetCurrentFileInfo64 (unzFile file, - unz_file_info64 * pfile_info, - char * szFileName, uLong fileNameBufferSize, - void *extraField, uLong extraFieldBufferSize, - char* szComment, uLong commentBufferSize) -{ +extern int ZEXPORT unzGetCurrentFileInfo64(unzFile file, + unz_file_info64 * pfile_info, + char * szFileName, uLong fileNameBufferSize, + void *extraField, uLong extraFieldBufferSize, + char* szComment, uLong commentBufferSize) { return unz64local_GetCurrentFileInfoInternal(file,pfile_info,NULL, - szFileName,fileNameBufferSize, - extraField,extraFieldBufferSize, - szComment,commentBufferSize); + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); } -extern int ZEXPORT unzGetCurrentFileInfo (unzFile file, - unz_file_info * pfile_info, - char * szFileName, uLong fileNameBufferSize, - void *extraField, uLong extraFieldBufferSize, - char* szComment, uLong commentBufferSize) -{ +extern int ZEXPORT unzGetCurrentFileInfo(unzFile file, + unz_file_info * pfile_info, + char * szFileName, uLong fileNameBufferSize, + void *extraField, uLong extraFieldBufferSize, + char* szComment, uLong commentBufferSize) { int err; unz_file_info64 file_info64; err = unz64local_GetCurrentFileInfoInternal(file,&file_info64,NULL, @@ -1162,7 +1045,7 @@ extern int ZEXPORT unzGetCurrentFileInfo (unzFile file, pfile_info->internal_fa = file_info64.internal_fa; pfile_info->external_fa = file_info64.external_fa; - pfile_info->tmu_date = file_info64.tmu_date, + pfile_info->tmu_date = file_info64.tmu_date; pfile_info->compressed_size = (uLong)file_info64.compressed_size; @@ -1175,8 +1058,7 @@ extern int ZEXPORT unzGetCurrentFileInfo (unzFile file, Set the current file of the zipfile to the first file. return UNZ_OK if there is no problem */ -extern int ZEXPORT unzGoToFirstFile (unzFile file) -{ +extern int ZEXPORT unzGoToFirstFile(unzFile file) { int err=UNZ_OK; unz64_s* s; if (file==NULL) @@ -1196,8 +1078,7 @@ extern int ZEXPORT unzGoToFirstFile (unzFile file) return UNZ_OK if there is no problem return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. */ -extern int ZEXPORT unzGoToNextFile (unzFile file) -{ +extern int ZEXPORT unzGoToNextFile(unzFile file) { unz64_s* s; int err; @@ -1223,14 +1104,13 @@ extern int ZEXPORT unzGoToNextFile (unzFile file) /* Try locate the file szFileName in the zipfile. - For the iCaseSensitivity signification, see unzipStringFileNameCompare + For the iCaseSensitivity signification, see unzStringFileNameCompare return value : UNZ_OK if the file is found. It becomes the current file. UNZ_END_OF_LIST_OF_FILE if the file is not found */ -extern int ZEXPORT unzLocateFile (unzFile file, const char *szFileName, int iCaseSensitivity) -{ +extern int ZEXPORT unzLocateFile(unzFile file, const char *szFileName, int iCaseSensitivity) { unz64_s* s; int err; @@ -1305,8 +1185,7 @@ typedef struct unz_file_pos_s } unz_file_pos; */ -extern int ZEXPORT unzGetFilePos64(unzFile file, unz64_file_pos* file_pos) -{ +extern int ZEXPORT unzGetFilePos64(unzFile file, unz64_file_pos* file_pos) { unz64_s* s; if (file==NULL || file_pos==NULL) @@ -1321,10 +1200,7 @@ extern int ZEXPORT unzGetFilePos64(unzFile file, unz64_file_pos* file_pos) return UNZ_OK; } -extern int ZEXPORT unzGetFilePos( - unzFile file, - unz_file_pos* file_pos) -{ +extern int ZEXPORT unzGetFilePos(unzFile file, unz_file_pos* file_pos) { unz64_file_pos file_pos64; int err = unzGetFilePos64(file,&file_pos64); if (err==UNZ_OK) @@ -1335,8 +1211,7 @@ extern int ZEXPORT unzGetFilePos( return err; } -extern int ZEXPORT unzGoToFilePos64(unzFile file, const unz64_file_pos* file_pos) -{ +extern int ZEXPORT unzGoToFilePos64(unzFile file, const unz64_file_pos* file_pos) { unz64_s* s; int err; @@ -1357,10 +1232,7 @@ extern int ZEXPORT unzGoToFilePos64(unzFile file, const unz64_file_pos* file_pos return err; } -extern int ZEXPORT unzGoToFilePos( - unzFile file, - unz_file_pos* file_pos) -{ +extern int ZEXPORT unzGoToFilePos(unzFile file, unz_file_pos* file_pos) { unz64_file_pos file_pos64; if (file_pos == NULL) return UNZ_PARAMERROR; @@ -1382,10 +1254,9 @@ extern int ZEXPORT unzGoToFilePos( store in *piSizeVar the size of extra info in local header (filename and size of extra field data) */ -local int unz64local_CheckCurrentFileCoherencyHeader (unz64_s* s, uInt* piSizeVar, - ZPOS64_T * poffset_local_extrafield, - uInt * psize_local_extrafield) -{ +local int unz64local_CheckCurrentFileCoherencyHeader(unz64_s* s, uInt* piSizeVar, + ZPOS64_T * poffset_local_extrafield, + uInt * psize_local_extrafield) { uLong uMagic,uData,uFlags; uLong size_filename; uLong size_extra_field; @@ -1469,9 +1340,8 @@ local int unz64local_CheckCurrentFileCoherencyHeader (unz64_s* s, uInt* piSizeVa Open for reading data the current file in the zipfile. If there is no error and the file is opened, the return value is UNZ_OK. */ -extern int ZEXPORT unzOpenCurrentFile3 (unzFile file, int* method, - int* level, int raw, const char* password) -{ +extern int ZEXPORT unzOpenCurrentFile3(unzFile file, int* method, + int* level, int raw, const char* password) { int err=UNZ_OK; uInt iSizeVar; unz64_s* s; @@ -1509,7 +1379,7 @@ extern int ZEXPORT unzOpenCurrentFile3 (unzFile file, int* method, if (pfile_in_zip_read_info->read_buffer==NULL) { - TRYFREE(pfile_in_zip_read_info); + free(pfile_in_zip_read_info); return UNZ_INTERNALERROR; } @@ -1566,7 +1436,8 @@ extern int ZEXPORT unzOpenCurrentFile3 (unzFile file, int* method, pfile_in_zip_read_info->stream_initialised=Z_BZIP2ED; else { - TRYFREE(pfile_in_zip_read_info); + free(pfile_in_zip_read_info->read_buffer); + free(pfile_in_zip_read_info); return err; } #else @@ -1586,7 +1457,8 @@ extern int ZEXPORT unzOpenCurrentFile3 (unzFile file, int* method, pfile_in_zip_read_info->stream_initialised=Z_DEFLATED; else { - TRYFREE(pfile_in_zip_read_info); + free(pfile_in_zip_read_info->read_buffer); + free(pfile_in_zip_read_info); return err; } /* windowBits is passed < 0 to tell that there is no zlib header. @@ -1638,25 +1510,21 @@ extern int ZEXPORT unzOpenCurrentFile3 (unzFile file, int* method, return UNZ_OK; } -extern int ZEXPORT unzOpenCurrentFile (unzFile file) -{ +extern int ZEXPORT unzOpenCurrentFile(unzFile file) { return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL); } -extern int ZEXPORT unzOpenCurrentFilePassword (unzFile file, const char* password) -{ +extern int ZEXPORT unzOpenCurrentFilePassword(unzFile file, const char* password) { return unzOpenCurrentFile3(file, NULL, NULL, 0, password); } -extern int ZEXPORT unzOpenCurrentFile2 (unzFile file, int* method, int* level, int raw) -{ +extern int ZEXPORT unzOpenCurrentFile2(unzFile file, int* method, int* level, int raw) { return unzOpenCurrentFile3(file, method, level, raw, NULL); } /** Addition for GDAL : START */ -extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64( unzFile file) -{ +extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64(unzFile file) { unz64_s* s; file_in_zip64_read_info_s* pfile_in_zip_read_info; s=(unz64_s*)file; @@ -1676,13 +1544,12 @@ extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64( unzFile file) buf contain buffer where data must be copied len the size of buf. - return the number of byte copied if somes bytes are copied + return the number of byte copied if some bytes are copied return 0 if the end of file was reached return <0 with error code if there is an error (UNZ_ERRNO for IO error, or zLib error for uncompress error) */ -extern int ZEXPORT unzReadCurrentFile (unzFile file, voidp buf, unsigned len) -{ +extern int ZEXPORT unzReadCurrentFile(unzFile file, voidp buf, unsigned len) { int err=UNZ_OK; uInt iRead = 0; unz64_s* s; @@ -1767,7 +1634,7 @@ extern int ZEXPORT unzReadCurrentFile (unzFile file, voidp buf, unsigned len) if ((pfile_in_zip_read_info->stream.avail_in == 0) && (pfile_in_zip_read_info->rest_read_compressed == 0)) - return (iRead==0) ? UNZ_EOF : iRead; + return (iRead==0) ? UNZ_EOF : (int)iRead; if (pfile_in_zip_read_info->stream.avail_out < pfile_in_zip_read_info->stream.avail_in) @@ -1857,6 +1724,9 @@ extern int ZEXPORT unzReadCurrentFile (unzFile file, voidp buf, unsigned len) err = Z_DATA_ERROR; uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + /* Detect overflow, because z_stream.total_out is uLong (32 bits) */ + if (uTotalOutAftertotal_out_64 = pfile_in_zip_read_info->total_out_64 + uOutThis; @@ -1871,14 +1741,14 @@ extern int ZEXPORT unzReadCurrentFile (unzFile file, voidp buf, unsigned len) iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); if (err==Z_STREAM_END) - return (iRead==0) ? UNZ_EOF : iRead; + return (iRead==0) ? UNZ_EOF : (int)iRead; if (err!=Z_OK) break; } } if (err==Z_OK) - return iRead; + return (int)iRead; return err; } @@ -1886,8 +1756,7 @@ extern int ZEXPORT unzReadCurrentFile (unzFile file, voidp buf, unsigned len) /* Give the current position in uncompressed data */ -extern z_off_t ZEXPORT unztell (unzFile file) -{ +extern z_off_t ZEXPORT unztell(unzFile file) { unz64_s* s; file_in_zip64_read_info_s* pfile_in_zip_read_info; if (file==NULL) @@ -1901,8 +1770,7 @@ extern z_off_t ZEXPORT unztell (unzFile file) return (z_off_t)pfile_in_zip_read_info->stream.total_out; } -extern ZPOS64_T ZEXPORT unztell64 (unzFile file) -{ +extern ZPOS64_T ZEXPORT unztell64(unzFile file) { unz64_s* s; file_in_zip64_read_info_s* pfile_in_zip_read_info; @@ -1921,8 +1789,7 @@ extern ZPOS64_T ZEXPORT unztell64 (unzFile file) /* return 1 if the end of file was reached, 0 elsewhere */ -extern int ZEXPORT unzeof (unzFile file) -{ +extern int ZEXPORT unzeof(unzFile file) { unz64_s* s; file_in_zip64_read_info_s* pfile_in_zip_read_info; if (file==NULL) @@ -1953,8 +1820,7 @@ more info in the local-header version than in the central-header) the return value is the number of bytes copied in buf, or (if <0) the error code */ -extern int ZEXPORT unzGetLocalExtrafield (unzFile file, voidp buf, unsigned len) -{ +extern int ZEXPORT unzGetLocalExtrafield(unzFile file, voidp buf, unsigned len) { unz64_s* s; file_in_zip64_read_info_s* pfile_in_zip_read_info; uInt read_now; @@ -1998,11 +1864,10 @@ extern int ZEXPORT unzGetLocalExtrafield (unzFile file, voidp buf, unsigned len) } /* - Close the file in zip opened with unzipOpenCurrentFile + Close the file in zip opened with unzOpenCurrentFile Return UNZ_CRCERROR if all the file was read but the CRC is not good */ -extern int ZEXPORT unzCloseCurrentFile (unzFile file) -{ +extern int ZEXPORT unzCloseCurrentFile(unzFile file) { int err=UNZ_OK; unz64_s* s; @@ -2024,7 +1889,7 @@ extern int ZEXPORT unzCloseCurrentFile (unzFile file) } - TRYFREE(pfile_in_zip_read_info->read_buffer); + free(pfile_in_zip_read_info->read_buffer); pfile_in_zip_read_info->read_buffer = NULL; if (pfile_in_zip_read_info->stream_initialised == Z_DEFLATED) inflateEnd(&pfile_in_zip_read_info->stream); @@ -2035,7 +1900,7 @@ extern int ZEXPORT unzCloseCurrentFile (unzFile file) pfile_in_zip_read_info->stream_initialised = 0; - TRYFREE(pfile_in_zip_read_info); + free(pfile_in_zip_read_info); s->pfile_in_zip_read=NULL; @@ -2048,8 +1913,7 @@ extern int ZEXPORT unzCloseCurrentFile (unzFile file) uSizeBuf is the size of the szComment buffer. return the number of byte copied or an error code <0 */ -extern int ZEXPORT unzGetGlobalComment (unzFile file, char * szComment, uLong uSizeBuf) -{ +extern int ZEXPORT unzGetGlobalComment(unzFile file, char * szComment, uLong uSizeBuf) { unz64_s* s; uLong uReadThis ; if (file==NULL) @@ -2076,8 +1940,7 @@ extern int ZEXPORT unzGetGlobalComment (unzFile file, char * szComment, uLong uS } /* Additions by RX '2004 */ -extern ZPOS64_T ZEXPORT unzGetOffset64(unzFile file) -{ +extern ZPOS64_T ZEXPORT unzGetOffset64(unzFile file) { unz64_s* s; if (file==NULL) @@ -2091,8 +1954,7 @@ extern ZPOS64_T ZEXPORT unzGetOffset64(unzFile file) return s->pos_in_central_dir; } -extern uLong ZEXPORT unzGetOffset (unzFile file) -{ +extern uLong ZEXPORT unzGetOffset(unzFile file) { ZPOS64_T offset64; if (file==NULL) @@ -2101,8 +1963,7 @@ extern uLong ZEXPORT unzGetOffset (unzFile file) return (uLong)offset64; } -extern int ZEXPORT unzSetOffset64(unzFile file, ZPOS64_T pos) -{ +extern int ZEXPORT unzSetOffset64(unzFile file, ZPOS64_T pos) { unz64_s* s; int err; @@ -2119,7 +1980,6 @@ extern int ZEXPORT unzSetOffset64(unzFile file, ZPOS64_T pos) return err; } -extern int ZEXPORT unzSetOffset (unzFile file, uLong pos) -{ +extern int ZEXPORT unzSetOffset (unzFile file, uLong pos) { return unzSetOffset64(file,pos); } diff --git a/mupen64plus-core/subprojects/minizip/unzip.h b/mupen64plus-core/subprojects/minizip/unzip.h index 3183968b..5cfc9c62 100644 --- a/mupen64plus-core/subprojects/minizip/unzip.h +++ b/mupen64plus-core/subprojects/minizip/unzip.h @@ -83,12 +83,12 @@ typedef voidp unzFile; /* tm_unz contain date/time info */ typedef struct tm_unz_s { - uInt tm_sec; /* seconds after the minute - [0,59] */ - uInt tm_min; /* minutes after the hour - [0,59] */ - uInt tm_hour; /* hours since midnight - [0,23] */ - uInt tm_mday; /* day of the month - [1,31] */ - uInt tm_mon; /* months since January - [0,11] */ - uInt tm_year; /* years - [1980..2044] */ + int tm_sec; /* seconds after the minute - [0,59] */ + int tm_min; /* minutes after the hour - [0,59] */ + int tm_hour; /* hours since midnight - [0,23] */ + int tm_mday; /* day of the month - [1,31] */ + int tm_mon; /* months since January - [0,11] */ + int tm_year; /* years - [1980..2044] */ } tm_unz; /* unz_global_info structure contain global data about the ZIPfile @@ -150,21 +150,21 @@ typedef struct unz_file_info_s tm_unz tmu_date; } unz_file_info; -extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1, - const char* fileName2, - int iCaseSensitivity)); +extern int ZEXPORT unzStringFileNameCompare(const char* fileName1, + const char* fileName2, + int iCaseSensitivity); /* - Compare two filename (fileName1,fileName2). - If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) - If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + Compare two filenames (fileName1,fileName2). + If iCaseSensitivity = 1, comparison is case sensitive (like strcmp) + If iCaseSensitivity = 2, comparison is not case sensitive (like strcmpi or strcasecmp) - If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + If iCaseSensitivity = 0, case sensitivity is default of your operating system (like 1 on Unix, 2 on Windows) */ -extern unzFile ZEXPORT unzOpen OF((const char *path)); -extern unzFile ZEXPORT unzOpen64 OF((const void *path)); +extern unzFile ZEXPORT unzOpen(const char *path); +extern unzFile ZEXPORT unzOpen64(const void *path); /* Open a Zip file. path contain the full pathname (by example, on a Windows XP computer "c:\\zlib\\zlib113.zip" or on an Unix computer @@ -181,41 +181,41 @@ extern unzFile ZEXPORT unzOpen64 OF((const void *path)); */ -extern unzFile ZEXPORT unzOpen2 OF((const char *path, - zlib_filefunc_def* pzlib_filefunc_def)); +extern unzFile ZEXPORT unzOpen2(const char *path, + zlib_filefunc_def* pzlib_filefunc_def); /* Open a Zip file, like unzOpen, but provide a set of file low level API for read/write the zip file (see ioapi.h) */ -extern unzFile ZEXPORT unzOpen2_64 OF((const void *path, - zlib_filefunc64_def* pzlib_filefunc_def)); +extern unzFile ZEXPORT unzOpen2_64(const void *path, + zlib_filefunc64_def* pzlib_filefunc_def); /* Open a Zip file, like unz64Open, but provide a set of file low level API for read/write the zip file (see ioapi.h) */ -extern int ZEXPORT unzClose OF((unzFile file)); +extern int ZEXPORT unzClose(unzFile file); /* - Close a ZipFile opened with unzipOpen. + Close a ZipFile opened with unzOpen. If there is files inside the .Zip opened with unzOpenCurrentFile (see later), - these files MUST be closed with unzipCloseCurrentFile before call unzipClose. + these files MUST be closed with unzCloseCurrentFile before call unzClose. return UNZ_OK if there is no problem. */ -extern int ZEXPORT unzGetGlobalInfo OF((unzFile file, - unz_global_info *pglobal_info)); +extern int ZEXPORT unzGetGlobalInfo(unzFile file, + unz_global_info *pglobal_info); -extern int ZEXPORT unzGetGlobalInfo64 OF((unzFile file, - unz_global_info64 *pglobal_info)); +extern int ZEXPORT unzGetGlobalInfo64(unzFile file, + unz_global_info64 *pglobal_info); /* Write info about the ZipFile in the *pglobal_info structure. No preparation of the structure is needed return UNZ_OK if there is no problem. */ -extern int ZEXPORT unzGetGlobalComment OF((unzFile file, - char *szComment, - uLong uSizeBuf)); +extern int ZEXPORT unzGetGlobalComment(unzFile file, + char *szComment, + uLong uSizeBuf); /* Get the global comment string of the ZipFile, in the szComment buffer. uSizeBuf is the size of the szComment buffer. @@ -226,22 +226,22 @@ extern int ZEXPORT unzGetGlobalComment OF((unzFile file, /***************************************************************************/ /* Unzip package allow you browse the directory of the zipfile */ -extern int ZEXPORT unzGoToFirstFile OF((unzFile file)); +extern int ZEXPORT unzGoToFirstFile(unzFile file); /* Set the current file of the zipfile to the first file. return UNZ_OK if there is no problem */ -extern int ZEXPORT unzGoToNextFile OF((unzFile file)); +extern int ZEXPORT unzGoToNextFile(unzFile file); /* Set the current file of the zipfile to the next file. return UNZ_OK if there is no problem return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. */ -extern int ZEXPORT unzLocateFile OF((unzFile file, - const char *szFileName, - int iCaseSensitivity)); +extern int ZEXPORT unzLocateFile(unzFile file, + const char *szFileName, + int iCaseSensitivity); /* Try locate the file szFileName in the zipfile. For the iCaseSensitivity signification, see unzStringFileNameCompare @@ -285,28 +285,28 @@ extern int ZEXPORT unzGoToFilePos64( /* ****************************************** */ -extern int ZEXPORT unzGetCurrentFileInfo64 OF((unzFile file, - unz_file_info64 *pfile_info, - char *szFileName, - uLong fileNameBufferSize, - void *extraField, - uLong extraFieldBufferSize, - char *szComment, - uLong commentBufferSize)); - -extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, - unz_file_info *pfile_info, - char *szFileName, - uLong fileNameBufferSize, - void *extraField, - uLong extraFieldBufferSize, - char *szComment, - uLong commentBufferSize)); +extern int ZEXPORT unzGetCurrentFileInfo64(unzFile file, + unz_file_info64 *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize); + +extern int ZEXPORT unzGetCurrentFileInfo(unzFile file, + unz_file_info *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize); /* Get Info about the current file - if pfile_info!=NULL, the *pfile_info structure will contain somes info about + if pfile_info!=NULL, the *pfile_info structure will contain some info about the current file - if szFileName!=NULL, the filemane string will be copied in szFileName + if szFileName!=NULL, the filename string will be copied in szFileName (fileNameBufferSize is the size of the buffer) if extraField!=NULL, the extra field information will be copied in extraField (extraFieldBufferSize is the size of the buffer). @@ -318,7 +318,7 @@ extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, /** Addition for GDAL : START */ -extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64 OF((unzFile file)); +extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64(unzFile file); /** Addition for GDAL : END */ @@ -328,24 +328,24 @@ extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64 OF((unzFile file)); from it, and close it (you can close it before reading all the file) */ -extern int ZEXPORT unzOpenCurrentFile OF((unzFile file)); +extern int ZEXPORT unzOpenCurrentFile(unzFile file); /* Open for reading data the current file in the zipfile. If there is no error, the return value is UNZ_OK. */ -extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file, - const char* password)); +extern int ZEXPORT unzOpenCurrentFilePassword(unzFile file, + const char* password); /* Open for reading data the current file in the zipfile. password is a crypting password If there is no error, the return value is UNZ_OK. */ -extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file, - int* method, - int* level, - int raw)); +extern int ZEXPORT unzOpenCurrentFile2(unzFile file, + int* method, + int* level, + int raw); /* Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) if raw==1 @@ -355,11 +355,11 @@ extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file, but you CANNOT set method parameter as NULL */ -extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file, - int* method, - int* level, - int raw, - const char* password)); +extern int ZEXPORT unzOpenCurrentFile3(unzFile file, + int* method, + int* level, + int raw, + const char* password); /* Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) if raw==1 @@ -370,41 +370,41 @@ extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file, */ -extern int ZEXPORT unzCloseCurrentFile OF((unzFile file)); +extern int ZEXPORT unzCloseCurrentFile(unzFile file); /* Close the file in zip opened with unzOpenCurrentFile Return UNZ_CRCERROR if all the file was read but the CRC is not good */ -extern int ZEXPORT unzReadCurrentFile OF((unzFile file, - voidp buf, - unsigned len)); +extern int ZEXPORT unzReadCurrentFile(unzFile file, + voidp buf, + unsigned len); /* Read bytes from the current file (opened by unzOpenCurrentFile) buf contain buffer where data must be copied len the size of buf. - return the number of byte copied if somes bytes are copied + return the number of byte copied if some bytes are copied return 0 if the end of file was reached return <0 with error code if there is an error (UNZ_ERRNO for IO error, or zLib error for uncompress error) */ -extern z_off_t ZEXPORT unztell OF((unzFile file)); +extern z_off_t ZEXPORT unztell(unzFile file); -extern ZPOS64_T ZEXPORT unztell64 OF((unzFile file)); +extern ZPOS64_T ZEXPORT unztell64(unzFile file); /* Give the current position in uncompressed data */ -extern int ZEXPORT unzeof OF((unzFile file)); +extern int ZEXPORT unzeof(unzFile file); /* return 1 if the end of file was reached, 0 elsewhere */ -extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file, - voidp buf, - unsigned len)); +extern int ZEXPORT unzGetLocalExtrafield(unzFile file, + voidp buf, + unsigned len); /* Read extra field from the current file (opened by unzOpenCurrentFile) This is the local-header version of the extra field (sometimes, there is diff --git a/mupen64plus-core/subprojects/minizip/zip.c b/mupen64plus-core/subprojects/minizip/zip.c index d54fe1c8..60bdffac 100644 --- a/mupen64plus-core/subprojects/minizip/zip.c +++ b/mupen64plus-core/subprojects/minizip/zip.c @@ -14,8 +14,8 @@ Oct-2009 - Mathias Svensson - Added Zip64 Support when creating new file archives Oct-2009 - Mathias Svensson - Did some code cleanup and refactoring to get better overview of some functions. Oct-2009 - Mathias Svensson - Added zipRemoveExtraInfoBlock to strip extra field data from its ZIP64 data - It is used when recreting zip archive with RAW when deleting items from a zip. - ZIP64 data is automaticly added to items that needs it, and existing ZIP64 data need to be removed. + It is used when recreating zip archive with RAW when deleting items from a zip. + ZIP64 data is automatically added to items that needs it, and existing ZIP64 data need to be removed. Oct-2009 - Mathias Svensson - Added support for BZIP2 as compression mode (bzip2 lib is required) Jan-2010 - back to unzip and minizip 1.0 name scheme, with compatibility layer @@ -25,15 +25,13 @@ #include #include #include +#include #include - -#include "zip.h" #include "zlib.h" +#include "zip.h" #ifdef STDC # include -# include -# include #endif #ifdef NO_ERRNO_H extern int errno; @@ -48,7 +46,7 @@ /* compile with -Dlocal if your debugger can't find static symbols */ #ifndef VERSIONMADEBY -# define VERSIONMADEBY (0x0) /* platform depedent */ +# define VERSIONMADEBY (0x0) /* platform dependent */ #endif #ifndef Z_BUFSIZE @@ -62,9 +60,6 @@ #ifndef ALLOC # define ALLOC(size) (malloc(size)) #endif -#ifndef TRYFREE -# define TRYFREE(p) {if (p) free(p);} -#endif /* #define SIZECENTRALDIRITEM (0x2e) @@ -117,7 +112,7 @@ typedef struct linkedlist_datablock_internal_s struct linkedlist_datablock_internal_s* next_datablock; uLong avail_in_this_block; uLong filled_in_this_block; - uLong unused; /* for future use and alignement */ + uLong unused; /* for future use and alignment */ unsigned char data[SIZEDATA_INDATABLOCK]; } linkedlist_datablock_internal; @@ -139,40 +134,40 @@ typedef struct uInt pos_in_buffered_data; /* last written byte in buffered_data */ ZPOS64_T pos_local_header; /* offset of the local header of the file - currenty writing */ + currently writing */ char* central_header; /* central header data for the current file */ uLong size_centralExtra; uLong size_centralheader; /* size of the central header for cur file */ uLong size_centralExtraFree; /* Extra bytes allocated to the centralheader but that are not used */ uLong flag; /* flag of the file currently writing */ - int method; /* compression method of file currenty wr.*/ + int method; /* compression method of file currently wr.*/ int raw; /* 1 for directly writing raw data */ Byte buffered_data[Z_BUFSIZE];/* buffer contain compressed data to be writ*/ uLong dosDate; uLong crc32; int encrypt; - int zip64; /* Add ZIP64 extened information in the extra field */ + int zip64; /* Add ZIP64 extended information in the extra field */ ZPOS64_T pos_zip64extrainfo; ZPOS64_T totalCompressedData; ZPOS64_T totalUncompressedData; #ifndef NOCRYPT unsigned long keys[3]; /* keys defining the pseudo-random sequence */ - const unsigned long* pcrc_32_tab; - int crypt_header_size; + const z_crc_t* pcrc_32_tab; + unsigned crypt_header_size; #endif } curfile64_info; typedef struct { zlib_filefunc64_32_def z_filefunc; - voidpf filestream; /* io structore of the zipfile */ + voidpf filestream; /* io structure of the zipfile */ linkedlist_data central_dir;/* datablock with central dir in construction*/ int in_opened_file_inzip; /* 1 if a file in the zip is currently writ.*/ - curfile64_info ci; /* info on the file curretly writing */ + curfile64_info ci; /* info on the file currently writing */ ZPOS64_T begin_pos; /* position of the beginning of the zipfile */ - ZPOS64_T add_position_when_writting_offset; + ZPOS64_T add_position_when_writing_offset; ZPOS64_T number_entry; #ifndef NO_ADDFILEINEXISTINGZIP @@ -187,8 +182,7 @@ typedef struct #include "crypt.h" #endif -local linkedlist_datablock_internal* allocate_new_datablock() -{ +local linkedlist_datablock_internal* allocate_new_datablock(void) { linkedlist_datablock_internal* ldi; ldi = (linkedlist_datablock_internal*) ALLOC(sizeof(linkedlist_datablock_internal)); @@ -201,30 +195,26 @@ local linkedlist_datablock_internal* allocate_new_datablock() return ldi; } -local void free_datablock(linkedlist_datablock_internal* ldi) -{ +local void free_datablock(linkedlist_datablock_internal* ldi) { while (ldi!=NULL) { linkedlist_datablock_internal* ldinext = ldi->next_datablock; - TRYFREE(ldi); + free(ldi); ldi = ldinext; } } -local void init_linkedlist(linkedlist_data* ll) -{ +local void init_linkedlist(linkedlist_data* ll) { ll->first_block = ll->last_block = NULL; } -local void free_linkedlist(linkedlist_data* ll) -{ +local void free_linkedlist(linkedlist_data* ll) { free_datablock(ll->first_block); ll->first_block = ll->last_block = NULL; } -local int add_data_in_datablock(linkedlist_data* ll, const void* buf, uLong len) -{ +local int add_data_in_datablock(linkedlist_data* ll, const void* buf, uLong len) { linkedlist_datablock_internal* ldi; const unsigned char* from_copy; @@ -239,7 +229,7 @@ local int add_data_in_datablock(linkedlist_data* ll, const void* buf, uLong len) } ldi = ll->last_block; - from_copy = (unsigned char*)buf; + from_copy = (const unsigned char*)buf; while (len>0) { @@ -284,9 +274,7 @@ local int add_data_in_datablock(linkedlist_data* ll, const void* buf, uLong len) nbByte == 1, 2 ,4 or 8 (byte, short or long, ZPOS64_T) */ -local int zip64local_putValue OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte)); -local int zip64local_putValue (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte) -{ +local int zip64local_putValue(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte) { unsigned char buf[8]; int n; for (n = 0; n < nbByte; n++) @@ -302,15 +290,13 @@ local int zip64local_putValue (const zlib_filefunc64_32_def* pzlib_filefunc_def, } } - if (ZWRITE64(*pzlib_filefunc_def,filestream,buf,nbByte)!=(uLong)nbByte) + if (ZWRITE64(*pzlib_filefunc_def,filestream,buf,(uLong)nbByte)!=(uLong)nbByte) return ZIP_ERRNO; else return ZIP_OK; } -local void zip64local_putValue_inmemory OF((void* dest, ZPOS64_T x, int nbByte)); -local void zip64local_putValue_inmemory (void* dest, ZPOS64_T x, int nbByte) -{ +local void zip64local_putValue_inmemory (void* dest, ZPOS64_T x, int nbByte) { unsigned char* buf=(unsigned char*)dest; int n; for (n = 0; n < nbByte; n++) { @@ -330,25 +316,21 @@ local void zip64local_putValue_inmemory (void* dest, ZPOS64_T x, int nbByte) /****************************************************************************/ -local uLong zip64local_TmzDateToDosDate(const tm_zip* ptm) -{ +local uLong zip64local_TmzDateToDosDate(const tm_zip* ptm) { uLong year = (uLong)ptm->tm_year; if (year>=1980) year-=1980; else if (year>=80) year-=80; return - (uLong) (((ptm->tm_mday) + (32 * (ptm->tm_mon+1)) + (512 * year)) << 16) | - ((ptm->tm_sec/2) + (32* ptm->tm_min) + (2048 * (uLong)ptm->tm_hour)); + (uLong) (((uLong)(ptm->tm_mday) + (32 * (uLong)(ptm->tm_mon+1)) + (512 * year)) << 16) | + (((uLong)ptm->tm_sec/2) + (32 * (uLong)ptm->tm_min) + (2048 * (uLong)ptm->tm_hour)); } /****************************************************************************/ -local int zip64local_getByte OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int *pi)); - -local int zip64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def,voidpf filestream,int* pi) -{ +local int zip64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int* pi) { unsigned char c; int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,&c,1); if (err==1) @@ -369,10 +351,7 @@ local int zip64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def,vo /* =========================================================================== Reads a long in LSB order from the given gz_stream. Sets */ -local int zip64local_getShort OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); - -local int zip64local_getShort (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX) -{ +local int zip64local_getShort(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX) { uLong x ; int i = 0; int err; @@ -391,10 +370,7 @@ local int zip64local_getShort (const zlib_filefunc64_32_def* pzlib_filefunc_def, return err; } -local int zip64local_getLong OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); - -local int zip64local_getLong (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX) -{ +local int zip64local_getLong(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX) { uLong x ; int i = 0; int err; @@ -421,11 +397,8 @@ local int zip64local_getLong (const zlib_filefunc64_32_def* pzlib_filefunc_def, return err; } -local int zip64local_getLong64 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX)); - -local int zip64local_getLong64 (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX) -{ +local int zip64local_getLong64(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX) { ZPOS64_T x; int i = 0; int err; @@ -476,10 +449,7 @@ local int zip64local_getLong64 (const zlib_filefunc64_32_def* pzlib_filefunc_def Locate the Central directory of a zipfile (at the end, just before the global comment) */ -local ZPOS64_T zip64local_SearchCentralDir OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); - -local ZPOS64_T zip64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) -{ +local ZPOS64_T zip64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) { unsigned char* buf; ZPOS64_T uSizeFile; ZPOS64_T uBackRead; @@ -523,14 +493,14 @@ local ZPOS64_T zip64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_f if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) { - uPosFound = uReadPos+i; + uPosFound = uReadPos+(unsigned)i; break; } if (uPosFound!=0) break; } - TRYFREE(buf); + free(buf); return uPosFound; } @@ -538,10 +508,7 @@ local ZPOS64_T zip64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_f Locate the End of Zip64 Central directory locator and from there find the CD of a zipfile (at the end, just before the global comment) */ -local ZPOS64_T zip64local_SearchCentralDir64 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); - -local ZPOS64_T zip64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) -{ +local ZPOS64_T zip64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) { unsigned char* buf; ZPOS64_T uSizeFile; ZPOS64_T uBackRead; @@ -587,7 +554,7 @@ local ZPOS64_T zip64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib // Signature "0x07064b50" Zip64 end of central directory locater if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x06) && ((*(buf+i+3))==0x07)) { - uPosFound = uReadPos+i; + uPosFound = uReadPos+(unsigned)i; break; } } @@ -596,7 +563,7 @@ local ZPOS64_T zip64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib break; } - TRYFREE(buf); + free(buf); if (uPosFound == 0) return 0; @@ -608,7 +575,7 @@ local ZPOS64_T zip64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) return 0; - /* number of the disk with the start of the zip64 end of central directory */ + /* number of the disk with the start of the zip64 end of central directory */ if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) return 0; if (uL != 0) @@ -638,8 +605,7 @@ local ZPOS64_T zip64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib return relativeOffset; } -int LoadCentralDirectoryRecord(zip64_internal* pziinit) -{ +local int LoadCentralDirectoryRecord(zip64_internal* pziinit) { int err=ZIP_OK; ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ @@ -648,10 +614,10 @@ int LoadCentralDirectoryRecord(zip64_internal* pziinit) ZPOS64_T central_pos; uLong uL; - uLong number_disk; /* number of the current dist, used for - spaning ZIP, unsupported, always 0*/ - uLong number_disk_with_CD; /* number the the disk with central dir, used - for spaning ZIP, unsupported, always 0*/ + uLong number_disk; /* number of the current disk, used for + spanning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number of the disk with central dir, used + for spanning ZIP, unsupported, always 0*/ ZPOS64_T number_entry; ZPOS64_T number_entry_CD; /* total number of entries in the central dir @@ -808,7 +774,7 @@ int LoadCentralDirectoryRecord(zip64_internal* pziinit) } byte_before_the_zipfile = central_pos - (offset_central_dir+size_central_dir); - pziinit->add_position_when_writting_offset = byte_before_the_zipfile; + pziinit->add_position_when_writing_offset = byte_before_the_zipfile; { ZPOS64_T size_central_dir_to_read = size_central_dir; @@ -831,7 +797,7 @@ int LoadCentralDirectoryRecord(zip64_internal* pziinit) size_central_dir_to_read-=read_this; } - TRYFREE(buf_read); + free(buf_read); } pziinit->begin_pos = byte_before_the_zipfile; pziinit->number_entry = number_entry_CD; @@ -847,8 +813,7 @@ int LoadCentralDirectoryRecord(zip64_internal* pziinit) /************************************************************/ -extern zipFile ZEXPORT zipOpen3 (const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_32_def* pzlib_filefunc64_32_def) -{ +extern zipFile ZEXPORT zipOpen3(const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_32_def* pzlib_filefunc64_32_def) { zip64_internal ziinit; zip64_internal* zi; int err=ZIP_OK; @@ -876,7 +841,7 @@ extern zipFile ZEXPORT zipOpen3 (const void *pathname, int append, zipcharpc* gl ziinit.in_opened_file_inzip = 0; ziinit.ci.stream_initialised = 0; ziinit.number_entry = 0; - ziinit.add_position_when_writting_offset = 0; + ziinit.add_position_when_writing_offset = 0; init_linkedlist(&(ziinit.central_dir)); @@ -906,9 +871,9 @@ extern zipFile ZEXPORT zipOpen3 (const void *pathname, int append, zipcharpc* gl if (err != ZIP_OK) { # ifndef NO_ADDFILEINEXISTINGZIP - TRYFREE(ziinit.globalcomment); + free(ziinit.globalcomment); # endif /* !NO_ADDFILEINEXISTINGZIP*/ - TRYFREE(zi); + free(zi); return NULL; } else @@ -918,8 +883,7 @@ extern zipFile ZEXPORT zipOpen3 (const void *pathname, int append, zipcharpc* gl } } -extern zipFile ZEXPORT zipOpen2 (const char *pathname, int append, zipcharpc* globalcomment, zlib_filefunc_def* pzlib_filefunc32_def) -{ +extern zipFile ZEXPORT zipOpen2(const char *pathname, int append, zipcharpc* globalcomment, zlib_filefunc_def* pzlib_filefunc32_def) { if (pzlib_filefunc32_def != NULL) { zlib_filefunc64_32_def zlib_filefunc64_32_def_fill; @@ -930,8 +894,7 @@ extern zipFile ZEXPORT zipOpen2 (const char *pathname, int append, zipcharpc* gl return zipOpen3(pathname, append, globalcomment, NULL); } -extern zipFile ZEXPORT zipOpen2_64 (const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_def* pzlib_filefunc_def) -{ +extern zipFile ZEXPORT zipOpen2_64(const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_def* pzlib_filefunc_def) { if (pzlib_filefunc_def != NULL) { zlib_filefunc64_32_def zlib_filefunc64_32_def_fill; @@ -946,18 +909,15 @@ extern zipFile ZEXPORT zipOpen2_64 (const void *pathname, int append, zipcharpc* -extern zipFile ZEXPORT zipOpen (const char* pathname, int append) -{ +extern zipFile ZEXPORT zipOpen(const char* pathname, int append) { return zipOpen3((const void*)pathname,append,NULL,NULL); } -extern zipFile ZEXPORT zipOpen64 (const void* pathname, int append) -{ +extern zipFile ZEXPORT zipOpen64(const void* pathname, int append) { return zipOpen3(pathname,append,NULL,NULL); } -int Write_LocalFileHeader(zip64_internal* zi, const char* filename, uInt size_extrafield_local, const void* extrafield_local) -{ +local int Write_LocalFileHeader(zip64_internal* zi, const char* filename, uInt size_extrafield_local, const void* extrafield_local) { /* write the local header */ int err; uInt size_filename = (uInt)strlen(filename); @@ -1035,8 +995,8 @@ int Write_LocalFileHeader(zip64_internal* zi, const char* filename, uInt size_ex // Remember position of Zip64 extended info for the local file header. (needed when we update size after done with file) zi->ci.pos_zip64extrainfo = ZTELL64(zi->z_filefunc,zi->filestream); - err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (short)HeaderID,2); - err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (short)DataSize,2); + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)HeaderID,2); + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)DataSize,2); err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)UncompressedSize,8); err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)CompressedSize,8); @@ -1053,14 +1013,13 @@ int Write_LocalFileHeader(zip64_internal* zi, const char* filename, uInt size_ex It is not done here because then we need to realloc a new buffer since parameters are 'const' and I want to minimize unnecessary allocations. */ -extern int ZEXPORT zipOpenNewFileInZip4_64 (zipFile file, const char* filename, const zip_fileinfo* zipfi, - const void* extrafield_local, uInt size_extrafield_local, - const void* extrafield_global, uInt size_extrafield_global, - const char* comment, int method, int level, int raw, - int windowBits,int memLevel, int strategy, - const char* password, uLong crcForCrypting, - uLong versionMadeBy, uLong flagBase, int zip64) -{ +extern int ZEXPORT zipOpenNewFileInZip4_64(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting, + uLong versionMadeBy, uLong flagBase, int zip64) { zip64_internal* zi; uInt size_filename; uInt size_comment; @@ -1068,6 +1027,7 @@ extern int ZEXPORT zipOpenNewFileInZip4_64 (zipFile file, const char* filename, int err = ZIP_OK; # ifdef NOCRYPT + (crcForCrypting); if (password != NULL) return ZIP_PARAMERROR; # endif @@ -1083,6 +1043,17 @@ extern int ZEXPORT zipOpenNewFileInZip4_64 (zipFile file, const char* filename, return ZIP_PARAMERROR; #endif + // The filename and comment length must fit in 16 bits. + if ((filename!=NULL) && (strlen(filename)>0xffff)) + return ZIP_PARAMERROR; + if ((comment!=NULL) && (strlen(comment)>0xffff)) + return ZIP_PARAMERROR; + // The extra field length must fit in 16 bits. If the member also requires + // a Zip64 extra block, that will also need to fit within that 16-bit + // length, but that will be checked for later. + if ((size_extrafield_local>0xffff) || (size_extrafield_global>0xffff)) + return ZIP_PARAMERROR; + zi = (zip64_internal*)file; if (zi->in_opened_file_inzip == 1) @@ -1164,7 +1135,7 @@ extern int ZEXPORT zipOpenNewFileInZip4_64 (zipFile file, const char* filename, if(zi->ci.pos_local_header >= 0xffffffff) zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)0xffffffff,4); else - zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)zi->ci.pos_local_header - zi->add_position_when_writting_offset,4); + zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)zi->ci.pos_local_header - zi->add_position_when_writing_offset,4); for (i=0;ici.central_header+SIZECENTRALHEADER+i) = *(filename+i); @@ -1262,35 +1233,33 @@ extern int ZEXPORT zipOpenNewFileInZip4_64 (zipFile file, const char* filename, return err; } -extern int ZEXPORT zipOpenNewFileInZip4 (zipFile file, const char* filename, const zip_fileinfo* zipfi, - const void* extrafield_local, uInt size_extrafield_local, - const void* extrafield_global, uInt size_extrafield_global, - const char* comment, int method, int level, int raw, - int windowBits,int memLevel, int strategy, - const char* password, uLong crcForCrypting, - uLong versionMadeBy, uLong flagBase) -{ - return zipOpenNewFileInZip4_64 (file, filename, zipfi, - extrafield_local, size_extrafield_local, - extrafield_global, size_extrafield_global, - comment, method, level, raw, - windowBits, memLevel, strategy, - password, crcForCrypting, versionMadeBy, flagBase, 0); +extern int ZEXPORT zipOpenNewFileInZip4(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting, + uLong versionMadeBy, uLong flagBase) { + return zipOpenNewFileInZip4_64(file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting, versionMadeBy, flagBase, 0); } -extern int ZEXPORT zipOpenNewFileInZip3 (zipFile file, const char* filename, const zip_fileinfo* zipfi, - const void* extrafield_local, uInt size_extrafield_local, - const void* extrafield_global, uInt size_extrafield_global, - const char* comment, int method, int level, int raw, - int windowBits,int memLevel, int strategy, - const char* password, uLong crcForCrypting) -{ - return zipOpenNewFileInZip4_64 (file, filename, zipfi, - extrafield_local, size_extrafield_local, - extrafield_global, size_extrafield_global, - comment, method, level, raw, - windowBits, memLevel, strategy, - password, crcForCrypting, VERSIONMADEBY, 0, 0); +extern int ZEXPORT zipOpenNewFileInZip3(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting) { + return zipOpenNewFileInZip4_64(file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting, VERSIONMADEBY, 0, 0); } extern int ZEXPORT zipOpenNewFileInZip3_64(zipFile file, const char* filename, const zip_fileinfo* zipfi, @@ -1298,70 +1267,64 @@ extern int ZEXPORT zipOpenNewFileInZip3_64(zipFile file, const char* filename, c const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits,int memLevel, int strategy, - const char* password, uLong crcForCrypting, int zip64) -{ - return zipOpenNewFileInZip4_64 (file, filename, zipfi, - extrafield_local, size_extrafield_local, - extrafield_global, size_extrafield_global, - comment, method, level, raw, - windowBits, memLevel, strategy, - password, crcForCrypting, VERSIONMADEBY, 0, zip64); + const char* password, uLong crcForCrypting, int zip64) { + return zipOpenNewFileInZip4_64(file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting, VERSIONMADEBY, 0, zip64); } extern int ZEXPORT zipOpenNewFileInZip2(zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, - const char* comment, int method, int level, int raw) -{ - return zipOpenNewFileInZip4_64 (file, filename, zipfi, - extrafield_local, size_extrafield_local, - extrafield_global, size_extrafield_global, - comment, method, level, raw, - -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, - NULL, 0, VERSIONMADEBY, 0, 0); + const char* comment, int method, int level, int raw) { + return zipOpenNewFileInZip4_64(file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, 0); } extern int ZEXPORT zipOpenNewFileInZip2_64(zipFile file, const char* filename, const zip_fileinfo* zipfi, - const void* extrafield_local, uInt size_extrafield_local, - const void* extrafield_global, uInt size_extrafield_global, - const char* comment, int method, int level, int raw, int zip64) -{ - return zipOpenNewFileInZip4_64 (file, filename, zipfi, - extrafield_local, size_extrafield_local, - extrafield_global, size_extrafield_global, - comment, method, level, raw, - -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, - NULL, 0, VERSIONMADEBY, 0, zip64); + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, int zip64) { + return zipOpenNewFileInZip4_64(file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, zip64); } -extern int ZEXPORT zipOpenNewFileInZip64 (zipFile file, const char* filename, const zip_fileinfo* zipfi, - const void* extrafield_local, uInt size_extrafield_local, - const void*extrafield_global, uInt size_extrafield_global, - const char* comment, int method, int level, int zip64) -{ - return zipOpenNewFileInZip4_64 (file, filename, zipfi, - extrafield_local, size_extrafield_local, - extrafield_global, size_extrafield_global, - comment, method, level, 0, - -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, - NULL, 0, VERSIONMADEBY, 0, zip64); +extern int ZEXPORT zipOpenNewFileInZip64(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void*extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int zip64) { + return zipOpenNewFileInZip4_64(file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, 0, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, zip64); } -extern int ZEXPORT zipOpenNewFileInZip (zipFile file, const char* filename, const zip_fileinfo* zipfi, - const void* extrafield_local, uInt size_extrafield_local, - const void*extrafield_global, uInt size_extrafield_global, - const char* comment, int method, int level) -{ - return zipOpenNewFileInZip4_64 (file, filename, zipfi, - extrafield_local, size_extrafield_local, - extrafield_global, size_extrafield_global, - comment, method, level, 0, - -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, - NULL, 0, VERSIONMADEBY, 0, 0); +extern int ZEXPORT zipOpenNewFileInZip(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void*extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level) { + return zipOpenNewFileInZip4_64(file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, 0, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, 0); } -local int zip64FlushWriteBuffer(zip64_internal* zi) -{ +local int zip64FlushWriteBuffer(zip64_internal* zi) { int err=ZIP_OK; if (zi->ci.encrypt != 0) @@ -1399,8 +1362,7 @@ local int zip64FlushWriteBuffer(zip64_internal* zi) return err; } -extern int ZEXPORT zipWriteInFileInZip (zipFile file,const void* buf,unsigned int len) -{ +extern int ZEXPORT zipWriteInFileInZip(zipFile file, const void* buf, unsigned int len) { zip64_internal* zi; int err=ZIP_OK; @@ -1450,7 +1412,7 @@ extern int ZEXPORT zipWriteInFileInZip (zipFile file,const void* buf,unsigned in else #endif { - zi->ci.stream.next_in = (Bytef*)buf; + zi->ci.stream.next_in = (Bytef*)(uintptr_t)buf; zi->ci.stream.avail_in = len; while ((err==ZIP_OK) && (zi->ci.stream.avail_in>0)) @@ -1471,11 +1433,6 @@ extern int ZEXPORT zipWriteInFileInZip (zipFile file,const void* buf,unsigned in { uLong uTotalOutBefore = zi->ci.stream.total_out; err=deflate(&zi->ci.stream, Z_NO_FLUSH); - if(uTotalOutBefore > zi->ci.stream.total_out) - { - int bBreak = 0; - bBreak++; - } zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; } @@ -1506,17 +1463,15 @@ extern int ZEXPORT zipWriteInFileInZip (zipFile file,const void* buf,unsigned in return err; } -extern int ZEXPORT zipCloseFileInZipRaw (zipFile file, uLong uncompressed_size, uLong crc32) -{ +extern int ZEXPORT zipCloseFileInZipRaw(zipFile file, uLong uncompressed_size, uLong crc32) { return zipCloseFileInZipRaw64 (file, uncompressed_size, crc32); } -extern int ZEXPORT zipCloseFileInZipRaw64 (zipFile file, ZPOS64_T uncompressed_size, uLong crc32) -{ +extern int ZEXPORT zipCloseFileInZipRaw64(zipFile file, ZPOS64_T uncompressed_size, uLong crc32) { zip64_internal* zi; ZPOS64_T compressed_size; uLong invalidValue = 0xffffffff; - short datasize = 0; + unsigned datasize = 0; int err=ZIP_OK; if (file == NULL) @@ -1653,7 +1608,7 @@ extern int ZEXPORT zipCloseFileInZipRaw64 (zipFile file, ZPOS64_T uncompressed_s if((uLong)(datasize + 4) > zi->ci.size_centralExtraFree) { - // we can not write more data to the buffer that we have room for. + // we cannot write more data to the buffer that we have room for. return ZIP_BADZIPFILE; } @@ -1747,15 +1702,13 @@ extern int ZEXPORT zipCloseFileInZipRaw64 (zipFile file, ZPOS64_T uncompressed_s return err; } -extern int ZEXPORT zipCloseFileInZip (zipFile file) -{ +extern int ZEXPORT zipCloseFileInZip(zipFile file) { return zipCloseFileInZipRaw (file,0,0); } -int Write_Zip64EndOfCentralDirectoryLocator(zip64_internal* zi, ZPOS64_T zip64eocd_pos_inzip) -{ +local int Write_Zip64EndOfCentralDirectoryLocator(zip64_internal* zi, ZPOS64_T zip64eocd_pos_inzip) { int err = ZIP_OK; - ZPOS64_T pos = zip64eocd_pos_inzip - zi->add_position_when_writting_offset; + ZPOS64_T pos = zip64eocd_pos_inzip - zi->add_position_when_writing_offset; err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ZIP64ENDLOCHEADERMAGIC,4); @@ -1774,8 +1727,7 @@ int Write_Zip64EndOfCentralDirectoryLocator(zip64_internal* zi, ZPOS64_T zip64eo return err; } -int Write_Zip64EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip) -{ +local int Write_Zip64EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip) { int err = ZIP_OK; uLong Zip64DataSize = 44; @@ -1808,13 +1760,13 @@ int Write_Zip64EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centra if (err==ZIP_OK) /* offset of start of central directory with respect to the starting disk number */ { - ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; + ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writing_offset; err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (ZPOS64_T)pos,8); } return err; } -int Write_EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip) -{ + +local int Write_EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip) { int err = ZIP_OK; /*signature*/ @@ -1849,20 +1801,19 @@ int Write_EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, if (err==ZIP_OK) /* offset of start of central directory with respect to the starting disk number */ { - ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; + ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writing_offset; if(pos >= 0xffffffff) { err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)0xffffffff,4); } else - err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)(centraldir_pos_inzip - zi->add_position_when_writting_offset),4); + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)(centraldir_pos_inzip - zi->add_position_when_writing_offset),4); } return err; } -int Write_GlobalComment(zip64_internal* zi, const char* global_comment) -{ +local int Write_GlobalComment(zip64_internal* zi, const char* global_comment) { int err = ZIP_OK; uInt size_global_comment = 0; @@ -1879,8 +1830,7 @@ int Write_GlobalComment(zip64_internal* zi, const char* global_comment) return err; } -extern int ZEXPORT zipClose (zipFile file, const char* global_comment) -{ +extern int ZEXPORT zipClose(zipFile file, const char* global_comment) { zip64_internal* zi; int err = 0; uLong size_centraldir = 0; @@ -1921,8 +1871,8 @@ extern int ZEXPORT zipClose (zipFile file, const char* global_comment) } free_linkedlist(&(zi->central_dir)); - pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; - if(pos >= 0xffffffff || zi->number_entry > 0xFFFF) + pos = centraldir_pos_inzip - zi->add_position_when_writing_offset; + if(pos >= 0xffffffff || zi->number_entry >= 0xFFFF) { ZPOS64_T Zip64EOCDpos = ZTELL64(zi->z_filefunc,zi->filestream); Write_Zip64EndOfCentralDirectoryRecord(zi, size_centraldir, centraldir_pos_inzip); @@ -1941,15 +1891,14 @@ extern int ZEXPORT zipClose (zipFile file, const char* global_comment) err = ZIP_ERRNO; #ifndef NO_ADDFILEINEXISTINGZIP - TRYFREE(zi->globalcomment); + free(zi->globalcomment); #endif - TRYFREE(zi); + free(zi); return err; } -extern int ZEXPORT zipRemoveExtraInfoBlock (char* pData, int* dataLen, short sHeader) -{ +extern int ZEXPORT zipRemoveExtraInfoBlock(char* pData, int* dataLen, short sHeader) { char* p = pData; int size = 0; char* pNewHeader; @@ -1959,10 +1908,10 @@ extern int ZEXPORT zipRemoveExtraInfoBlock (char* pData, int* dataLen, short sHe int retVal = ZIP_OK; - if(pData == NULL || *dataLen < 4) + if(pData == NULL || dataLen == NULL || *dataLen < 4) return ZIP_PARAMERROR; - pNewHeader = (char*)ALLOC(*dataLen); + pNewHeader = (char*)ALLOC((unsigned)*dataLen); pTmp = pNewHeader; while(p < (pData + *dataLen)) @@ -2001,7 +1950,7 @@ extern int ZEXPORT zipRemoveExtraInfoBlock (char* pData, int* dataLen, short sHe else retVal = ZIP_ERRNO; - TRYFREE(pNewHeader); + free(pNewHeader); return retVal; } diff --git a/mupen64plus-core/subprojects/minizip/zip.h b/mupen64plus-core/subprojects/minizip/zip.h index 8aaebb62..3e230d34 100644 --- a/mupen64plus-core/subprojects/minizip/zip.h +++ b/mupen64plus-core/subprojects/minizip/zip.h @@ -88,12 +88,12 @@ typedef voidp zipFile; /* tm_zip contain date/time info */ typedef struct tm_zip_s { - uInt tm_sec; /* seconds after the minute - [0,59] */ - uInt tm_min; /* minutes after the hour - [0,59] */ - uInt tm_hour; /* hours since midnight - [0,23] */ - uInt tm_mday; /* day of the month - [1,31] */ - uInt tm_mon; /* months since January - [0,11] */ - uInt tm_year; /* years - [1980..2044] */ + int tm_sec; /* seconds after the minute - [0,59] */ + int tm_min; /* minutes after the hour - [0,59] */ + int tm_hour; /* hours since midnight - [0,23] */ + int tm_mday; /* day of the month - [1,31] */ + int tm_mon; /* months since January - [0,11] */ + int tm_year; /* years - [1980..2044] */ } tm_zip; typedef struct @@ -113,8 +113,8 @@ typedef const char* zipcharpc; #define APPEND_STATUS_CREATEAFTER (1) #define APPEND_STATUS_ADDINZIP (2) -extern zipFile ZEXPORT zipOpen OF((const char *pathname, int append)); -extern zipFile ZEXPORT zipOpen64 OF((const void *pathname, int append)); +extern zipFile ZEXPORT zipOpen(const char *pathname, int append); +extern zipFile ZEXPORT zipOpen64(const void *pathname, int append); /* Create a zipfile. pathname contain on Windows XP a filename like "c:\\zlib\\zlib113.zip" or on @@ -131,50 +131,55 @@ extern zipFile ZEXPORT zipOpen64 OF((const void *pathname, int append)); /* Note : there is no delete function into a zipfile. If you want delete file into a zipfile, you must open a zipfile, and create another - Of couse, you can use RAW reading and writing to copy the file you did not want delte + Of course, you can use RAW reading and writing to copy the file you did not want delete */ -extern zipFile ZEXPORT zipOpen2 OF((const char *pathname, - int append, - zipcharpc* globalcomment, - zlib_filefunc_def* pzlib_filefunc_def)); +extern zipFile ZEXPORT zipOpen2(const char *pathname, + int append, + zipcharpc* globalcomment, + zlib_filefunc_def* pzlib_filefunc_def); -extern zipFile ZEXPORT zipOpen2_64 OF((const void *pathname, +extern zipFile ZEXPORT zipOpen2_64(const void *pathname, int append, zipcharpc* globalcomment, - zlib_filefunc64_def* pzlib_filefunc_def)); - -extern int ZEXPORT zipOpenNewFileInZip OF((zipFile file, - const char* filename, - const zip_fileinfo* zipfi, - const void* extrafield_local, - uInt size_extrafield_local, - const void* extrafield_global, - uInt size_extrafield_global, - const char* comment, - int method, - int level)); - -extern int ZEXPORT zipOpenNewFileInZip64 OF((zipFile file, - const char* filename, - const zip_fileinfo* zipfi, - const void* extrafield_local, - uInt size_extrafield_local, - const void* extrafield_global, - uInt size_extrafield_global, - const char* comment, - int method, - int level, - int zip64)); + zlib_filefunc64_def* pzlib_filefunc_def); + +extern zipFile ZEXPORT zipOpen3(const void *pathname, + int append, + zipcharpc* globalcomment, + zlib_filefunc64_32_def* pzlib_filefunc64_32_def); + +extern int ZEXPORT zipOpenNewFileInZip(zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level); + +extern int ZEXPORT zipOpenNewFileInZip64(zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int zip64); /* Open a file in the ZIP for writing. filename : the filename in zip (if NULL, '-' without quote will be used *zipfi contain supplemental information if extrafield_local!=NULL and size_extrafield_local>0, extrafield_local - contains the extrafield data the the local header + contains the extrafield data for the local header if extrafield_global!=NULL and size_extrafield_global>0, extrafield_global - contains the extrafield data the the local header + contains the extrafield data for the global header if comment != NULL, comment contain the comment string method contain the compression method (0 for store, Z_DEFLATED for deflate) level contain the level of compression (can be Z_DEFAULT_COMPRESSION) @@ -184,70 +189,69 @@ extern int ZEXPORT zipOpenNewFileInZip64 OF((zipFile file, */ -extern int ZEXPORT zipOpenNewFileInZip2 OF((zipFile file, - const char* filename, - const zip_fileinfo* zipfi, - const void* extrafield_local, - uInt size_extrafield_local, - const void* extrafield_global, - uInt size_extrafield_global, - const char* comment, - int method, - int level, - int raw)); - - -extern int ZEXPORT zipOpenNewFileInZip2_64 OF((zipFile file, - const char* filename, - const zip_fileinfo* zipfi, - const void* extrafield_local, - uInt size_extrafield_local, - const void* extrafield_global, - uInt size_extrafield_global, - const char* comment, - int method, - int level, - int raw, - int zip64)); +extern int ZEXPORT zipOpenNewFileInZip2(zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw); + + +extern int ZEXPORT zipOpenNewFileInZip2_64(zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int zip64); /* Same than zipOpenNewFileInZip, except if raw=1, we write raw file */ -extern int ZEXPORT zipOpenNewFileInZip3 OF((zipFile file, - const char* filename, - const zip_fileinfo* zipfi, - const void* extrafield_local, - uInt size_extrafield_local, - const void* extrafield_global, - uInt size_extrafield_global, - const char* comment, - int method, - int level, - int raw, - int windowBits, - int memLevel, - int strategy, - const char* password, - uLong crcForCrypting)); - -extern int ZEXPORT zipOpenNewFileInZip3_64 OF((zipFile file, - const char* filename, - const zip_fileinfo* zipfi, - const void* extrafield_local, - uInt size_extrafield_local, - const void* extrafield_global, - uInt size_extrafield_global, - const char* comment, - int method, - int level, - int raw, - int windowBits, - int memLevel, - int strategy, - const char* password, - uLong crcForCrypting, - int zip64 - )); +extern int ZEXPORT zipOpenNewFileInZip3(zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting); + +extern int ZEXPORT zipOpenNewFileInZip3_64(zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + int zip64); /* Same than zipOpenNewFileInZip2, except @@ -256,47 +260,45 @@ extern int ZEXPORT zipOpenNewFileInZip3_64 OF((zipFile file, crcForCrypting : crc of file to compress (needed for crypting) */ -extern int ZEXPORT zipOpenNewFileInZip4 OF((zipFile file, - const char* filename, - const zip_fileinfo* zipfi, - const void* extrafield_local, - uInt size_extrafield_local, - const void* extrafield_global, - uInt size_extrafield_global, - const char* comment, - int method, - int level, - int raw, - int windowBits, - int memLevel, - int strategy, - const char* password, - uLong crcForCrypting, - uLong versionMadeBy, - uLong flagBase - )); - - -extern int ZEXPORT zipOpenNewFileInZip4_64 OF((zipFile file, - const char* filename, - const zip_fileinfo* zipfi, - const void* extrafield_local, - uInt size_extrafield_local, - const void* extrafield_global, - uInt size_extrafield_global, - const char* comment, - int method, - int level, - int raw, - int windowBits, - int memLevel, - int strategy, - const char* password, - uLong crcForCrypting, - uLong versionMadeBy, - uLong flagBase, - int zip64 - )); +extern int ZEXPORT zipOpenNewFileInZip4(zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + uLong versionMadeBy, + uLong flagBase); + + +extern int ZEXPORT zipOpenNewFileInZip4_64(zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + uLong versionMadeBy, + uLong flagBase, + int zip64); /* Same than zipOpenNewFileInZip4, except versionMadeBy : value for Version made by field @@ -304,25 +306,25 @@ extern int ZEXPORT zipOpenNewFileInZip4_64 OF((zipFile file, */ -extern int ZEXPORT zipWriteInFileInZip OF((zipFile file, - const void* buf, - unsigned len)); +extern int ZEXPORT zipWriteInFileInZip(zipFile file, + const void* buf, + unsigned len); /* Write data in the zipfile */ -extern int ZEXPORT zipCloseFileInZip OF((zipFile file)); +extern int ZEXPORT zipCloseFileInZip(zipFile file); /* Close the current file in the zipfile */ -extern int ZEXPORT zipCloseFileInZipRaw OF((zipFile file, - uLong uncompressed_size, - uLong crc32)); +extern int ZEXPORT zipCloseFileInZipRaw(zipFile file, + uLong uncompressed_size, + uLong crc32); -extern int ZEXPORT zipCloseFileInZipRaw64 OF((zipFile file, - ZPOS64_T uncompressed_size, - uLong crc32)); +extern int ZEXPORT zipCloseFileInZipRaw64(zipFile file, + ZPOS64_T uncompressed_size, + uLong crc32); /* Close the current file in the zipfile, for file opened with @@ -330,14 +332,14 @@ extern int ZEXPORT zipCloseFileInZipRaw64 OF((zipFile file, uncompressed_size and crc32 are value for the uncompressed size */ -extern int ZEXPORT zipClose OF((zipFile file, - const char* global_comment)); +extern int ZEXPORT zipClose(zipFile file, + const char* global_comment); /* Close the zipfile */ -extern int ZEXPORT zipRemoveExtraInfoBlock OF((char* pData, int* dataLen, short sHeader)); +extern int ZEXPORT zipRemoveExtraInfoBlock(char* pData, int* dataLen, short sHeader); /* zipRemoveExtraInfoBlock - Added by Mathias Svensson diff --git a/mupen64plus-core/subprojects/xxhash/xxhash.h b/mupen64plus-core/subprojects/xxhash/xxhash.h index 2d56d23c..a18e8c76 100644 --- a/mupen64plus-core/subprojects/xxhash/xxhash.h +++ b/mupen64plus-core/subprojects/xxhash/xxhash.h @@ -1,7 +1,7 @@ /* * xxHash - Extremely Fast Hash algorithm * Header File - * Copyright (C) 2012-2020 Yann Collet + * Copyright (C) 2012-2021 Yann Collet * * BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php) * @@ -33,43 +33,141 @@ * - xxHash source repository: https://github.com/Cyan4973/xxHash */ -/* TODO: update */ -/* Notice extracted from xxHash homepage: - -xxHash is an extremely fast hash algorithm, running at RAM speed limits. -It also successfully passes all tests from the SMHasher suite. - -Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2 Duo @3GHz) - -Name Speed Q.Score Author -xxHash 5.4 GB/s 10 -CrapWow 3.2 GB/s 2 Andrew -MumurHash 3a 2.7 GB/s 10 Austin Appleby -SpookyHash 2.0 GB/s 10 Bob Jenkins -SBox 1.4 GB/s 9 Bret Mulvey -Lookup3 1.2 GB/s 9 Bob Jenkins -SuperFastHash 1.2 GB/s 1 Paul Hsieh -CityHash64 1.05 GB/s 10 Pike & Alakuijala -FNV 0.55 GB/s 5 Fowler, Noll, Vo -CRC32 0.43 GB/s 9 -MD5-32 0.33 GB/s 10 Ronald L. Rivest -SHA1-32 0.28 GB/s 10 - -Q.Score is a measure of quality of the hash function. -It depends on successfully passing SMHasher test set. -10 is a perfect score. - -Note: SMHasher's CRC32 implementation is not the fastest one. -Other speed-oriented implementations can be faster, -especially in combination with PCLMUL instruction: -https://fastcompression.blogspot.com/2019/03/presenting-xxh3.html?showComment=1552696407071#c3490092340461170735 - -A 64-bit version, named XXH64, is available since r35. -It offers much better speed, but for 64-bit applications only. -Name Speed on 64 bits Speed on 32 bits -XXH64 13.8 GB/s 1.9 GB/s -XXH32 6.8 GB/s 6.0 GB/s -*/ +/*! + * @mainpage xxHash + * + * xxHash is an extremely fast non-cryptographic hash algorithm, working at RAM speed + * limits. + * + * It is proposed in four flavors, in three families: + * 1. @ref XXH32_family + * - Classic 32-bit hash function. Simple, compact, and runs on almost all + * 32-bit and 64-bit systems. + * 2. @ref XXH64_family + * - Classic 64-bit adaptation of XXH32. Just as simple, and runs well on most + * 64-bit systems (but _not_ 32-bit systems). + * 3. @ref XXH3_family + * - Modern 64-bit and 128-bit hash function family which features improved + * strength and performance across the board, especially on smaller data. + * It benefits greatly from SIMD and 64-bit without requiring it. + * + * Benchmarks + * --- + * The reference system uses an Intel i7-9700K CPU, and runs Ubuntu x64 20.04. + * The open source benchmark program is compiled with clang v10.0 using -O3 flag. + * + * | Hash Name | ISA ext | Width | Large Data Speed | Small Data Velocity | + * | -------------------- | ------- | ----: | ---------------: | ------------------: | + * | XXH3_64bits() | @b AVX2 | 64 | 59.4 GB/s | 133.1 | + * | MeowHash | AES-NI | 128 | 58.2 GB/s | 52.5 | + * | XXH3_128bits() | @b AVX2 | 128 | 57.9 GB/s | 118.1 | + * | CLHash | PCLMUL | 64 | 37.1 GB/s | 58.1 | + * | XXH3_64bits() | @b SSE2 | 64 | 31.5 GB/s | 133.1 | + * | XXH3_128bits() | @b SSE2 | 128 | 29.6 GB/s | 118.1 | + * | RAM sequential read | | N/A | 28.0 GB/s | N/A | + * | ahash | AES-NI | 64 | 22.5 GB/s | 107.2 | + * | City64 | | 64 | 22.0 GB/s | 76.6 | + * | T1ha2 | | 64 | 22.0 GB/s | 99.0 | + * | City128 | | 128 | 21.7 GB/s | 57.7 | + * | FarmHash | AES-NI | 64 | 21.3 GB/s | 71.9 | + * | XXH64() | | 64 | 19.4 GB/s | 71.0 | + * | SpookyHash | | 64 | 19.3 GB/s | 53.2 | + * | Mum | | 64 | 18.0 GB/s | 67.0 | + * | CRC32C | SSE4.2 | 32 | 13.0 GB/s | 57.9 | + * | XXH32() | | 32 | 9.7 GB/s | 71.9 | + * | City32 | | 32 | 9.1 GB/s | 66.0 | + * | Blake3* | @b AVX2 | 256 | 4.4 GB/s | 8.1 | + * | Murmur3 | | 32 | 3.9 GB/s | 56.1 | + * | SipHash* | | 64 | 3.0 GB/s | 43.2 | + * | Blake3* | @b SSE2 | 256 | 2.4 GB/s | 8.1 | + * | HighwayHash | | 64 | 1.4 GB/s | 6.0 | + * | FNV64 | | 64 | 1.2 GB/s | 62.7 | + * | Blake2* | | 256 | 1.1 GB/s | 5.1 | + * | SHA1* | | 160 | 0.8 GB/s | 5.6 | + * | MD5* | | 128 | 0.6 GB/s | 7.8 | + * @note + * - Hashes which require a specific ISA extension are noted. SSE2 is also noted, + * even though it is mandatory on x64. + * - Hashes with an asterisk are cryptographic. Note that MD5 is non-cryptographic + * by modern standards. + * - Small data velocity is a rough average of algorithm's efficiency for small + * data. For more accurate information, see the wiki. + * - More benchmarks and strength tests are found on the wiki: + * https://github.com/Cyan4973/xxHash/wiki + * + * Usage + * ------ + * All xxHash variants use a similar API. Changing the algorithm is a trivial + * substitution. + * + * @pre + * For functions which take an input and length parameter, the following + * requirements are assumed: + * - The range from [`input`, `input + length`) is valid, readable memory. + * - The only exception is if the `length` is `0`, `input` may be `NULL`. + * - For C++, the objects must have the *TriviallyCopyable* property, as the + * functions access bytes directly as if it was an array of `unsigned char`. + * + * @anchor single_shot_example + * **Single Shot** + * + * These functions are stateless functions which hash a contiguous block of memory, + * immediately returning the result. They are the easiest and usually the fastest + * option. + * + * XXH32(), XXH64(), XXH3_64bits(), XXH3_128bits() + * + * @code{.c} + * #include + * #include "xxhash.h" + * + * // Example for a function which hashes a null terminated string with XXH32(). + * XXH32_hash_t hash_string(const char* string, XXH32_hash_t seed) + * { + * // NULL pointers are only valid if the length is zero + * size_t length = (string == NULL) ? 0 : strlen(string); + * return XXH32(string, length, seed); + * } + * @endcode + * + * @anchor streaming_example + * **Streaming** + * + * These groups of functions allow incremental hashing of unknown size, even + * more than what would fit in a size_t. + * + * XXH32_reset(), XXH64_reset(), XXH3_64bits_reset(), XXH3_128bits_reset() + * + * @code{.c} + * #include + * #include + * #include "xxhash.h" + * // Example for a function which hashes a FILE incrementally with XXH3_64bits(). + * XXH64_hash_t hashFile(FILE* f) + * { + * // Allocate a state struct. Do not just use malloc() or new. + * XXH3_state_t* state = XXH3_createState(); + * assert(state != NULL && "Out of memory!"); + * // Reset the state to start a new hashing session. + * XXH3_64bits_reset(state); + * char buffer[4096]; + * size_t count; + * // Read the file in chunks + * while ((count = fread(buffer, 1, sizeof(buffer), f)) != 0) { + * // Run update() as many times as necessary to process the data + * XXH3_64bits_update(state, buffer, count); + * } + * // Retrieve the finalized hash. This will not change the state. + * XXH64_hash_t result = XXH3_64bits_digest(state); + * // Free the state. Do not use free(). + * XXH3_freeState(state); + * return result; + * } + * @endcode + * + * @file xxhash.h + * xxHash prototypes and implementation + */ #if defined (__cplusplus) extern "C" { @@ -79,21 +177,80 @@ extern "C" { * INLINE mode ******************************/ /*! - * XXH_INLINE_ALL (and XXH_PRIVATE_API) + * @defgroup public Public API + * Contains details on the public xxHash functions. + * @{ + */ +#ifdef XXH_DOXYGEN +/*! + * @brief Gives access to internal state declaration, required for static allocation. + * + * Incompatible with dynamic linking, due to risks of ABI changes. + * + * Usage: + * @code{.c} + * #define XXH_STATIC_LINKING_ONLY + * #include "xxhash.h" + * @endcode + */ +# define XXH_STATIC_LINKING_ONLY +/* Do not undef XXH_STATIC_LINKING_ONLY for Doxygen */ + +/*! + * @brief Gives access to internal definitions. + * + * Usage: + * @code{.c} + * #define XXH_STATIC_LINKING_ONLY + * #define XXH_IMPLEMENTATION + * #include "xxhash.h" + * @endcode + */ +# define XXH_IMPLEMENTATION +/* Do not undef XXH_IMPLEMENTATION for Doxygen */ + +/*! + * @brief Exposes the implementation and marks all functions as `inline`. + * * Use these build macros to inline xxhash into the target unit. * Inlining improves performance on small inputs, especially when the length is * expressed as a compile-time constant: * - * https://fastcompression.blogspot.com/2018/03/xxhash-for-small-keys-impressive-power.html + * https://fastcompression.blogspot.com/2018/03/xxhash-for-small-keys-impressive-power.html * * It also keeps xxHash symbols private to the unit, so they are not exported. * * Usage: + * @code{.c} * #define XXH_INLINE_ALL * #include "xxhash.h" - * + * @endcode * Do not compile and link xxhash.o as a separate object, as it is not useful. */ +# define XXH_INLINE_ALL +# undef XXH_INLINE_ALL +/*! + * @brief Exposes the implementation without marking functions as inline. + */ +# define XXH_PRIVATE_API +# undef XXH_PRIVATE_API +/*! + * @brief Emulate a namespace by transparently prefixing all symbols. + * + * If you want to include _and expose_ xxHash functions from within your own + * library, but also want to avoid symbol collisions with other libraries which + * may also include xxHash, you can use @ref XXH_NAMESPACE to automatically prefix + * any public symbol from xxhash library with the value of @ref XXH_NAMESPACE + * (therefore, avoid empty or numeric values). + * + * Note that no change is required within the calling program as long as it + * includes `xxhash.h`: Regular symbol names will be automatically translated + * by this header. + */ +# define XXH_NAMESPACE /* YOUR NAME HERE */ +# undef XXH_NAMESPACE +#endif + #if (defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API)) \ && !defined(XXH_INLINE_ALL_31684351384) /* this section should be traversed only once */ @@ -116,29 +273,80 @@ extern "C" { /* * This part deals with the special case where a unit wants to inline xxHash, - * but "xxhash.h" has previously been included without XXH_INLINE_ALL, such - * as part of some previously included *.h header file. + * but "xxhash.h" has previously been included without XXH_INLINE_ALL, + * such as part of some previously included *.h header file. * Without further action, the new include would just be ignored, * and functions would effectively _not_ be inlined (silent failure). * The following macros solve this situation by prefixing all inlined names, * avoiding naming collision with previous inclusions. */ -# ifdef XXH_NAMESPACE -# error "XXH_INLINE_ALL with XXH_NAMESPACE is not supported" - /* - * Note: Alternative: #undef all symbols (it's a pretty large list). - * Without #error: it compiles, but functions are actually not inlined. - */ -# endif + /* Before that, we unconditionally #undef all symbols, + * in case they were already defined with XXH_NAMESPACE. + * They will then be redefined for XXH_INLINE_ALL + */ +# undef XXH_versionNumber + /* XXH32 */ +# undef XXH32 +# undef XXH32_createState +# undef XXH32_freeState +# undef XXH32_reset +# undef XXH32_update +# undef XXH32_digest +# undef XXH32_copyState +# undef XXH32_canonicalFromHash +# undef XXH32_hashFromCanonical + /* XXH64 */ +# undef XXH64 +# undef XXH64_createState +# undef XXH64_freeState +# undef XXH64_reset +# undef XXH64_update +# undef XXH64_digest +# undef XXH64_copyState +# undef XXH64_canonicalFromHash +# undef XXH64_hashFromCanonical + /* XXH3_64bits */ +# undef XXH3_64bits +# undef XXH3_64bits_withSecret +# undef XXH3_64bits_withSeed +# undef XXH3_64bits_withSecretandSeed +# undef XXH3_createState +# undef XXH3_freeState +# undef XXH3_copyState +# undef XXH3_64bits_reset +# undef XXH3_64bits_reset_withSeed +# undef XXH3_64bits_reset_withSecret +# undef XXH3_64bits_update +# undef XXH3_64bits_digest +# undef XXH3_generateSecret + /* XXH3_128bits */ +# undef XXH128 +# undef XXH3_128bits +# undef XXH3_128bits_withSeed +# undef XXH3_128bits_withSecret +# undef XXH3_128bits_reset +# undef XXH3_128bits_reset_withSeed +# undef XXH3_128bits_reset_withSecret +# undef XXH3_128bits_reset_withSecretandSeed +# undef XXH3_128bits_update +# undef XXH3_128bits_digest +# undef XXH128_isEqual +# undef XXH128_cmp +# undef XXH128_canonicalFromHash +# undef XXH128_hashFromCanonical + /* Finally, free the namespace itself */ +# undef XXH_NAMESPACE + + /* employ the namespace for XXH_INLINE_ALL */ # define XXH_NAMESPACE XXH_INLINE_ /* - * Some identifiers (enums, type names) are not symbols, but they must - * still be renamed to avoid redeclaration. + * Some identifiers (enums, type names) are not symbols, + * but they must nonetheless be renamed to avoid redeclaration. * Alternative solution: do not redeclare them. - * However, this requires some #ifdefs, and is a more dispersed action. - * Meanwhile, renaming can be achieved in a single block + * However, this requires some #ifdefs, and has a more dispersed impact. + * Meanwhile, renaming can be achieved in a single place. */ -# define XXH_IPREF(Id) XXH_INLINE_ ## Id +# define XXH_IPREF(Id) XXH_NAMESPACE ## Id # define XXH_OK XXH_IPREF(XXH_OK) # define XXH_ERROR XXH_IPREF(XXH_ERROR) # define XXH_errorcode XXH_IPREF(XXH_errorcode) @@ -157,15 +365,13 @@ extern "C" { # undef XXHASH_H_STATIC_13879238742 #endif /* XXH_INLINE_ALL || XXH_PRIVATE_API */ - - /* **************************************************************** * Stable API *****************************************************************/ #ifndef XXHASH_H_5627135585666179 #define XXHASH_H_5627135585666179 1 -/* specific declaration modes for Windows */ +/*! @brief Marks a global symbol. */ #if !defined(XXH_INLINE_ALL) && !defined(XXH_PRIVATE_API) # if defined(WIN32) && defined(_MSC_VER) && (defined(XXH_IMPORT) || defined(XXH_EXPORT)) # ifdef XXH_EXPORT @@ -178,19 +384,6 @@ extern "C" { # endif #endif -/*! - * XXH_NAMESPACE, aka Namespace Emulation: - * - * If you want to include _and expose_ xxHash functions from within your own - * library, but also want to avoid symbol collisions with other libraries which - * may also include xxHash, you can use XXH_NAMESPACE to automatically prefix - * any public symbol from xxhash library with the value of XXH_NAMESPACE - * (therefore, avoid empty or numeric values). - * - * Note that no change is required within the calling program as long as it - * includes `xxhash.h`: Regular symbol names will be automatically translated - * by this header. - */ #ifdef XXH_NAMESPACE # define XXH_CAT(A,B) A##B # define XXH_NAME2(A,B) XXH_CAT(A,B) @@ -219,23 +412,28 @@ extern "C" { # define XXH3_64bits XXH_NAME2(XXH_NAMESPACE, XXH3_64bits) # define XXH3_64bits_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_withSecret) # define XXH3_64bits_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_withSeed) +# define XXH3_64bits_withSecretandSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_withSecretandSeed) # define XXH3_createState XXH_NAME2(XXH_NAMESPACE, XXH3_createState) # define XXH3_freeState XXH_NAME2(XXH_NAMESPACE, XXH3_freeState) # define XXH3_copyState XXH_NAME2(XXH_NAMESPACE, XXH3_copyState) # define XXH3_64bits_reset XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset) # define XXH3_64bits_reset_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset_withSeed) # define XXH3_64bits_reset_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset_withSecret) +# define XXH3_64bits_reset_withSecretandSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset_withSecretandSeed) # define XXH3_64bits_update XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_update) # define XXH3_64bits_digest XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_digest) # define XXH3_generateSecret XXH_NAME2(XXH_NAMESPACE, XXH3_generateSecret) +# define XXH3_generateSecret_fromSeed XXH_NAME2(XXH_NAMESPACE, XXH3_generateSecret_fromSeed) /* XXH3_128bits */ # define XXH128 XXH_NAME2(XXH_NAMESPACE, XXH128) # define XXH3_128bits XXH_NAME2(XXH_NAMESPACE, XXH3_128bits) # define XXH3_128bits_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_withSeed) # define XXH3_128bits_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_withSecret) +# define XXH3_128bits_withSecretandSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_withSecretandSeed) # define XXH3_128bits_reset XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset) # define XXH3_128bits_reset_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset_withSeed) # define XXH3_128bits_reset_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset_withSecret) +# define XXH3_128bits_reset_withSecretandSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset_withSecretandSeed) # define XXH3_128bits_update XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_update) # define XXH3_128bits_digest XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_digest) # define XXH128_isEqual XXH_NAME2(XXH_NAMESPACE, XXH128_isEqual) @@ -245,61 +443,140 @@ extern "C" { #endif +/* ************************************* +* Compiler specifics +***************************************/ + +/* specific declaration modes for Windows */ +#if !defined(XXH_INLINE_ALL) && !defined(XXH_PRIVATE_API) +# if defined(WIN32) && defined(_MSC_VER) && (defined(XXH_IMPORT) || defined(XXH_EXPORT)) +# ifdef XXH_EXPORT +# define XXH_PUBLIC_API __declspec(dllexport) +# elif XXH_IMPORT +# define XXH_PUBLIC_API __declspec(dllimport) +# endif +# else +# define XXH_PUBLIC_API /* do nothing */ +# endif +#endif + +#if defined (__GNUC__) +# define XXH_CONSTF __attribute__((const)) +# define XXH_PUREF __attribute__((pure)) +# define XXH_MALLOCF __attribute__((malloc)) +#else +# define XXH_CONSTF /* disable */ +# define XXH_PUREF +# define XXH_MALLOCF +#endif + /* ************************************* * Version ***************************************/ #define XXH_VERSION_MAJOR 0 #define XXH_VERSION_MINOR 8 -#define XXH_VERSION_RELEASE 0 +#define XXH_VERSION_RELEASE 2 +/*! @brief Version number, encoded as two digits each */ #define XXH_VERSION_NUMBER (XXH_VERSION_MAJOR *100*100 + XXH_VERSION_MINOR *100 + XXH_VERSION_RELEASE) -XXH_PUBLIC_API unsigned XXH_versionNumber (void); + +/*! + * @brief Obtains the xxHash version. + * + * This is mostly useful when xxHash is compiled as a shared library, + * since the returned value comes from the library, as opposed to header file. + * + * @return @ref XXH_VERSION_NUMBER of the invoked library. + */ +XXH_PUBLIC_API XXH_CONSTF unsigned XXH_versionNumber (void); /* **************************** -* Definitions +* Common basic types ******************************/ #include /* size_t */ -typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode; +/*! + * @brief Exit code for the streaming API. + */ +typedef enum { + XXH_OK = 0, /*!< OK */ + XXH_ERROR /*!< Error */ +} XXH_errorcode; /*-********************************************************************** * 32-bit hash ************************************************************************/ -#if !defined (__VMS) \ +#if defined(XXH_DOXYGEN) /* Don't show include */ +/*! + * @brief An unsigned 32-bit integer. + * + * Not necessarily defined to `uint32_t` but functionally equivalent. + */ +typedef uint32_t XXH32_hash_t; + +#elif !defined (__VMS) \ && (defined (__cplusplus) \ || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) # include typedef uint32_t XXH32_hash_t; + #else # include # if UINT_MAX == 0xFFFFFFFFUL typedef unsigned int XXH32_hash_t; +# elif ULONG_MAX == 0xFFFFFFFFUL + typedef unsigned long XXH32_hash_t; # else -# if ULONG_MAX == 0xFFFFFFFFUL - typedef unsigned long XXH32_hash_t; -# else -# error "unsupported platform: need a 32-bit type" -# endif +# error "unsupported platform: need a 32-bit type" # endif #endif /*! - * XXH32(): - * Calculate the 32-bit hash of sequence "length" bytes stored at memory address "input". - * The memory between input & input+length must be valid (allocated and read-accessible). - * "seed" can be used to alter the result predictably. - * Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark): 5.4 GB/s + * @} + * + * @defgroup XXH32_family XXH32 family + * @ingroup public + * Contains functions used in the classic 32-bit xxHash algorithm. + * + * @note + * XXH32 is useful for older platforms, with no or poor 64-bit performance. + * Note that the @ref XXH3_family provides competitive speed for both 32-bit + * and 64-bit systems, and offers true 64/128 bit hash results. * - * Note: XXH3 provides competitive speed for both 32-bit and 64-bit systems, - * and offers true 64/128 bit hash results. It provides a superior level of - * dispersion, and greatly reduces the risks of collisions. + * @see @ref XXH64_family, @ref XXH3_family : Other xxHash families + * @see @ref XXH32_impl for implementation details + * @{ */ -XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t length, XXH32_hash_t seed); -/******* Streaming *******/ +/*! + * @brief Calculates the 32-bit hash of @p input using xxHash32. + * + * Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark): 5.4 GB/s + * + * See @ref single_shot_example "Single Shot Example" for an example. + * + * @param input The block of data to be hashed, at least @p length bytes in size. + * @param length The length of @p input, in bytes. + * @param seed The 32-bit seed to alter the hash's output predictably. + * + * @pre + * The memory between @p input and @p input + @p length must be valid, + * readable, contiguous memory. However, if @p length is `0`, @p input may be + * `NULL`. In C++, this also must be *TriviallyCopyable*. + * + * @return The calculated 32-bit hash value. + * + * @see + * XXH64(), XXH3_64bits_withSeed(), XXH3_128bits_withSeed(), XXH128(): + * Direct equivalents for the other variants of xxHash. + * @see + * XXH32_createState(), XXH32_update(), XXH32_digest(): Streaming version. + */ +XXH_PUBLIC_API XXH_PUREF XXH32_hash_t XXH32 (const void* input, size_t length, XXH32_hash_t seed); -/* - * Streaming functions generate the xxHash value from an incrememtal input. +#ifndef XXH_NO_STREAM +/*! + * Streaming functions generate the xxHash value from an incremental input. * This method is slower than single-call functions, due to state management. * For small inputs, prefer `XXH32()` and `XXH64()`, which are better optimized. * @@ -319,16 +596,94 @@ XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t length, XXH32_hash_ * digest, and generate new hash values later on by invoking `XXH*_digest()`. * * When done, release the state using `XXH*_freeState()`. + * + * @see streaming_example at the top of @ref xxhash.h for an example. + */ + +/*! + * @typedef struct XXH32_state_s XXH32_state_t + * @brief The opaque state struct for the XXH32 streaming API. + * + * @see XXH32_state_s for details. */ +typedef struct XXH32_state_s XXH32_state_t; -typedef struct XXH32_state_s XXH32_state_t; /* incomplete type */ -XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void); +/*! + * @brief Allocates an @ref XXH32_state_t. + * + * Must be freed with XXH32_freeState(). + * @return An allocated XXH32_state_t on success, `NULL` on failure. + */ +XXH_PUBLIC_API XXH_MALLOCF XXH32_state_t* XXH32_createState(void); +/*! + * @brief Frees an @ref XXH32_state_t. + * + * Must be allocated with XXH32_createState(). + * @param statePtr A pointer to an @ref XXH32_state_t allocated with @ref XXH32_createState(). + * @return XXH_OK. + */ XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr); +/*! + * @brief Copies one @ref XXH32_state_t to another. + * + * @param dst_state The state to copy to. + * @param src_state The state to copy from. + * @pre + * @p dst_state and @p src_state must not be `NULL` and must not overlap. + */ XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dst_state, const XXH32_state_t* src_state); +/*! + * @brief Resets an @ref XXH32_state_t to begin a new hash. + * + * This function resets and seeds a state. Call it before @ref XXH32_update(). + * + * @param statePtr The state struct to reset. + * @param seed The 32-bit seed to alter the hash result predictably. + * + * @pre + * @p statePtr must not be `NULL`. + * + * @return @ref XXH_OK on success, @ref XXH_ERROR on failure. + */ XXH_PUBLIC_API XXH_errorcode XXH32_reset (XXH32_state_t* statePtr, XXH32_hash_t seed); + +/*! + * @brief Consumes a block of @p input to an @ref XXH32_state_t. + * + * Call this to incrementally consume blocks of data. + * + * @param statePtr The state struct to update. + * @param input The block of data to be hashed, at least @p length bytes in size. + * @param length The length of @p input, in bytes. + * + * @pre + * @p statePtr must not be `NULL`. + * @pre + * The memory between @p input and @p input + @p length must be valid, + * readable, contiguous memory. However, if @p length is `0`, @p input may be + * `NULL`. In C++, this also must be *TriviallyCopyable*. + * + * @return @ref XXH_OK on success, @ref XXH_ERROR on failure. + */ XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* statePtr, const void* input, size_t length); -XXH_PUBLIC_API XXH32_hash_t XXH32_digest (const XXH32_state_t* statePtr); + +/*! + * @brief Returns the calculated hash value from an @ref XXH32_state_t. + * + * @note + * Calling XXH32_digest() will not affect @p statePtr, so you can update, + * digest, and update again. + * + * @param statePtr The state struct to calculate the hash from. + * + * @pre + * @p statePtr must not be `NULL`. + * + * @return The calculated xxHash32 value from that state. + */ +XXH_PUBLIC_API XXH_PUREF XXH32_hash_t XXH32_digest (const XXH32_state_t* statePtr); +#endif /* !XXH_NO_STREAM */ /******* Canonical representation *******/ @@ -351,62 +706,300 @@ XXH_PUBLIC_API XXH32_hash_t XXH32_digest (const XXH32_state_t* statePtr); * canonical format. */ -typedef struct { unsigned char digest[4]; } XXH32_canonical_t; +/*! + * @brief Canonical (big endian) representation of @ref XXH32_hash_t. + */ +typedef struct { + unsigned char digest[4]; /*!< Hash bytes, big endian */ +} XXH32_canonical_t; + +/*! + * @brief Converts an @ref XXH32_hash_t to a big endian @ref XXH32_canonical_t. + * + * @param dst The @ref XXH32_canonical_t pointer to be stored to. + * @param hash The @ref XXH32_hash_t to be converted. + * + * @pre + * @p dst must not be `NULL`. + */ XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash); -XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src); + +/*! + * @brief Converts an @ref XXH32_canonical_t to a native @ref XXH32_hash_t. + * + * @param src The @ref XXH32_canonical_t to convert. + * + * @pre + * @p src must not be `NULL`. + * + * @return The converted hash. + */ +XXH_PUBLIC_API XXH_PUREF XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src); + + +/*! @cond Doxygen ignores this part */ +#ifdef __has_attribute +# define XXH_HAS_ATTRIBUTE(x) __has_attribute(x) +#else +# define XXH_HAS_ATTRIBUTE(x) 0 +#endif +/*! @endcond */ + +/*! @cond Doxygen ignores this part */ +/* + * C23 __STDC_VERSION__ number hasn't been specified yet. For now + * leave as `201711L` (C17 + 1). + * TODO: Update to correct value when its been specified. + */ +#define XXH_C23_VN 201711L +/*! @endcond */ + +/*! @cond Doxygen ignores this part */ +/* C-language Attributes are added in C23. */ +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= XXH_C23_VN) && defined(__has_c_attribute) +# define XXH_HAS_C_ATTRIBUTE(x) __has_c_attribute(x) +#else +# define XXH_HAS_C_ATTRIBUTE(x) 0 +#endif +/*! @endcond */ + +/*! @cond Doxygen ignores this part */ +#if defined(__cplusplus) && defined(__has_cpp_attribute) +# define XXH_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) +#else +# define XXH_HAS_CPP_ATTRIBUTE(x) 0 +#endif +/*! @endcond */ + +/*! @cond Doxygen ignores this part */ +/* + * Define XXH_FALLTHROUGH macro for annotating switch case with the 'fallthrough' attribute + * introduced in CPP17 and C23. + * CPP17 : https://en.cppreference.com/w/cpp/language/attributes/fallthrough + * C23 : https://en.cppreference.com/w/c/language/attributes/fallthrough + */ +#if XXH_HAS_C_ATTRIBUTE(fallthrough) || XXH_HAS_CPP_ATTRIBUTE(fallthrough) +# define XXH_FALLTHROUGH [[fallthrough]] +#elif XXH_HAS_ATTRIBUTE(__fallthrough__) +# define XXH_FALLTHROUGH __attribute__ ((__fallthrough__)) +#else +# define XXH_FALLTHROUGH /* fallthrough */ +#endif +/*! @endcond */ + +/*! @cond Doxygen ignores this part */ +/* + * Define XXH_NOESCAPE for annotated pointers in public API. + * https://clang.llvm.org/docs/AttributeReference.html#noescape + * As of writing this, only supported by clang. + */ +#if XXH_HAS_ATTRIBUTE(noescape) +# define XXH_NOESCAPE __attribute__((noescape)) +#else +# define XXH_NOESCAPE +#endif +/*! @endcond */ +/*! + * @} + * @ingroup public + * @{ + */ + #ifndef XXH_NO_LONG_LONG /*-********************************************************************** * 64-bit hash ************************************************************************/ -#if !defined (__VMS) \ +#if defined(XXH_DOXYGEN) /* don't include */ +/*! + * @brief An unsigned 64-bit integer. + * + * Not necessarily defined to `uint64_t` but functionally equivalent. + */ +typedef uint64_t XXH64_hash_t; +#elif !defined (__VMS) \ && (defined (__cplusplus) \ || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) -# include - typedef uint64_t XXH64_hash_t; +# include + typedef uint64_t XXH64_hash_t; #else - /* the following type must have a width of 64-bit */ - typedef unsigned long long XXH64_hash_t; +# include +# if defined(__LP64__) && ULONG_MAX == 0xFFFFFFFFFFFFFFFFULL + /* LP64 ABI says uint64_t is unsigned long */ + typedef unsigned long XXH64_hash_t; +# else + /* the following type must have a width of 64-bit */ + typedef unsigned long long XXH64_hash_t; +# endif #endif /*! - * XXH64(): - * Returns the 64-bit hash of sequence of length @length stored at memory - * address @input. - * @seed can be used to alter the result predictably. + * @} + * + * @defgroup XXH64_family XXH64 family + * @ingroup public + * @{ + * Contains functions used in the classic 64-bit xxHash algorithm. + * + * @note + * XXH3 provides competitive speed for both 32-bit and 64-bit systems, + * and offers true 64/128 bit hash results. + * It provides better speed for systems with vector processing capabilities. + */ + +/*! + * @brief Calculates the 64-bit hash of @p input using xxHash64. * * This function usually runs faster on 64-bit systems, but slower on 32-bit * systems (see benchmark). * - * Note: XXH3 provides competitive speed for both 32-bit and 64-bit systems, - * and offers true 64/128 bit hash results. It provides a superior level of - * dispersion, and greatly reduces the risks of collisions. + * @param input The block of data to be hashed, at least @p length bytes in size. + * @param length The length of @p input, in bytes. + * @param seed The 64-bit seed to alter the hash's output predictably. + * + * @pre + * The memory between @p input and @p input + @p length must be valid, + * readable, contiguous memory. However, if @p length is `0`, @p input may be + * `NULL`. In C++, this also must be *TriviallyCopyable*. + * + * @return The calculated 64-bit hash. + * + * @see + * XXH32(), XXH3_64bits_withSeed(), XXH3_128bits_withSeed(), XXH128(): + * Direct equivalents for the other variants of xxHash. + * @see + * XXH64_createState(), XXH64_update(), XXH64_digest(): Streaming version. */ -XXH_PUBLIC_API XXH64_hash_t XXH64 (const void* input, size_t length, XXH64_hash_t seed); +XXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH64(XXH_NOESCAPE const void* input, size_t length, XXH64_hash_t seed); /******* Streaming *******/ +#ifndef XXH_NO_STREAM +/*! + * @brief The opaque state struct for the XXH64 streaming API. + * + * @see XXH64_state_s for details. + */ typedef struct XXH64_state_s XXH64_state_t; /* incomplete type */ -XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void); + +/*! + * @brief Allocates an @ref XXH64_state_t. + * + * Must be freed with XXH64_freeState(). + * @return An allocated XXH64_state_t on success, `NULL` on failure. + */ +XXH_PUBLIC_API XXH_MALLOCF XXH64_state_t* XXH64_createState(void); + +/*! + * @brief Frees an @ref XXH64_state_t. + * + * Must be allocated with XXH64_createState(). + * @param statePtr A pointer to an @ref XXH64_state_t allocated with @ref XXH64_createState(). + * @return XXH_OK. + */ XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr); -XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dst_state, const XXH64_state_t* src_state); -XXH_PUBLIC_API XXH_errorcode XXH64_reset (XXH64_state_t* statePtr, XXH64_hash_t seed); -XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* statePtr, const void* input, size_t length); -XXH_PUBLIC_API XXH64_hash_t XXH64_digest (const XXH64_state_t* statePtr); +/*! + * @brief Copies one @ref XXH64_state_t to another. + * + * @param dst_state The state to copy to. + * @param src_state The state to copy from. + * @pre + * @p dst_state and @p src_state must not be `NULL` and must not overlap. + */ +XXH_PUBLIC_API void XXH64_copyState(XXH_NOESCAPE XXH64_state_t* dst_state, const XXH64_state_t* src_state); + +/*! + * @brief Resets an @ref XXH64_state_t to begin a new hash. + * + * This function resets and seeds a state. Call it before @ref XXH64_update(). + * + * @param statePtr The state struct to reset. + * @param seed The 64-bit seed to alter the hash result predictably. + * + * @pre + * @p statePtr must not be `NULL`. + * + * @return @ref XXH_OK on success, @ref XXH_ERROR on failure. + */ +XXH_PUBLIC_API XXH_errorcode XXH64_reset (XXH_NOESCAPE XXH64_state_t* statePtr, XXH64_hash_t seed); + +/*! + * @brief Consumes a block of @p input to an @ref XXH64_state_t. + * + * Call this to incrementally consume blocks of data. + * + * @param statePtr The state struct to update. + * @param input The block of data to be hashed, at least @p length bytes in size. + * @param length The length of @p input, in bytes. + * + * @pre + * @p statePtr must not be `NULL`. + * @pre + * The memory between @p input and @p input + @p length must be valid, + * readable, contiguous memory. However, if @p length is `0`, @p input may be + * `NULL`. In C++, this also must be *TriviallyCopyable*. + * + * @return @ref XXH_OK on success, @ref XXH_ERROR on failure. + */ +XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH_NOESCAPE XXH64_state_t* statePtr, XXH_NOESCAPE const void* input, size_t length); +/*! + * @brief Returns the calculated hash value from an @ref XXH64_state_t. + * + * @note + * Calling XXH64_digest() will not affect @p statePtr, so you can update, + * digest, and update again. + * + * @param statePtr The state struct to calculate the hash from. + * + * @pre + * @p statePtr must not be `NULL`. + * + * @return The calculated xxHash64 value from that state. + */ +XXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH64_digest (XXH_NOESCAPE const XXH64_state_t* statePtr); +#endif /* !XXH_NO_STREAM */ /******* Canonical representation *******/ + +/*! + * @brief Canonical (big endian) representation of @ref XXH64_hash_t. + */ typedef struct { unsigned char digest[sizeof(XXH64_hash_t)]; } XXH64_canonical_t; -XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash); -XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src); +/*! + * @brief Converts an @ref XXH64_hash_t to a big endian @ref XXH64_canonical_t. + * + * @param dst The @ref XXH64_canonical_t pointer to be stored to. + * @param hash The @ref XXH64_hash_t to be converted. + * + * @pre + * @p dst must not be `NULL`. + */ +XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH_NOESCAPE XXH64_canonical_t* dst, XXH64_hash_t hash); -/*-********************************************************************** -* XXH3 64-bit variant -************************************************************************/ +/*! + * @brief Converts an @ref XXH64_canonical_t to a native @ref XXH64_hash_t. + * + * @param src The @ref XXH64_canonical_t to convert. + * + * @pre + * @p src must not be `NULL`. + * + * @return The converted hash. + */ +XXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH64_hashFromCanonical(XXH_NOESCAPE const XXH64_canonical_t* src); + +#ifndef XXH_NO_XXH3 -/* ************************************************************************ - * XXH3 is a new hash algorithm featuring: +/*! + * @} + * ************************************************************************ + * @defgroup XXH3_family XXH3 family + * @ingroup public + * @{ + * + * XXH3 is a more recent hash algorithm featuring: * - Improved speed for both small and large inputs * - True 64-bit and 128-bit outputs * - SIMD acceleration @@ -416,102 +1009,172 @@ XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src * * https://fastcompression.blogspot.com/2019/03/presenting-xxh3.html * - * In general, expect XXH3 to run about ~2x faster on large inputs and >3x - * faster on small ones compared to XXH64, though exact differences depend on - * the platform. - * - * The algorithm is portable: Like XXH32 and XXH64, it generates the same hash - * on all platforms. + * Compared to XXH64, expect XXH3 to run approximately + * ~2x faster on large inputs and >3x faster on small ones, + * exact differences vary depending on platform. * - * It benefits greatly from SIMD and 64-bit arithmetic, but does not require it. - * - * Almost all 32-bit and 64-bit targets that can run XXH32 smoothly can run - * XXH3 at competitive speeds, even if XXH64 runs slowly. Further details are + * XXH3's speed benefits greatly from SIMD and 64-bit arithmetic, + * but does not require it. + * Most 32-bit and 64-bit targets that can run XXH32 smoothly can run XXH3 + * at competitive speeds, even without vector support. Further details are * explained in the implementation. * - * Optimized implementations are provided for AVX512, AVX2, SSE2, NEON, POWER8, - * ZVector and scalar targets. This can be controlled with the XXH_VECTOR macro. + * XXH3 has a fast scalar implementation, but it also includes accelerated SIMD + * implementations for many common platforms: + * - AVX512 + * - AVX2 + * - SSE2 + * - ARM NEON + * - WebAssembly SIMD128 + * - POWER8 VSX + * - s390x ZVector + * This can be controlled via the @ref XXH_VECTOR macro, but it automatically + * selects the best version according to predefined macros. For the x86 family, an + * automatic runtime dispatcher is included separately in @ref xxh_x86dispatch.c. + * + * XXH3 implementation is portable: + * it has a generic C90 formulation that can be compiled on any platform, + * all implementations generate exactly the same hash value on all platforms. + * Starting from v0.8.0, it's also labelled "stable", meaning that + * any future version will also generate the same hash value. * * XXH3 offers 2 variants, _64bits and _128bits. - * When only 64 bits are needed, prefer calling the _64bits variant, as it - * reduces the amount of mixing, resulting in faster speed on small inputs. * + * When only 64 bits are needed, prefer invoking the _64bits variant, as it + * reduces the amount of mixing, resulting in faster speed on small inputs. * It's also generally simpler to manipulate a scalar return type than a struct. * - * The 128-bit version adds additional strength, but it is slightly slower. - * - * The XXH3 algorithm is still in development. - * The results it produces may still change in future versions. + * The API supports one-shot hashing, streaming mode, and custom secrets. + */ +/*-********************************************************************** +* XXH3 64-bit variant +************************************************************************/ + +/*! + * @brief 64-bit unseeded variant of XXH3. + * + * This is equivalent to @ref XXH3_64bits_withSeed() with a seed of 0, however + * it may have slightly better performance due to constant propagation of the + * defaults. + * + * @see + * XXH32(), XXH64(), XXH3_128bits(): equivalent for the other xxHash algorithms + * @see + * XXH3_64bits_withSeed(), XXH3_64bits_withSecret(): other seeding variants + * @see + * XXH3_64bits_reset(), XXH3_64bits_update(), XXH3_64bits_digest(): Streaming version. + */ +XXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH3_64bits(XXH_NOESCAPE const void* input, size_t length); + +/*! + * @brief 64-bit seeded variant of XXH3 * - * Results produced by v0.7.x are not comparable with results from v0.7.y. - * However, the API is completely stable, and it can safely be used for - * ephemeral data (local sessions). + * This variant generates a custom secret on the fly based on default secret + * altered using the `seed` value. * - * Avoid storing values in long-term storage until the algorithm is finalized. - * XXH3's return values will be officially finalized upon reaching v0.8.0. + * While this operation is decently fast, note that it's not completely free. * - * After which, return values of XXH3 and XXH128 will no longer change in - * future versions. + * @note + * seed == 0 produces the same results as @ref XXH3_64bits(). * - * The API supports one-shot hashing, streaming mode, and custom secrets. + * @param input The data to hash + * @param length The length + * @param seed The 64-bit seed to alter the state. */ +XXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH3_64bits_withSeed(XXH_NOESCAPE const void* input, size_t length, XXH64_hash_t seed); -/* XXH3_64bits(): - * default 64-bit variant, using default secret and default seed of 0. - * It's the fastest variant. */ -XXH_PUBLIC_API XXH64_hash_t XXH3_64bits(const void* data, size_t len); - -/* - * XXH3_64bits_withSeed(): - * This variant generates a custom secret on the fly - * based on default secret altered using the `seed` value. - * While this operation is decently fast, note that it's not completely free. - * Note: seed==0 produces the same results as XXH3_64bits(). +/*! + * The bare minimum size for a custom secret. + * + * @see + * XXH3_64bits_withSecret(), XXH3_64bits_reset_withSecret(), + * XXH3_128bits_withSecret(), XXH3_128bits_reset_withSecret(). */ -XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSeed(const void* data, size_t len, XXH64_hash_t seed); +#define XXH3_SECRET_SIZE_MIN 136 -/* - * XXH3_64bits_withSecret(): +/*! + * @brief 64-bit variant of XXH3 with a custom "secret". + * * It's possible to provide any blob of bytes as a "secret" to generate the hash. * This makes it more difficult for an external actor to prepare an intentional collision. * The main condition is that secretSize *must* be large enough (>= XXH3_SECRET_SIZE_MIN). - * However, the quality of produced hash values depends on secret's entropy. - * Technically, the secret must look like a bunch of random bytes. + * However, the quality of the secret impacts the dispersion of the hash algorithm. + * Therefore, the secret _must_ look like a bunch of random bytes. * Avoid "trivial" or structured data such as repeated sequences or a text document. - * Whenever unsure about the "randomness" of the blob of bytes, - * consider relabelling it as a "custom seed" instead, - * and employ "XXH3_generateSecret()" (see below) - * to generate a high entropy secret derived from the custom seed. + * Whenever in doubt about the "randomness" of the blob of bytes, + * consider employing "XXH3_generateSecret()" instead (see below). + * It will generate a proper high entropy secret derived from the blob of bytes. + * Another advantage of using XXH3_generateSecret() is that + * it guarantees that all bits within the initial blob of bytes + * will impact every bit of the output. + * This is not necessarily the case when using the blob of bytes directly + * because, when hashing _small_ inputs, only a portion of the secret is employed. */ -#define XXH3_SECRET_SIZE_MIN 136 -XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSecret(const void* data, size_t len, const void* secret, size_t secretSize); +XXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH3_64bits_withSecret(XXH_NOESCAPE const void* data, size_t len, XXH_NOESCAPE const void* secret, size_t secretSize); /******* Streaming *******/ +#ifndef XXH_NO_STREAM /* * Streaming requires state maintenance. * This operation costs memory and CPU. * As a consequence, streaming is slower than one-shot hashing. * For better performance, prefer one-shot functions whenever applicable. */ + +/*! + * @brief The state struct for the XXH3 streaming API. + * + * @see XXH3_state_s for details. + */ typedef struct XXH3_state_s XXH3_state_t; -XXH_PUBLIC_API XXH3_state_t* XXH3_createState(void); +XXH_PUBLIC_API XXH_MALLOCF XXH3_state_t* XXH3_createState(void); XXH_PUBLIC_API XXH_errorcode XXH3_freeState(XXH3_state_t* statePtr); -XXH_PUBLIC_API void XXH3_copyState(XXH3_state_t* dst_state, const XXH3_state_t* src_state); -/* - * XXH3_64bits_reset(): - * Initialize with default parameters. - * digest will be equivalent to `XXH3_64bits()`. +/*! + * @brief Copies one @ref XXH3_state_t to another. + * + * @param dst_state The state to copy to. + * @param src_state The state to copy from. + * @pre + * @p dst_state and @p src_state must not be `NULL` and must not overlap. */ -XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset(XXH3_state_t* statePtr); -/* - * XXH3_64bits_reset_withSeed(): - * Generate a custom secret from `seed`, and store it into `statePtr`. - * digest will be equivalent to `XXH3_64bits_withSeed()`. +XXH_PUBLIC_API void XXH3_copyState(XXH_NOESCAPE XXH3_state_t* dst_state, XXH_NOESCAPE const XXH3_state_t* src_state); + +/*! + * @brief Resets an @ref XXH3_state_t to begin a new hash. + * + * This function resets `statePtr` and generate a secret with default parameters. Call it before @ref XXH3_64bits_update(). + * Digest will be equivalent to `XXH3_64bits()`. + * + * @param statePtr The state struct to reset. + * + * @pre + * @p statePtr must not be `NULL`. + * + * @return @ref XXH_OK on success, @ref XXH_ERROR on failure. + * */ -XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSeed(XXH3_state_t* statePtr, XXH64_hash_t seed); -/* +XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset(XXH_NOESCAPE XXH3_state_t* statePtr); + +/*! + * @brief Resets an @ref XXH3_state_t with 64-bit seed to begin a new hash. + * + * This function resets `statePtr` and generate a secret from `seed`. Call it before @ref XXH3_64bits_update(). + * Digest will be equivalent to `XXH3_64bits_withSeed()`. + * + * @param statePtr The state struct to reset. + * @param seed The 64-bit seed to alter the state. + * + * @pre + * @p statePtr must not be `NULL`. + * + * @return @ref XXH_OK on success, @ref XXH_ERROR on failure. + * + */ +XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSeed(XXH_NOESCAPE XXH3_state_t* statePtr, XXH64_hash_t seed); + +/*! * XXH3_64bits_reset_withSecret(): * `secret` is referenced, it _must outlive_ the hash streaming session. * Similar to one-shot API, `secretSize` must be >= `XXH3_SECRET_SIZE_MIN`, @@ -520,10 +1183,44 @@ XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSeed(XXH3_state_t* statePtr, * When in doubt about the randomness of a candidate `secret`, * consider employing `XXH3_generateSecret()` instead (see below). */ -XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSecret(XXH3_state_t* statePtr, const void* secret, size_t secretSize); +XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSecret(XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* secret, size_t secretSize); -XXH_PUBLIC_API XXH_errorcode XXH3_64bits_update (XXH3_state_t* statePtr, const void* input, size_t length); -XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_digest (const XXH3_state_t* statePtr); +/*! + * @brief Consumes a block of @p input to an @ref XXH3_state_t. + * + * Call this to incrementally consume blocks of data. + * + * @param statePtr The state struct to update. + * @param input The block of data to be hashed, at least @p length bytes in size. + * @param length The length of @p input, in bytes. + * + * @pre + * @p statePtr must not be `NULL`. + * @pre + * The memory between @p input and @p input + @p length must be valid, + * readable, contiguous memory. However, if @p length is `0`, @p input may be + * `NULL`. In C++, this also must be *TriviallyCopyable*. + * + * @return @ref XXH_OK on success, @ref XXH_ERROR on failure. + */ +XXH_PUBLIC_API XXH_errorcode XXH3_64bits_update (XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* input, size_t length); + +/*! + * @brief Returns the calculated XXH3 64-bit hash value from an @ref XXH3_state_t. + * + * @note + * Calling XXH3_64bits_digest() will not affect @p statePtr, so you can update, + * digest, and update again. + * + * @param statePtr The state struct to calculate the hash from. + * + * @pre + * @p statePtr must not be `NULL`. + * + * @return The calculated XXH3 64-bit hash value from that state. + */ +XXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH3_64bits_digest (XXH_NOESCAPE const XXH3_state_t* statePtr); +#endif /* !XXH_NO_STREAM */ /* note : canonical representation of XXH3 is the same as XXH64 * since they both produce XXH64_hash_t values */ @@ -533,16 +1230,42 @@ XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_digest (const XXH3_state_t* statePtr); * XXH3 128-bit variant ************************************************************************/ +/*! + * @brief The return value from 128-bit hashes. + * + * Stored in little endian order, although the fields themselves are in native + * endianness. + */ typedef struct { - XXH64_hash_t low64; - XXH64_hash_t high64; + XXH64_hash_t low64; /*!< `value & 0xFFFFFFFFFFFFFFFF` */ + XXH64_hash_t high64; /*!< `value >> 64` */ } XXH128_hash_t; -XXH_PUBLIC_API XXH128_hash_t XXH3_128bits(const void* data, size_t len); -XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSeed(const void* data, size_t len, XXH64_hash_t seed); -XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSecret(const void* data, size_t len, const void* secret, size_t secretSize); +/*! + * @brief Unseeded 128-bit variant of XXH3 + * + * The 128-bit variant of XXH3 has more strength, but it has a bit of overhead + * for shorter inputs. + * + * This is equivalent to @ref XXH3_128bits_withSeed() with a seed of 0, however + * it may have slightly better performance due to constant propagation of the + * defaults. + * + * @see + * XXH32(), XXH64(), XXH3_64bits(): equivalent for the other xxHash algorithms + * @see + * XXH3_128bits_withSeed(), XXH3_128bits_withSecret(): other seeding variants + * @see + * XXH3_128bits_reset(), XXH3_128bits_update(), XXH3_128bits_digest(): Streaming version. + */ +XXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH3_128bits(XXH_NOESCAPE const void* data, size_t len); +/*! @brief Seeded 128-bit variant of XXH3. @see XXH3_64bits_withSeed(). */ +XXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH3_128bits_withSeed(XXH_NOESCAPE const void* data, size_t len, XXH64_hash_t seed); +/*! @brief Custom secret 128-bit variant of XXH3. @see XXH3_64bits_withSecret(). */ +XXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH3_128bits_withSecret(XXH_NOESCAPE const void* data, size_t len, XXH_NOESCAPE const void* secret, size_t secretSize); /******* Streaming *******/ +#ifndef XXH_NO_STREAM /* * Streaming requires state maintenance. * This operation costs memory and CPU. @@ -555,43 +1278,133 @@ XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSecret(const void* data, size_t le * All reset and streaming functions have same meaning as their 64-bit counterpart. */ -XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset(XXH3_state_t* statePtr); -XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSeed(XXH3_state_t* statePtr, XXH64_hash_t seed); -XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSecret(XXH3_state_t* statePtr, const void* secret, size_t secretSize); +/*! + * @brief Resets an @ref XXH3_state_t to begin a new hash. + * + * This function resets `statePtr` and generate a secret with default parameters. Call it before @ref XXH3_128bits_update(). + * Digest will be equivalent to `XXH3_128bits()`. + * + * @param statePtr The state struct to reset. + * + * @pre + * @p statePtr must not be `NULL`. + * + * @return @ref XXH_OK on success, @ref XXH_ERROR on failure. + * + */ +XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset(XXH_NOESCAPE XXH3_state_t* statePtr); + +/*! + * @brief Resets an @ref XXH3_state_t with 64-bit seed to begin a new hash. + * + * This function resets `statePtr` and generate a secret from `seed`. Call it before @ref XXH3_128bits_update(). + * Digest will be equivalent to `XXH3_128bits_withSeed()`. + * + * @param statePtr The state struct to reset. + * @param seed The 64-bit seed to alter the state. + * + * @pre + * @p statePtr must not be `NULL`. + * + * @return @ref XXH_OK on success, @ref XXH_ERROR on failure. + * + */ +XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSeed(XXH_NOESCAPE XXH3_state_t* statePtr, XXH64_hash_t seed); +/*! @brief Custom secret 128-bit variant of XXH3. @see XXH_64bits_reset_withSecret(). */ +XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSecret(XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* secret, size_t secretSize); + +/*! + * @brief Consumes a block of @p input to an @ref XXH3_state_t. + * + * Call this to incrementally consume blocks of data. + * + * @param statePtr The state struct to update. + * @param input The block of data to be hashed, at least @p length bytes in size. + * @param length The length of @p input, in bytes. + * + * @pre + * @p statePtr must not be `NULL`. + * @pre + * The memory between @p input and @p input + @p length must be valid, + * readable, contiguous memory. However, if @p length is `0`, @p input may be + * `NULL`. In C++, this also must be *TriviallyCopyable*. + * + * @return @ref XXH_OK on success, @ref XXH_ERROR on failure. + */ +XXH_PUBLIC_API XXH_errorcode XXH3_128bits_update (XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* input, size_t length); -XXH_PUBLIC_API XXH_errorcode XXH3_128bits_update (XXH3_state_t* statePtr, const void* input, size_t length); -XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_digest (const XXH3_state_t* statePtr); +/*! + * @brief Returns the calculated XXH3 128-bit hash value from an @ref XXH3_state_t. + * + * @note + * Calling XXH3_128bits_digest() will not affect @p statePtr, so you can update, + * digest, and update again. + * + * @param statePtr The state struct to calculate the hash from. + * + * @pre + * @p statePtr must not be `NULL`. + * + * @return The calculated XXH3 128-bit hash value from that state. + */ +XXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH3_128bits_digest (XXH_NOESCAPE const XXH3_state_t* statePtr); +#endif /* !XXH_NO_STREAM */ /* Following helper functions make it possible to compare XXH128_hast_t values. * Since XXH128_hash_t is a structure, this capability is not offered by the language. * Note: For better performance, these functions can be inlined using XXH_INLINE_ALL */ /*! - * XXH128_isEqual(): - * Return: 1 if `h1` and `h2` are equal, 0 if they are not. + * XXH128_isEqual(): + * Return: 1 if `h1` and `h2` are equal, 0 if they are not. + */ +XXH_PUBLIC_API XXH_PUREF int XXH128_isEqual(XXH128_hash_t h1, XXH128_hash_t h2); + +/*! + * @brief Compares two @ref XXH128_hash_t + * This comparator is compatible with stdlib's `qsort()`/`bsearch()`. + * + * @return: >0 if *h128_1 > *h128_2 + * =0 if *h128_1 == *h128_2 + * <0 if *h128_1 < *h128_2 + */ +XXH_PUBLIC_API XXH_PUREF int XXH128_cmp(XXH_NOESCAPE const void* h128_1, XXH_NOESCAPE const void* h128_2); + + +/******* Canonical representation *******/ +typedef struct { unsigned char digest[sizeof(XXH128_hash_t)]; } XXH128_canonical_t; + + +/*! + * @brief Converts an @ref XXH128_hash_t to a big endian @ref XXH128_canonical_t. + * + * @param dst The @ref XXH128_canonical_t pointer to be stored to. + * @param hash The @ref XXH128_hash_t to be converted. + * + * @pre + * @p dst must not be `NULL`. */ -XXH_PUBLIC_API int XXH128_isEqual(XXH128_hash_t h1, XXH128_hash_t h2); +XXH_PUBLIC_API void XXH128_canonicalFromHash(XXH_NOESCAPE XXH128_canonical_t* dst, XXH128_hash_t hash); /*! - * XXH128_cmp(): + * @brief Converts an @ref XXH128_canonical_t to a native @ref XXH128_hash_t. * - * This comparator is compatible with stdlib's `qsort()`/`bsearch()`. + * @param src The @ref XXH128_canonical_t to convert. + * + * @pre + * @p src must not be `NULL`. * - * return: >0 if *h128_1 > *h128_2 - * =0 if *h128_1 == *h128_2 - * <0 if *h128_1 < *h128_2 + * @return The converted hash. */ -XXH_PUBLIC_API int XXH128_cmp(const void* h128_1, const void* h128_2); - - -/******* Canonical representation *******/ -typedef struct { unsigned char digest[sizeof(XXH128_hash_t)]; } XXH128_canonical_t; -XXH_PUBLIC_API void XXH128_canonicalFromHash(XXH128_canonical_t* dst, XXH128_hash_t hash); -XXH_PUBLIC_API XXH128_hash_t XXH128_hashFromCanonical(const XXH128_canonical_t* src); +XXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH128_hashFromCanonical(XXH_NOESCAPE const XXH128_canonical_t* src); +#endif /* !XXH_NO_XXH3 */ #endif /* XXH_NO_LONG_LONG */ +/*! + * @} + */ #endif /* XXHASH_H_5627135585666179 */ @@ -612,36 +1425,59 @@ XXH_PUBLIC_API XXH128_hash_t XXH128_hashFromCanonical(const XXH128_canonical_t* * Never **ever** access their members directly. */ +/*! + * @internal + * @brief Structure for XXH32 streaming API. + * + * @note This is only defined when @ref XXH_STATIC_LINKING_ONLY, + * @ref XXH_INLINE_ALL, or @ref XXH_IMPLEMENTATION is defined. Otherwise it is + * an opaque type. This allows fields to safely be changed. + * + * Typedef'd to @ref XXH32_state_t. + * Do not access the members of this struct directly. + * @see XXH64_state_s, XXH3_state_s + */ struct XXH32_state_s { - XXH32_hash_t total_len_32; - XXH32_hash_t large_len; - XXH32_hash_t v1; - XXH32_hash_t v2; - XXH32_hash_t v3; - XXH32_hash_t v4; - XXH32_hash_t mem32[4]; - XXH32_hash_t memsize; - XXH32_hash_t reserved; /* never read nor write, might be removed in a future version */ + XXH32_hash_t total_len_32; /*!< Total length hashed, modulo 2^32 */ + XXH32_hash_t large_len; /*!< Whether the hash is >= 16 (handles @ref total_len_32 overflow) */ + XXH32_hash_t v[4]; /*!< Accumulator lanes */ + XXH32_hash_t mem32[4]; /*!< Internal buffer for partial reads. Treated as unsigned char[16]. */ + XXH32_hash_t memsize; /*!< Amount of data in @ref mem32 */ + XXH32_hash_t reserved; /*!< Reserved field. Do not read nor write to it. */ }; /* typedef'd to XXH32_state_t */ #ifndef XXH_NO_LONG_LONG /* defined when there is no 64-bit support */ +/*! + * @internal + * @brief Structure for XXH64 streaming API. + * + * @note This is only defined when @ref XXH_STATIC_LINKING_ONLY, + * @ref XXH_INLINE_ALL, or @ref XXH_IMPLEMENTATION is defined. Otherwise it is + * an opaque type. This allows fields to safely be changed. + * + * Typedef'd to @ref XXH64_state_t. + * Do not access the members of this struct directly. + * @see XXH32_state_s, XXH3_state_s + */ struct XXH64_state_s { - XXH64_hash_t total_len; - XXH64_hash_t v1; - XXH64_hash_t v2; - XXH64_hash_t v3; - XXH64_hash_t v4; - XXH64_hash_t mem64[4]; - XXH32_hash_t memsize; - XXH32_hash_t reserved32; /* required for padding anyway */ - XXH64_hash_t reserved64; /* never read nor write, might be removed in a future version */ + XXH64_hash_t total_len; /*!< Total length hashed. This is always 64-bit. */ + XXH64_hash_t v[4]; /*!< Accumulator lanes */ + XXH64_hash_t mem64[4]; /*!< Internal buffer for partial reads. Treated as unsigned char[32]. */ + XXH32_hash_t memsize; /*!< Amount of data in @ref mem64 */ + XXH32_hash_t reserved32; /*!< Reserved field, needed for padding anyways*/ + XXH64_hash_t reserved64; /*!< Reserved field. Do not read or write to it. */ }; /* typedef'd to XXH64_state_t */ -#if defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) /* C11+ */ +#ifndef XXH_NO_XXH3 + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) /* >= C11 */ # include # define XXH_ALIGN(n) alignas(n) +#elif defined(__cplusplus) && (__cplusplus >= 201103L) /* >= C++11 */ +/* In C++ alignas() is a keyword */ +# define XXH_ALIGN(n) alignas(n) #elif defined(__GNUC__) # define XXH_ALIGN(n) __attribute__ ((aligned(n))) #elif defined(_MSC_VER) @@ -652,35 +1488,89 @@ struct XXH64_state_s { /* Old GCC versions only accept the attribute after the type in structures. */ #if !(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) /* C11+ */ \ + && ! (defined(__cplusplus) && (__cplusplus >= 201103L)) /* >= C++11 */ \ && defined(__GNUC__) # define XXH_ALIGN_MEMBER(align, type) type XXH_ALIGN(align) #else # define XXH_ALIGN_MEMBER(align, type) XXH_ALIGN(align) type #endif +/*! + * @brief The size of the internal XXH3 buffer. + * + * This is the optimal update size for incremental hashing. + * + * @see XXH3_64b_update(), XXH3_128b_update(). + */ #define XXH3_INTERNALBUFFER_SIZE 256 + +/*! + * @internal + * @brief Default size of the secret buffer (and @ref XXH3_kSecret). + * + * This is the size used in @ref XXH3_kSecret and the seeded functions. + * + * Not to be confused with @ref XXH3_SECRET_SIZE_MIN. + */ #define XXH3_SECRET_DEFAULT_SIZE 192 + +/*! + * @internal + * @brief Structure for XXH3 streaming API. + * + * @note This is only defined when @ref XXH_STATIC_LINKING_ONLY, + * @ref XXH_INLINE_ALL, or @ref XXH_IMPLEMENTATION is defined. + * Otherwise it is an opaque type. + * Never use this definition in combination with dynamic library. + * This allows fields to safely be changed in the future. + * + * @note ** This structure has a strict alignment requirement of 64 bytes!! ** + * Do not allocate this with `malloc()` or `new`, + * it will not be sufficiently aligned. + * Use @ref XXH3_createState() and @ref XXH3_freeState(), or stack allocation. + * + * Typedef'd to @ref XXH3_state_t. + * Do never access the members of this struct directly. + * + * @see XXH3_INITSTATE() for stack initialization. + * @see XXH3_createState(), XXH3_freeState(). + * @see XXH32_state_s, XXH64_state_s + */ struct XXH3_state_s { XXH_ALIGN_MEMBER(64, XXH64_hash_t acc[8]); - /* used to store a custom secret generated from a seed */ + /*!< The 8 accumulators. See @ref XXH32_state_s::v and @ref XXH64_state_s::v */ XXH_ALIGN_MEMBER(64, unsigned char customSecret[XXH3_SECRET_DEFAULT_SIZE]); + /*!< Used to store a custom secret generated from a seed. */ XXH_ALIGN_MEMBER(64, unsigned char buffer[XXH3_INTERNALBUFFER_SIZE]); + /*!< The internal buffer. @see XXH32_state_s::mem32 */ XXH32_hash_t bufferedSize; - XXH32_hash_t reserved32; + /*!< The amount of memory in @ref buffer, @see XXH32_state_s::memsize */ + XXH32_hash_t useSeed; + /*!< Reserved field. Needed for padding on 64-bit. */ size_t nbStripesSoFar; + /*!< Number or stripes processed. */ XXH64_hash_t totalLen; + /*!< Total length hashed. 64-bit even on 32-bit targets. */ size_t nbStripesPerBlock; + /*!< Number of stripes per block. */ size_t secretLimit; + /*!< Size of @ref customSecret or @ref extSecret */ XXH64_hash_t seed; + /*!< Seed for _withSeed variants. Must be zero otherwise, @see XXH3_INITSTATE() */ XXH64_hash_t reserved64; - const unsigned char* extSecret; /* reference to external secret; - * if == NULL, use .customSecret instead */ + /*!< Reserved field. */ + const unsigned char* extSecret; + /*!< Reference to an external secret for the _withSecret variants, NULL + * for other variants. */ /* note: there may be some padding at the end due to alignment on 64 bytes */ }; /* typedef'd to XXH3_state_t */ #undef XXH_ALIGN_MEMBER -/* When the XXH3_state_t structure is merely emplaced on stack, +/*! + * @brief Initializes a stack-allocated `XXH3_state_s`. + * + * When the @ref XXH3_state_t structure is merely emplaced on stack, * it should be initialized with XXH3_INITSTATE() or a memset() * in case its first reset uses XXH3_NNbits_reset_withSeed(). * This init can be omitted if the first reset uses default or _withSecret mode. @@ -688,51 +1578,165 @@ struct XXH3_state_s { * Note that this doesn't prepare the state for a streaming operation, * it's still necessary to use XXH3_NNbits_reset*() afterwards. */ -#define XXH3_INITSTATE(XXH3_state_ptr) { (XXH3_state_ptr)->seed = 0; } +#define XXH3_INITSTATE(XXH3_state_ptr) \ + do { \ + XXH3_state_t* tmp_xxh3_state_ptr = (XXH3_state_ptr); \ + tmp_xxh3_state_ptr->seed = 0; \ + tmp_xxh3_state_ptr->extSecret = NULL; \ + } while(0) + + +/*! + * simple alias to pre-selected XXH3_128bits variant + */ +XXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH128(XXH_NOESCAPE const void* data, size_t len, XXH64_hash_t seed); /* === Experimental API === */ /* Symbols defined below must be considered tied to a specific library version. */ -/* +/*! * XXH3_generateSecret(): * * Derive a high-entropy secret from any user-defined content, named customSeed. * The generated secret can be used in combination with `*_withSecret()` functions. - * The `_withSecret()` variants are useful to provide a higher level of protection than 64-bit seed, - * as it becomes much more difficult for an external actor to guess how to impact the calculation logic. + * The `_withSecret()` variants are useful to provide a higher level of protection + * than 64-bit seed, as it becomes much more difficult for an external actor to + * guess how to impact the calculation logic. * * The function accepts as input a custom seed of any length and any content, - * and derives from it a high-entropy secret of length XXH3_SECRET_DEFAULT_SIZE - * into an already allocated buffer secretBuffer. - * The generated secret is _always_ XXH_SECRET_DEFAULT_SIZE bytes long. + * and derives from it a high-entropy secret of length @p secretSize into an + * already allocated buffer @p secretBuffer. * * The generated secret can then be used with any `*_withSecret()` variant. - * Functions `XXH3_128bits_withSecret()`, `XXH3_64bits_withSecret()`, - * `XXH3_128bits_reset_withSecret()` and `XXH3_64bits_reset_withSecret()` + * The functions @ref XXH3_128bits_withSecret(), @ref XXH3_64bits_withSecret(), + * @ref XXH3_128bits_reset_withSecret() and @ref XXH3_64bits_reset_withSecret() * are part of this list. They all accept a `secret` parameter - * which must be very long for implementation reasons (>= XXH3_SECRET_SIZE_MIN) + * which must be large enough for implementation reasons (>= @ref XXH3_SECRET_SIZE_MIN) * _and_ feature very high entropy (consist of random-looking bytes). - * These conditions can be a high bar to meet, so - * this function can be used to generate a secret of proper quality. - * - * customSeed can be anything. It can have any size, even small ones, - * and its content can be anything, even stupidly "low entropy" source such as a bunch of zeroes. - * The resulting `secret` will nonetheless provide all expected qualities. - * - * Supplying NULL as the customSeed copies the default secret into `secretBuffer`. - * When customSeedSize > 0, supplying NULL as customSeed is undefined behavior. + * These conditions can be a high bar to meet, so @ref XXH3_generateSecret() can + * be employed to ensure proper quality. + * + * @p customSeed can be anything. It can have any size, even small ones, + * and its content can be anything, even "poor entropy" sources such as a bunch + * of zeroes. The resulting `secret` will nonetheless provide all required qualities. + * + * @pre + * - @p secretSize must be >= @ref XXH3_SECRET_SIZE_MIN + * - When @p customSeedSize > 0, supplying NULL as customSeed is undefined behavior. + * + * Example code: + * @code{.c} + * #include + * #include + * #include + * #define XXH_STATIC_LINKING_ONLY // expose unstable API + * #include "xxhash.h" + * // Hashes argv[2] using the entropy from argv[1]. + * int main(int argc, char* argv[]) + * { + * char secret[XXH3_SECRET_SIZE_MIN]; + * if (argv != 3) { return 1; } + * XXH3_generateSecret(secret, sizeof(secret), argv[1], strlen(argv[1])); + * XXH64_hash_t h = XXH3_64bits_withSecret( + * argv[2], strlen(argv[2]), + * secret, sizeof(secret) + * ); + * printf("%016llx\n", (unsigned long long) h); + * } + * @endcode */ -XXH_PUBLIC_API void XXH3_generateSecret(void* secretBuffer, const void* customSeed, size_t customSeedSize); +XXH_PUBLIC_API XXH_errorcode XXH3_generateSecret(XXH_NOESCAPE void* secretBuffer, size_t secretSize, XXH_NOESCAPE const void* customSeed, size_t customSeedSize); +/*! + * @brief Generate the same secret as the _withSeed() variants. + * + * The generated secret can be used in combination with + *`*_withSecret()` and `_withSecretandSeed()` variants. + * + * Example C++ `std::string` hash class: + * @code{.cpp} + * #include + * #define XXH_STATIC_LINKING_ONLY // expose unstable API + * #include "xxhash.h" + * // Slow, seeds each time + * class HashSlow { + * XXH64_hash_t seed; + * public: + * HashSlow(XXH64_hash_t s) : seed{s} {} + * size_t operator()(const std::string& x) const { + * return size_t{XXH3_64bits_withSeed(x.c_str(), x.length(), seed)}; + * } + * }; + * // Fast, caches the seeded secret for future uses. + * class HashFast { + * unsigned char secret[XXH3_SECRET_SIZE_MIN]; + * public: + * HashFast(XXH64_hash_t s) { + * XXH3_generateSecret_fromSeed(secret, seed); + * } + * size_t operator()(const std::string& x) const { + * return size_t{ + * XXH3_64bits_withSecret(x.c_str(), x.length(), secret, sizeof(secret)) + * }; + * } + * }; + * @endcode + * @param secretBuffer A writable buffer of @ref XXH3_SECRET_SIZE_MIN bytes + * @param seed The seed to seed the state. + */ +XXH_PUBLIC_API void XXH3_generateSecret_fromSeed(XXH_NOESCAPE void* secretBuffer, XXH64_hash_t seed); -/* simple short-cut to pre-selected XXH3_128bits variant */ -XXH_PUBLIC_API XXH128_hash_t XXH128(const void* data, size_t len, XXH64_hash_t seed); - +/*! + * These variants generate hash values using either + * @p seed for "short" keys (< XXH3_MIDSIZE_MAX = 240 bytes) + * or @p secret for "large" keys (>= XXH3_MIDSIZE_MAX). + * + * This generally benefits speed, compared to `_withSeed()` or `_withSecret()`. + * `_withSeed()` has to generate the secret on the fly for "large" keys. + * It's fast, but can be perceptible for "not so large" keys (< 1 KB). + * `_withSecret()` has to generate the masks on the fly for "small" keys, + * which requires more instructions than _withSeed() variants. + * Therefore, _withSecretandSeed variant combines the best of both worlds. + * + * When @p secret has been generated by XXH3_generateSecret_fromSeed(), + * this variant produces *exactly* the same results as `_withSeed()` variant, + * hence offering only a pure speed benefit on "large" input, + * by skipping the need to regenerate the secret for every large input. + * + * Another usage scenario is to hash the secret to a 64-bit hash value, + * for example with XXH3_64bits(), which then becomes the seed, + * and then employ both the seed and the secret in _withSecretandSeed(). + * On top of speed, an added benefit is that each bit in the secret + * has a 50% chance to swap each bit in the output, via its impact to the seed. + * + * This is not guaranteed when using the secret directly in "small data" scenarios, + * because only portions of the secret are employed for small data. + */ +XXH_PUBLIC_API XXH_PUREF XXH64_hash_t +XXH3_64bits_withSecretandSeed(XXH_NOESCAPE const void* data, size_t len, + XXH_NOESCAPE const void* secret, size_t secretSize, + XXH64_hash_t seed); +/*! @copydoc XXH3_64bits_withSecretandSeed() */ +XXH_PUBLIC_API XXH_PUREF XXH128_hash_t +XXH3_128bits_withSecretandSeed(XXH_NOESCAPE const void* input, size_t length, + XXH_NOESCAPE const void* secret, size_t secretSize, + XXH64_hash_t seed64); +#ifndef XXH_NO_STREAM +/*! @copydoc XXH3_64bits_withSecretandSeed() */ +XXH_PUBLIC_API XXH_errorcode +XXH3_64bits_reset_withSecretandSeed(XXH_NOESCAPE XXH3_state_t* statePtr, + XXH_NOESCAPE const void* secret, size_t secretSize, + XXH64_hash_t seed64); +/*! @copydoc XXH3_64bits_withSecretandSeed() */ +XXH_PUBLIC_API XXH_errorcode +XXH3_128bits_reset_withSecretandSeed(XXH_NOESCAPE XXH3_state_t* statePtr, + XXH_NOESCAPE const void* secret, size_t secretSize, + XXH64_hash_t seed64); +#endif /* !XXH_NO_STREAM */ +#endif /* !XXH_NO_XXH3 */ #endif /* XXH_NO_LONG_LONG */ - - #if defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API) # define XXH_IMPLEMENTATION #endif @@ -774,8 +1778,24 @@ XXH_PUBLIC_API XXH128_hash_t XXH128(const void* data, size_t len, XXH64_hash_t s /* ************************************* * Tuning parameters ***************************************/ + /*! - * XXH_FORCE_MEMORY_ACCESS: + * @defgroup tuning Tuning parameters + * @{ + * + * Various macros to control xxHash's behavior. + */ +#ifdef XXH_DOXYGEN +/*! + * @brief Define this to disable 64-bit code. + * + * Useful if only using the @ref XXH32_family and you have a strict C90 compiler. + */ +# define XXH_NO_LONG_LONG +# undef XXH_NO_LONG_LONG /* don't actually */ +/*! + * @brief Controls how unaligned memory is accessed. + * * By default, access to unaligned memory is controlled by `memcpy()`, which is * safe and portable. * @@ -784,77 +1804,108 @@ XXH_PUBLIC_API XXH128_hash_t XXH128(const void* data, size_t len, XXH64_hash_t s * * The below switch allow selection of a different access method * in the search for improved performance. - * Method 0 (default): - * Use `memcpy()`. Safe and portable. Default. - * Method 1: - * `__attribute__((packed))` statement. It depends on compiler extensions - * and is therefore not portable. - * This method is safe if your compiler supports it, and *generally* as - * fast or faster than `memcpy`. - * Method 2: - * Direct access via cast. This method doesn't depend on the compiler but - * violates the C standard. - * It can generate buggy code on targets which do not support unaligned - * memory accesses. - * But in some circumstances, it's the only known way to get the most - * performance (example: GCC + ARMv6) - * Method 3: - * Byteshift. This can generate the best code on old compilers which don't + * + * @par Possible options: + * + * - `XXH_FORCE_MEMORY_ACCESS=0` (default): `memcpy` + * @par + * Use `memcpy()`. Safe and portable. Note that most modern compilers will + * eliminate the function call and treat it as an unaligned access. + * + * - `XXH_FORCE_MEMORY_ACCESS=1`: `__attribute__((aligned(1)))` + * @par + * Depends on compiler extensions and is therefore not portable. + * This method is safe _if_ your compiler supports it, + * and *generally* as fast or faster than `memcpy`. + * + * - `XXH_FORCE_MEMORY_ACCESS=2`: Direct cast + * @par + * Casts directly and dereferences. This method doesn't depend on the + * compiler, but it violates the C standard as it directly dereferences an + * unaligned pointer. It can generate buggy code on targets which do not + * support unaligned memory accesses, but in some circumstances, it's the + * only known way to get the most performance. + * + * - `XXH_FORCE_MEMORY_ACCESS=3`: Byteshift + * @par + * Also portable. This can generate the best code on old compilers which don't * inline small `memcpy()` calls, and it might also be faster on big-endian - * systems which lack a native byteswap instruction. - * See https://stackoverflow.com/a/32095106/646947 for details. - * Prefer these methods in priority order (0 > 1 > 2 > 3) + * systems which lack a native byteswap instruction. However, some compilers + * will emit literal byteshifts even if the target supports unaligned access. + * + * + * @warning + * Methods 1 and 2 rely on implementation-defined behavior. Use these with + * care, as what works on one compiler/platform/optimization level may cause + * another to read garbage data or even crash. + * + * See https://fastcompression.blogspot.com/2015/08/accessing-unaligned-memory.html for details. + * + * Prefer these methods in priority order (0 > 3 > 1 > 2) */ -#ifndef XXH_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */ -# if !defined(__clang__) && defined(__GNUC__) && defined(__ARM_FEATURE_UNALIGNED) && defined(__ARM_ARCH) && (__ARM_ARCH == 6) -# define XXH_FORCE_MEMORY_ACCESS 2 -# elif !defined(__clang__) && ((defined(__INTEL_COMPILER) && !defined(_WIN32)) || \ - (defined(__GNUC__) && (defined(__ARM_ARCH) && __ARM_ARCH >= 7))) -# define XXH_FORCE_MEMORY_ACCESS 1 -# endif -#endif +# define XXH_FORCE_MEMORY_ACCESS 0 /*! - * XXH_ACCEPT_NULL_INPUT_POINTER: - * If the input pointer is NULL, xxHash's default behavior is to dereference it, - * triggering a segfault. - * When this macro is enabled, xxHash actively checks the input for a null pointer. - * If it is, the result for null input pointers is the same as a zero-length input. - */ -#ifndef XXH_ACCEPT_NULL_INPUT_POINTER /* can be defined externally */ -# define XXH_ACCEPT_NULL_INPUT_POINTER 0 -#endif + * @def XXH_SIZE_OPT + * @brief Controls how much xxHash optimizes for size. + * + * xxHash, when compiled, tends to result in a rather large binary size. This + * is mostly due to heavy usage to forced inlining and constant folding of the + * @ref XXH3_family to increase performance. + * + * However, some developers prefer size over speed. This option can + * significantly reduce the size of the generated code. When using the `-Os` + * or `-Oz` options on GCC or Clang, this is defined to 1 by default, + * otherwise it is defined to 0. + * + * Most of these size optimizations can be controlled manually. + * + * This is a number from 0-2. + * - `XXH_SIZE_OPT` == 0: Default. xxHash makes no size optimizations. Speed + * comes first. + * - `XXH_SIZE_OPT` == 1: Default for `-Os` and `-Oz`. xxHash is more + * conservative and disables hacks that increase code size. It implies the + * options @ref XXH_NO_INLINE_HINTS == 1, @ref XXH_FORCE_ALIGN_CHECK == 0, + * and @ref XXH3_NEON_LANES == 8 if they are not already defined. + * - `XXH_SIZE_OPT` == 2: xxHash tries to make itself as small as possible. + * Performance may cry. For example, the single shot functions just use the + * streaming API. + */ +# define XXH_SIZE_OPT 0 /*! - * XXH_FORCE_ALIGN_CHECK: - * This is an important performance trick - * for architectures without decent unaligned memory access performance. - * It checks for input alignment, and when conditions are met, - * uses a "fast path" employing direct 32-bit/64-bit read, - * resulting in _dramatically faster_ read speed. + * @def XXH_FORCE_ALIGN_CHECK + * @brief If defined to non-zero, adds a special path for aligned inputs (XXH32() + * and XXH64() only). + * + * This is an important performance trick for architectures without decent + * unaligned memory access performance. + * + * It checks for input alignment, and when conditions are met, uses a "fast + * path" employing direct 32-bit/64-bit reads, resulting in _dramatically + * faster_ read speed. * - * The check costs one initial branch per hash, which is generally negligible, but not zero. - * Moreover, it's not useful to generate binary for an additional code path - * if memory access uses same instruction for both aligned and unaligned adresses. + * The check costs one initial branch per hash, which is generally negligible, + * but not zero. + * + * Moreover, it's not useful to generate an additional code path if memory + * access uses the same instruction for both aligned and unaligned + * addresses (e.g. x86 and aarch64). * * In these cases, the alignment check can be removed by setting this macro to 0. * Then the code will always use unaligned memory access. - * Align check is automatically disabled on x86, x64 & arm64, + * Align check is automatically disabled on x86, x64, ARM64, and some ARM chips * which are platforms known to offer good unaligned memory accesses performance. * + * It is also disabled by default when @ref XXH_SIZE_OPT >= 1. + * * This option does not affect XXH3 (only XXH32 and XXH64). */ -#ifndef XXH_FORCE_ALIGN_CHECK /* can be defined externally */ -# if defined(__i386) || defined(__x86_64__) || defined(__aarch64__) \ - || defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM64) /* visual */ -# define XXH_FORCE_ALIGN_CHECK 0 -# else -# define XXH_FORCE_ALIGN_CHECK 1 -# endif -#endif +# define XXH_FORCE_ALIGN_CHECK 0 /*! - * XXH_NO_INLINE_HINTS: + * @def XXH_NO_INLINE_HINTS + * @brief When non-zero, sets all functions to `static`. * * By default, xxHash tries to force the compiler to inline almost all internal * functions. @@ -869,48 +1920,169 @@ XXH_PUBLIC_API XXH128_hash_t XXH128(const void* data, size_t len, XXH64_hash_t s * XXH_NO_INLINE_HINTS marks all internal functions as static, giving the * compiler full control on whether to inline or not. * - * When not optimizing (-O0), optimizing for size (-Os, -Oz), or using - * -fno-inline with GCC or Clang, this will automatically be defined. + * When not optimizing (-O0), using `-fno-inline` with GCC or Clang, or if + * @ref XXH_SIZE_OPT >= 1, this will automatically be defined. + */ +# define XXH_NO_INLINE_HINTS 0 + +/*! + * @def XXH3_INLINE_SECRET + * @brief Determines whether to inline the XXH3 withSecret code. + * + * When the secret size is known, the compiler can improve the performance + * of XXH3_64bits_withSecret() and XXH3_128bits_withSecret(). + * + * However, if the secret size is not known, it doesn't have any benefit. This + * happens when xxHash is compiled into a global symbol. Therefore, if + * @ref XXH_INLINE_ALL is *not* defined, this will be defined to 0. + * + * Additionally, this defaults to 0 on GCC 12+, which has an issue with function pointers + * that are *sometimes* force inline on -Og, and it is impossible to automatically + * detect this optimization level. + */ +# define XXH3_INLINE_SECRET 0 + +/*! + * @def XXH32_ENDJMP + * @brief Whether to use a jump for `XXH32_finalize`. + * + * For performance, `XXH32_finalize` uses multiple branches in the finalizer. + * This is generally preferable for performance, + * but depending on exact architecture, a jmp may be preferable. + * + * This setting is only possibly making a difference for very small inputs. + */ +# define XXH32_ENDJMP 0 + +/*! + * @internal + * @brief Redefines old internal names. + * + * For compatibility with code that uses xxHash's internals before the names + * were changed to improve namespacing. There is no other reason to use this. + */ +# define XXH_OLD_NAMES +# undef XXH_OLD_NAMES /* don't actually use, it is ugly. */ + +/*! + * @def XXH_NO_STREAM + * @brief Disables the streaming API. + * + * When xxHash is not inlined and the streaming functions are not used, disabling + * the streaming functions can improve code size significantly, especially with + * the @ref XXH3_family which tends to make constant folded copies of itself. + */ +# define XXH_NO_STREAM +# undef XXH_NO_STREAM /* don't actually */ +#endif /* XXH_DOXYGEN */ +/*! + * @} */ + +#ifndef XXH_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */ + /* prefer __packed__ structures (method 1) for GCC + * < ARMv7 with unaligned access (e.g. Raspbian armhf) still uses byte shifting, so we use memcpy + * which for some reason does unaligned loads. */ +# if defined(__GNUC__) && !(defined(__ARM_ARCH) && __ARM_ARCH < 7 && defined(__ARM_FEATURE_UNALIGNED)) +# define XXH_FORCE_MEMORY_ACCESS 1 +# endif +#endif + +#ifndef XXH_SIZE_OPT + /* default to 1 for -Os or -Oz */ +# if (defined(__GNUC__) || defined(__clang__)) && defined(__OPTIMIZE_SIZE__) +# define XXH_SIZE_OPT 1 +# else +# define XXH_SIZE_OPT 0 +# endif +#endif + +#ifndef XXH_FORCE_ALIGN_CHECK /* can be defined externally */ + /* don't check on sizeopt, x86, aarch64, or arm when unaligned access is available */ +# if XXH_SIZE_OPT >= 1 || \ + defined(__i386) || defined(__x86_64__) || defined(__aarch64__) || defined(__ARM_FEATURE_UNALIGNED) \ + || defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM64) || defined(_M_ARM) /* visual */ +# define XXH_FORCE_ALIGN_CHECK 0 +# else +# define XXH_FORCE_ALIGN_CHECK 1 +# endif +#endif + #ifndef XXH_NO_INLINE_HINTS -# if defined(__OPTIMIZE_SIZE__) /* -Os, -Oz */ \ - || defined(__NO_INLINE__) /* -O0, -fno-inline */ +# if XXH_SIZE_OPT >= 1 || defined(__NO_INLINE__) /* -O0, -fno-inline */ # define XXH_NO_INLINE_HINTS 1 # else # define XXH_NO_INLINE_HINTS 0 # endif #endif -/*! - * XXH_REROLL: - * Whether to reroll XXH32_finalize, and XXH64_finalize, - * instead of using an unrolled jump table/if statement loop. - * - * This is automatically defined on -Os/-Oz on GCC and Clang. - */ -#ifndef XXH_REROLL -# if defined(__OPTIMIZE_SIZE__) -# define XXH_REROLL 1 +#ifndef XXH3_INLINE_SECRET +# if (defined(__GNUC__) && !defined(__clang__) && __GNUC__ >= 12) \ + || !defined(XXH_INLINE_ALL) +# define XXH3_INLINE_SECRET 0 # else -# define XXH_REROLL 0 +# define XXH3_INLINE_SECRET 1 # endif #endif +#ifndef XXH32_ENDJMP +/* generally preferable for performance */ +# define XXH32_ENDJMP 0 +#endif + +/*! + * @defgroup impl Implementation + * @{ + */ + /* ************************************* * Includes & Memory related functions ***************************************/ -/*! +#if defined(XXH_NO_STREAM) +/* nothing */ +#elif defined(XXH_NO_STDLIB) + +/* When requesting to disable any mention of stdlib, + * the library loses the ability to invoked malloc / free. + * In practice, it means that functions like `XXH*_createState()` + * will always fail, and return NULL. + * This flag is useful in situations where + * xxhash.h is integrated into some kernel, embedded or limited environment + * without access to dynamic allocation. + */ + +static XXH_CONSTF void* XXH_malloc(size_t s) { (void)s; return NULL; } +static void XXH_free(void* p) { (void)p; } + +#else + +/* * Modify the local functions below should you wish to use * different memory routines for malloc() and free() */ #include -static void* XXH_malloc(size_t s) { return malloc(s); } +/*! + * @internal + * @brief Modify this function to use a different routine than malloc(). + */ +static XXH_MALLOCF void* XXH_malloc(size_t s) { return malloc(s); } + +/*! + * @internal + * @brief Modify this function to use a different routine than free(). + */ static void XXH_free(void* p) { free(p); } -/*! and for memcpy() */ +#endif /* XXH_NO_STDLIB */ + #include + +/*! + * @internal + * @brief Modify this function to use a different routine than memcpy(). + */ static void* XXH_memcpy(void* dest, const void* src, size_t size) { return memcpy(dest,src,size); @@ -927,19 +2099,19 @@ static void* XXH_memcpy(void* dest, const void* src, size_t size) #endif #if XXH_NO_INLINE_HINTS /* disable inlining hints */ -# if defined(__GNUC__) +# if defined(__GNUC__) || defined(__clang__) # define XXH_FORCE_INLINE static __attribute__((unused)) # else # define XXH_FORCE_INLINE static # endif # define XXH_NO_INLINE static /* enable inlining hints */ +#elif defined(__GNUC__) || defined(__clang__) +# define XXH_FORCE_INLINE static __inline__ __attribute__((always_inline, unused)) +# define XXH_NO_INLINE static __attribute__((noinline)) #elif defined(_MSC_VER) /* Visual Studio */ # define XXH_FORCE_INLINE static __forceinline # define XXH_NO_INLINE static __declspec(noinline) -#elif defined(__GNUC__) -# define XXH_FORCE_INLINE static __inline__ __attribute__((always_inline, unused)) -# define XXH_NO_INLINE static __attribute__((noinline)) #elif defined (__cplusplus) \ || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) /* C99 */ # define XXH_FORCE_INLINE static inline @@ -949,12 +2121,21 @@ static void* XXH_memcpy(void* dest, const void* src, size_t size) # define XXH_NO_INLINE static #endif +#if XXH3_INLINE_SECRET +# define XXH3_WITH_SECRET_INLINE XXH_FORCE_INLINE +#else +# define XXH3_WITH_SECRET_INLINE XXH_NO_INLINE +#endif /* ************************************* * Debug ***************************************/ -/* +/*! + * @ingroup tuning + * @def XXH_DEBUGLEVEL + * @brief Sets the debugging level. + * * XXH_DEBUGLEVEL is expected to be defined externally, typically via the * compiler's command line options. The value must be a number. */ @@ -970,12 +2151,54 @@ static void* XXH_memcpy(void* dest, const void* src, size_t size) # include /* note: can still be disabled with NDEBUG */ # define XXH_ASSERT(c) assert(c) #else -# define XXH_ASSERT(c) ((void)0) +# if defined(__INTEL_COMPILER) +# define XXH_ASSERT(c) XXH_ASSUME((unsigned char) (c)) +# else +# define XXH_ASSERT(c) XXH_ASSUME(c) +# endif #endif /* note: use after variable declarations */ -#define XXH_STATIC_ASSERT(c) do { enum { XXH_sa = 1/(int)(!!(c)) }; } while (0) +#ifndef XXH_STATIC_ASSERT +# if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) /* C11 */ +# define XXH_STATIC_ASSERT_WITH_MESSAGE(c,m) do { _Static_assert((c),m); } while(0) +# elif defined(__cplusplus) && (__cplusplus >= 201103L) /* C++11 */ +# define XXH_STATIC_ASSERT_WITH_MESSAGE(c,m) do { static_assert((c),m); } while(0) +# else +# define XXH_STATIC_ASSERT_WITH_MESSAGE(c,m) do { struct xxh_sa { char x[(c) ? 1 : -1]; }; } while(0) +# endif +# define XXH_STATIC_ASSERT(c) XXH_STATIC_ASSERT_WITH_MESSAGE((c),#c) +#endif + +/*! + * @internal + * @def XXH_COMPILER_GUARD(var) + * @brief Used to prevent unwanted optimizations for @p var. + * + * It uses an empty GCC inline assembly statement with a register constraint + * which forces @p var into a general purpose register (eg eax, ebx, ecx + * on x86) and marks it as modified. + * + * This is used in a few places to avoid unwanted autovectorization (e.g. + * XXH32_round()). All vectorization we want is explicit via intrinsics, + * and _usually_ isn't wanted elsewhere. + * + * We also use it to prevent unwanted constant folding for AArch64 in + * XXH3_initCustomSecret_scalar(). + */ +#if defined(__GNUC__) || defined(__clang__) +# define XXH_COMPILER_GUARD(var) __asm__("" : "+r" (var)) +#else +# define XXH_COMPILER_GUARD(var) ((void)0) +#endif +/* Specifically for NEON vectors which use the "w" constraint, on + * Clang. */ +#if defined(__clang__) && defined(__ARM_ARCH) && !defined(__wasm__) +# define XXH_COMPILER_GUARD_CLANG_NEON(var) __asm__("" : "+w" (var)) +#else +# define XXH_COMPILER_GUARD_CLANG_NEON(var) ((void)0) +#endif /* ************************************* * Basic Types @@ -991,6 +2214,7 @@ static void* XXH_memcpy(void* dest, const void* src, size_t size) typedef XXH32_hash_t xxh_u32; #ifdef XXH_OLD_NAMES +# warning "XXH_OLD_NAMES is planned to be removed starting v0.9. If the program depends on it, consider moving away from it by employing newer type names directly" # define BYTE xxh_u8 # define U8 xxh_u8 # define U32 xxh_u32 @@ -998,6 +2222,56 @@ typedef XXH32_hash_t xxh_u32; /* *** Memory access *** */ +/*! + * @internal + * @fn xxh_u32 XXH_read32(const void* ptr) + * @brief Reads an unaligned 32-bit integer from @p ptr in native endianness. + * + * Affected by @ref XXH_FORCE_MEMORY_ACCESS. + * + * @param ptr The pointer to read from. + * @return The 32-bit native endian integer from the bytes at @p ptr. + */ + +/*! + * @internal + * @fn xxh_u32 XXH_readLE32(const void* ptr) + * @brief Reads an unaligned 32-bit little endian integer from @p ptr. + * + * Affected by @ref XXH_FORCE_MEMORY_ACCESS. + * + * @param ptr The pointer to read from. + * @return The 32-bit little endian integer from the bytes at @p ptr. + */ + +/*! + * @internal + * @fn xxh_u32 XXH_readBE32(const void* ptr) + * @brief Reads an unaligned 32-bit big endian integer from @p ptr. + * + * Affected by @ref XXH_FORCE_MEMORY_ACCESS. + * + * @param ptr The pointer to read from. + * @return The 32-bit big endian integer from the bytes at @p ptr. + */ + +/*! + * @internal + * @fn xxh_u32 XXH_readLE32_align(const void* ptr, XXH_alignment align) + * @brief Like @ref XXH_readLE32(), but has an option for aligned reads. + * + * Affected by @ref XXH_FORCE_MEMORY_ACCESS. + * Note that when @ref XXH_FORCE_ALIGN_CHECK == 0, the @p align parameter is + * always @ref XXH_alignment::XXH_unaligned. + * + * @param ptr The pointer to read from. + * @param align Whether @p ptr is aligned. + * @pre + * If @p align == @ref XXH_alignment::XXH_aligned, @p ptr must be 4 byte + * aligned. + * @return The 32-bit little endian integer from the bytes at @p ptr. + */ + #if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3)) /* * Manual byteshift. Best for old compilers which don't inline memcpy. @@ -1014,46 +2288,54 @@ static xxh_u32 XXH_read32(const void* memPtr) { return *(const xxh_u32*) memPtr; #elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1)) /* - * __pack instructions are safer but compiler specific, hence potentially - * problematic for some compilers. - * - * Currently only defined for GCC and ICC. + * __attribute__((aligned(1))) is supported by gcc and clang. Originally the + * documentation claimed that it only increased the alignment, but actually it + * can decrease it on gcc, clang, and icc: + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69502, + * https://gcc.godbolt.org/z/xYez1j67Y. */ #ifdef XXH_OLD_NAMES typedef union { xxh_u32 u32; } __attribute__((packed)) unalign; #endif static xxh_u32 XXH_read32(const void* ptr) { - typedef union { xxh_u32 u32; } __attribute__((packed)) xxh_unalign; - return ((const xxh_unalign*)ptr)->u32; + typedef __attribute__((aligned(1))) xxh_u32 xxh_unalign32; + return *((const xxh_unalign32*)ptr); } #else /* * Portable and safe solution. Generally efficient. - * see: https://stackoverflow.com/a/32095106/646947 + * see: https://fastcompression.blogspot.com/2015/08/accessing-unaligned-memory.html */ static xxh_u32 XXH_read32(const void* memPtr) { xxh_u32 val; - memcpy(&val, memPtr, sizeof(val)); + XXH_memcpy(&val, memPtr, sizeof(val)); return val; } #endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */ -/* *** Endianess *** */ -typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess; +/* *** Endianness *** */ /*! - * XXH_CPU_LITTLE_ENDIAN: + * @ingroup tuning + * @def XXH_CPU_LITTLE_ENDIAN + * @brief Whether the target is little endian. + * * Defined to 1 if the target is little endian, or 0 if it is big endian. * It can be defined externally, for example on the compiler command line. * - * If it is not defined, a runtime check (which is usually constant folded) - * is used instead. + * If it is not defined, + * a runtime check (which is usually constant folded) is used instead. + * + * @note + * This is not necessarily defined to an integer constant. + * + * @see XXH_isLittleEndian() for the runtime check. */ #ifndef XXH_CPU_LITTLE_ENDIAN /* @@ -1068,8 +2350,11 @@ typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess; || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) # define XXH_CPU_LITTLE_ENDIAN 0 # else -/* - * runtime test, presumed to simplify to a constant by compiler +/*! + * @internal + * @brief Runtime check for @ref XXH_CPU_LITTLE_ENDIAN. + * + * Most compilers will constant fold this. */ static int XXH_isLittleEndian(void) { @@ -1098,6 +2383,64 @@ static int XXH_isLittleEndian(void) # define XXH_HAS_BUILTIN(x) 0 #endif + + +/* + * C23 and future versions have standard "unreachable()". + * Once it has been implemented reliably we can add it as an + * additional case: + * + * ``` + * #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= XXH_C23_VN) + * # include + * # ifdef unreachable + * # define XXH_UNREACHABLE() unreachable() + * # endif + * #endif + * ``` + * + * Note C++23 also has std::unreachable() which can be detected + * as follows: + * ``` + * #if defined(__cpp_lib_unreachable) && (__cpp_lib_unreachable >= 202202L) + * # include + * # define XXH_UNREACHABLE() std::unreachable() + * #endif + * ``` + * NB: `__cpp_lib_unreachable` is defined in the `` header. + * We don't use that as including `` in `extern "C"` blocks + * doesn't work on GCC12 + */ + +#if XXH_HAS_BUILTIN(__builtin_unreachable) +# define XXH_UNREACHABLE() __builtin_unreachable() + +#elif defined(_MSC_VER) +# define XXH_UNREACHABLE() __assume(0) + +#else +# define XXH_UNREACHABLE() +#endif + +#if XXH_HAS_BUILTIN(__builtin_assume) +# define XXH_ASSUME(c) __builtin_assume(c) +#else +# define XXH_ASSUME(c) if (!(c)) { XXH_UNREACHABLE(); } +#endif + +/*! + * @internal + * @def XXH_rotl32(x,r) + * @brief 32-bit rotate left. + * + * @param x The 32-bit integer to be rotated. + * @param r The number of bits to rotate. + * @pre + * @p r > 0 && @p r < 32 + * @note + * @p x and @p r may be evaluated multiple times. + * @return The rotated result. + */ #if !defined(NO_CLANG_BUILTIN) && XXH_HAS_BUILTIN(__builtin_rotateleft32) \ && XXH_HAS_BUILTIN(__builtin_rotateleft64) # define XXH_rotl32 __builtin_rotateleft32 @@ -1111,6 +2454,14 @@ static int XXH_isLittleEndian(void) # define XXH_rotl64(x,r) (((x) << (r)) | ((x) >> (64 - (r)))) #endif +/*! + * @internal + * @fn xxh_u32 XXH_swap32(xxh_u32 x) + * @brief A 32-bit byteswap. + * + * @param x The 32-bit integer to byteswap. + * @return @p x, byteswapped. + */ #if defined(_MSC_VER) /* Visual Studio */ # define XXH_swap32 _byteswap_ulong #elif XXH_GCC_VERSION >= 403 @@ -1129,7 +2480,15 @@ static xxh_u32 XXH_swap32 (xxh_u32 x) /* *************************** * Memory reads *****************************/ -typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment; + +/*! + * @internal + * @brief Enum to indicate whether a pointer is aligned. + */ +typedef enum { + XXH_aligned, /*!< Aligned */ + XXH_unaligned /*!< Possibly unaligned */ +} XXH_alignment; /* * XXH_FORCE_MEMORY_ACCESS==3 is an endian-independent byteshift load. @@ -1182,17 +2541,27 @@ XXH_readLE32_align(const void* ptr, XXH_alignment align) /* ************************************* * Misc ***************************************/ +/*! @ingroup public */ XXH_PUBLIC_API unsigned XXH_versionNumber (void) { return XXH_VERSION_NUMBER; } /* ******************************************************************* * 32-bit hash functions *********************************************************************/ -static const xxh_u32 XXH_PRIME32_1 = 0x9E3779B1U; /* 0b10011110001101110111100110110001 */ -static const xxh_u32 XXH_PRIME32_2 = 0x85EBCA77U; /* 0b10000101111010111100101001110111 */ -static const xxh_u32 XXH_PRIME32_3 = 0xC2B2AE3DU; /* 0b11000010101100101010111000111101 */ -static const xxh_u32 XXH_PRIME32_4 = 0x27D4EB2FU; /* 0b00100111110101001110101100101111 */ -static const xxh_u32 XXH_PRIME32_5 = 0x165667B1U; /* 0b00010110010101100110011110110001 */ +/*! + * @} + * @defgroup XXH32_impl XXH32 implementation + * @ingroup impl + * + * Details on the XXH32 implementation. + * @{ + */ + /* #define instead of static const, to be used as initializers */ +#define XXH_PRIME32_1 0x9E3779B1U /*!< 0b10011110001101110111100110110001 */ +#define XXH_PRIME32_2 0x85EBCA77U /*!< 0b10000101111010111100101001110111 */ +#define XXH_PRIME32_3 0xC2B2AE3DU /*!< 0b11000010101100101010111000111101 */ +#define XXH_PRIME32_4 0x27D4EB2FU /*!< 0b00100111110101001110101100101111 */ +#define XXH_PRIME32_5 0x165667B1U /*!< 0b00010110010101100110011110110001 */ #ifdef XXH_OLD_NAMES # define PRIME32_1 XXH_PRIME32_1 @@ -1202,18 +2571,28 @@ static const xxh_u32 XXH_PRIME32_5 = 0x165667B1U; /* 0b00010110010101100110011 # define PRIME32_5 XXH_PRIME32_5 #endif +/*! + * @internal + * @brief Normal stripe processing routine. + * + * This shuffles the bits so that any bit from @p input impacts several bits in + * @p acc. + * + * @param acc The accumulator lane. + * @param input The stripe of input to mix. + * @return The mixed accumulator lane. + */ static xxh_u32 XXH32_round(xxh_u32 acc, xxh_u32 input) { acc += input * XXH_PRIME32_2; acc = XXH_rotl32(acc, 13); acc *= XXH_PRIME32_1; -#if defined(__GNUC__) && defined(__SSE4_1__) && !defined(XXH_ENABLE_AUTOVECTORIZE) +#if (defined(__SSE4_1__) || defined(__aarch64__) || defined(__wasm_simd128__)) && !defined(XXH_ENABLE_AUTOVECTORIZE) /* * UGLY HACK: - * This inline assembly hack forces acc into a normal register. This is the - * only thing that prevents GCC and Clang from autovectorizing the XXH32 - * loop (pragmas and attributes don't work for some resason) without globally - * disabling SSE4.1. + * A compiler fence is the only thing that prevents GCC and Clang from + * autovectorizing the XXH32 loop (pragmas and attributes don't work for some + * reason) without globally disabling SSE4.1. * * The reason we want to avoid vectorization is because despite working on * 4 integers at a time, there are multiple factors slowing XXH32 down on @@ -1238,55 +2617,73 @@ static xxh_u32 XXH32_round(xxh_u32 acc, xxh_u32 input) * can load data, while v3 can multiply. SSE forces them to operate * together. * - * How this hack works: - * __asm__("" // Declare an assembly block but don't declare any instructions - * : // However, as an Input/Output Operand, - * "+r" // constrain a read/write operand (+) as a general purpose register (r). - * (acc) // and set acc as the operand - * ); + * This is also enabled on AArch64, as Clang is *very aggressive* in vectorizing + * the loop. NEON is only faster on the A53, and with the newer cores, it is less + * than half the speed. * - * Because of the 'r', the compiler has promised that seed will be in a - * general purpose register and the '+' says that it will be 'read/write', - * so it has to assume it has changed. It is like volatile without all the - * loads and stores. - * - * Since the argument has to be in a normal register (not an SSE register), - * each time XXH32_round is called, it is impossible to vectorize. + * Additionally, this is used on WASM SIMD128 because it JITs to the same + * SIMD instructions and has the same issue. */ - __asm__("" : "+r" (acc)); + XXH_COMPILER_GUARD(acc); #endif return acc; } -/* mix all bits */ -static xxh_u32 XXH32_avalanche(xxh_u32 h32) +/*! + * @internal + * @brief Mixes all bits to finalize the hash. + * + * The final mix ensures that all input bits have a chance to impact any bit in + * the output digest, resulting in an unbiased distribution. + * + * @param hash The hash to avalanche. + * @return The avalanched hash. + */ +static xxh_u32 XXH32_avalanche(xxh_u32 hash) { - h32 ^= h32 >> 15; - h32 *= XXH_PRIME32_2; - h32 ^= h32 >> 13; - h32 *= XXH_PRIME32_3; - h32 ^= h32 >> 16; - return(h32); + hash ^= hash >> 15; + hash *= XXH_PRIME32_2; + hash ^= hash >> 13; + hash *= XXH_PRIME32_3; + hash ^= hash >> 16; + return hash; } #define XXH_get32bits(p) XXH_readLE32_align(p, align) -static xxh_u32 -XXH32_finalize(xxh_u32 h32, const xxh_u8* ptr, size_t len, XXH_alignment align) +/*! + * @internal + * @brief Processes the last 0-15 bytes of @p ptr. + * + * There may be up to 15 bytes remaining to consume from the input. + * This final stage will digest them to ensure that all input bytes are present + * in the final mix. + * + * @param hash The hash to finalize. + * @param ptr The pointer to the remaining input. + * @param len The remaining length, modulo 16. + * @param align Whether @p ptr is aligned. + * @return The finalized hash. + * @see XXH64_finalize(). + */ +static XXH_PUREF xxh_u32 +XXH32_finalize(xxh_u32 hash, const xxh_u8* ptr, size_t len, XXH_alignment align) { -#define XXH_PROCESS1 do { \ - h32 += (*ptr++) * XXH_PRIME32_5; \ - h32 = XXH_rotl32(h32, 11) * XXH_PRIME32_1; \ +#define XXH_PROCESS1 do { \ + hash += (*ptr++) * XXH_PRIME32_5; \ + hash = XXH_rotl32(hash, 11) * XXH_PRIME32_1; \ } while (0) -#define XXH_PROCESS4 do { \ - h32 += XXH_get32bits(ptr) * XXH_PRIME32_3; \ - ptr += 4; \ - h32 = XXH_rotl32(h32, 17) * XXH_PRIME32_4; \ +#define XXH_PROCESS4 do { \ + hash += XXH_get32bits(ptr) * XXH_PRIME32_3; \ + ptr += 4; \ + hash = XXH_rotl32(hash, 17) * XXH_PRIME32_4; \ } while (0) - /* Compact rerolled version */ - if (XXH_REROLL) { + if (ptr==NULL) XXH_ASSERT(len == 0); + + /* Compact rerolled version; generally faster */ + if (!XXH32_ENDJMP) { len &= 15; while (len >= 4) { XXH_PROCESS4; @@ -1296,49 +2693,49 @@ XXH32_finalize(xxh_u32 h32, const xxh_u8* ptr, size_t len, XXH_alignment align) XXH_PROCESS1; --len; } - return XXH32_avalanche(h32); + return XXH32_avalanche(hash); } else { switch(len&15) /* or switch(bEnd - p) */ { case 12: XXH_PROCESS4; - /* fallthrough */ + XXH_FALLTHROUGH; /* fallthrough */ case 8: XXH_PROCESS4; - /* fallthrough */ + XXH_FALLTHROUGH; /* fallthrough */ case 4: XXH_PROCESS4; - return XXH32_avalanche(h32); + return XXH32_avalanche(hash); case 13: XXH_PROCESS4; - /* fallthrough */ + XXH_FALLTHROUGH; /* fallthrough */ case 9: XXH_PROCESS4; - /* fallthrough */ + XXH_FALLTHROUGH; /* fallthrough */ case 5: XXH_PROCESS4; XXH_PROCESS1; - return XXH32_avalanche(h32); + return XXH32_avalanche(hash); case 14: XXH_PROCESS4; - /* fallthrough */ + XXH_FALLTHROUGH; /* fallthrough */ case 10: XXH_PROCESS4; - /* fallthrough */ + XXH_FALLTHROUGH; /* fallthrough */ case 6: XXH_PROCESS4; XXH_PROCESS1; XXH_PROCESS1; - return XXH32_avalanche(h32); + return XXH32_avalanche(hash); case 15: XXH_PROCESS4; - /* fallthrough */ + XXH_FALLTHROUGH; /* fallthrough */ case 11: XXH_PROCESS4; - /* fallthrough */ + XXH_FALLTHROUGH; /* fallthrough */ case 7: XXH_PROCESS4; - /* fallthrough */ + XXH_FALLTHROUGH; /* fallthrough */ case 3: XXH_PROCESS1; - /* fallthrough */ + XXH_FALLTHROUGH; /* fallthrough */ case 2: XXH_PROCESS1; - /* fallthrough */ + XXH_FALLTHROUGH; /* fallthrough */ case 1: XXH_PROCESS1; - /* fallthrough */ - case 0: return XXH32_avalanche(h32); + XXH_FALLTHROUGH; /* fallthrough */ + case 0: return XXH32_avalanche(hash); } XXH_ASSERT(0); - return h32; /* reaching this point is deemed impossible */ + return hash; /* reaching this point is deemed impossible */ } } @@ -1350,20 +2747,23 @@ XXH32_finalize(xxh_u32 h32, const xxh_u8* ptr, size_t len, XXH_alignment align) # undef XXH_PROCESS4 #endif -XXH_FORCE_INLINE xxh_u32 +/*! + * @internal + * @brief The implementation for @ref XXH32(). + * + * @param input , len , seed Directly passed from @ref XXH32(). + * @param align Whether @p input is aligned. + * @return The calculated hash. + */ +XXH_FORCE_INLINE XXH_PUREF xxh_u32 XXH32_endian_align(const xxh_u8* input, size_t len, xxh_u32 seed, XXH_alignment align) { - const xxh_u8* bEnd = input + len; xxh_u32 h32; -#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) - if (input==NULL) { - len=0; - bEnd=input=(const xxh_u8*)(size_t)16; - } -#endif + if (input==NULL) XXH_ASSERT(len == 0); if (len>=16) { + const xxh_u8* const bEnd = input + len; const xxh_u8* const limit = bEnd - 15; xxh_u32 v1 = seed + XXH_PRIME32_1 + XXH_PRIME32_2; xxh_u32 v2 = seed + XXH_PRIME32_2; @@ -1388,18 +2788,16 @@ XXH32_endian_align(const xxh_u8* input, size_t len, xxh_u32 seed, XXH_alignment return XXH32_finalize(h32, input, len&15, align); } - +/*! @ingroup XXH32_family */ XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t len, XXH32_hash_t seed) { -#if 0 +#if !defined(XXH_NO_STREAM) && XXH_SIZE_OPT >= 2 /* Simple version, good for code maintenance, but unfortunately slow for small inputs */ XXH32_state_t state; XXH32_reset(&state, seed); XXH32_update(&state, (const xxh_u8*)input, len); return XXH32_digest(&state); - #else - if (XXH_FORCE_ALIGN_CHECK) { if ((((size_t)input) & 3) == 0) { /* Input is 4-bytes aligned, leverage the speed benefit */ return XXH32_endian_align((const xxh_u8*)input, len, seed, XXH_aligned); @@ -1412,45 +2810,46 @@ XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t len, XXH32_hash_t s /******* Hash streaming *******/ - +#ifndef XXH_NO_STREAM +/*! @ingroup XXH32_family */ XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void) { return (XXH32_state_t*)XXH_malloc(sizeof(XXH32_state_t)); } +/*! @ingroup XXH32_family */ XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr) { XXH_free(statePtr); return XXH_OK; } +/*! @ingroup XXH32_family */ XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dstState, const XXH32_state_t* srcState) { - memcpy(dstState, srcState, sizeof(*dstState)); + XXH_memcpy(dstState, srcState, sizeof(*dstState)); } +/*! @ingroup XXH32_family */ XXH_PUBLIC_API XXH_errorcode XXH32_reset(XXH32_state_t* statePtr, XXH32_hash_t seed) { - XXH32_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */ - memset(&state, 0, sizeof(state)); - state.v1 = seed + XXH_PRIME32_1 + XXH_PRIME32_2; - state.v2 = seed + XXH_PRIME32_2; - state.v3 = seed + 0; - state.v4 = seed - XXH_PRIME32_1; - /* do not write into reserved, planned to be removed in a future version */ - memcpy(statePtr, &state, sizeof(state) - sizeof(state.reserved)); + XXH_ASSERT(statePtr != NULL); + memset(statePtr, 0, sizeof(*statePtr)); + statePtr->v[0] = seed + XXH_PRIME32_1 + XXH_PRIME32_2; + statePtr->v[1] = seed + XXH_PRIME32_2; + statePtr->v[2] = seed + 0; + statePtr->v[3] = seed - XXH_PRIME32_1; return XXH_OK; } +/*! @ingroup XXH32_family */ XXH_PUBLIC_API XXH_errorcode XXH32_update(XXH32_state_t* state, const void* input, size_t len) { - if (input==NULL) -#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + if (input==NULL) { + XXH_ASSERT(len == 0); return XXH_OK; -#else - return XXH_ERROR; -#endif + } { const xxh_u8* p = (const xxh_u8*)input; const xxh_u8* const bEnd = p + len; @@ -1467,10 +2866,10 @@ XXH32_update(XXH32_state_t* state, const void* input, size_t len) if (state->memsize) { /* some data left from previous update */ XXH_memcpy((xxh_u8*)(state->mem32) + state->memsize, input, 16-state->memsize); { const xxh_u32* p32 = state->mem32; - state->v1 = XXH32_round(state->v1, XXH_readLE32(p32)); p32++; - state->v2 = XXH32_round(state->v2, XXH_readLE32(p32)); p32++; - state->v3 = XXH32_round(state->v3, XXH_readLE32(p32)); p32++; - state->v4 = XXH32_round(state->v4, XXH_readLE32(p32)); + state->v[0] = XXH32_round(state->v[0], XXH_readLE32(p32)); p32++; + state->v[1] = XXH32_round(state->v[1], XXH_readLE32(p32)); p32++; + state->v[2] = XXH32_round(state->v[2], XXH_readLE32(p32)); p32++; + state->v[3] = XXH32_round(state->v[3], XXH_readLE32(p32)); } p += 16-state->memsize; state->memsize = 0; @@ -1478,22 +2877,14 @@ XXH32_update(XXH32_state_t* state, const void* input, size_t len) if (p <= bEnd-16) { const xxh_u8* const limit = bEnd - 16; - xxh_u32 v1 = state->v1; - xxh_u32 v2 = state->v2; - xxh_u32 v3 = state->v3; - xxh_u32 v4 = state->v4; do { - v1 = XXH32_round(v1, XXH_readLE32(p)); p+=4; - v2 = XXH32_round(v2, XXH_readLE32(p)); p+=4; - v3 = XXH32_round(v3, XXH_readLE32(p)); p+=4; - v4 = XXH32_round(v4, XXH_readLE32(p)); p+=4; + state->v[0] = XXH32_round(state->v[0], XXH_readLE32(p)); p+=4; + state->v[1] = XXH32_round(state->v[1], XXH_readLE32(p)); p+=4; + state->v[2] = XXH32_round(state->v[2], XXH_readLE32(p)); p+=4; + state->v[3] = XXH32_round(state->v[3], XXH_readLE32(p)); p+=4; } while (p<=limit); - state->v1 = v1; - state->v2 = v2; - state->v3 = v3; - state->v4 = v4; } if (p < bEnd) { @@ -1506,28 +2897,30 @@ XXH32_update(XXH32_state_t* state, const void* input, size_t len) } -XXH_PUBLIC_API XXH32_hash_t XXH32_digest (const XXH32_state_t* state) +/*! @ingroup XXH32_family */ +XXH_PUBLIC_API XXH32_hash_t XXH32_digest(const XXH32_state_t* state) { xxh_u32 h32; if (state->large_len) { - h32 = XXH_rotl32(state->v1, 1) - + XXH_rotl32(state->v2, 7) - + XXH_rotl32(state->v3, 12) - + XXH_rotl32(state->v4, 18); + h32 = XXH_rotl32(state->v[0], 1) + + XXH_rotl32(state->v[1], 7) + + XXH_rotl32(state->v[2], 12) + + XXH_rotl32(state->v[3], 18); } else { - h32 = state->v3 /* == seed */ + XXH_PRIME32_5; + h32 = state->v[2] /* == seed */ + XXH_PRIME32_5; } h32 += state->total_len_32; return XXH32_finalize(h32, (const xxh_u8*)state->mem32, state->memsize, XXH_aligned); } - +#endif /* !XXH_NO_STREAM */ /******* Canonical representation *******/ -/* +/*! + * @ingroup XXH32_family * The default return values from XXH functions are unsigned 32 and 64 bit * integers. * @@ -1544,9 +2937,9 @@ XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t { XXH_STATIC_ASSERT(sizeof(XXH32_canonical_t) == sizeof(XXH32_hash_t)); if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap32(hash); - memcpy(dst, &hash, sizeof(*dst)); + XXH_memcpy(dst, &hash, sizeof(*dst)); } - +/*! @ingroup XXH32_family */ XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src) { return XXH_readBE32(src); @@ -1558,7 +2951,11 @@ XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src /* ******************************************************************* * 64-bit hash functions *********************************************************************/ - +/*! + * @} + * @ingroup impl + * @{ + */ /******* Memory access *******/ typedef XXH64_hash_t xxh_u64; @@ -1567,35 +2964,6 @@ typedef XXH64_hash_t xxh_u64; # define U64 xxh_u64 #endif -/*! - * XXH_REROLL_XXH64: - * Whether to reroll the XXH64_finalize() loop. - * - * Just like XXH32, we can unroll the XXH64_finalize() loop. This can be a - * performance gain on 64-bit hosts, as only one jump is required. - * - * However, on 32-bit hosts, because arithmetic needs to be done with two 32-bit - * registers, and 64-bit arithmetic needs to be simulated, it isn't beneficial - * to unroll. The code becomes ridiculously large (the largest function in the - * binary on i386!), and rerolling it saves anywhere from 3kB to 20kB. It is - * also slightly faster because it fits into cache better and is more likely - * to be inlined by the compiler. - * - * If XXH_REROLL is defined, this is ignored and the loop is always rerolled. - */ -#ifndef XXH_REROLL_XXH64 -# if (defined(__ILP32__) || defined(_ILP32)) /* ILP32 is often defined on 32-bit GCC family */ \ - || !(defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64) /* x86-64 */ \ - || defined(_M_ARM64) || defined(__aarch64__) || defined(__arm64__) /* aarch64 */ \ - || defined(__PPC64__) || defined(__PPC64LE__) || defined(__ppc64__) || defined(__powerpc64__) /* ppc64 */ \ - || defined(__mips64__) || defined(__mips64)) /* mips64 */ \ - || (!defined(SIZE_MAX) || SIZE_MAX < ULLONG_MAX) /* check limits */ -# define XXH_REROLL_XXH64 1 -# else -# define XXH_REROLL_XXH64 0 -# endif -#endif /* !defined(XXH_REROLL_XXH64) */ - #if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3)) /* * Manual byteshift. Best for old compilers which don't inline memcpy. @@ -1604,35 +2972,39 @@ typedef XXH64_hash_t xxh_u64; #elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2)) /* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */ -static xxh_u64 XXH_read64(const void* memPtr) { return *(const xxh_u64*) memPtr; } +static xxh_u64 XXH_read64(const void* memPtr) +{ + return *(const xxh_u64*) memPtr; +} #elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1)) /* - * __pack instructions are safer, but compiler specific, hence potentially - * problematic for some compilers. - * - * Currently only defined for GCC and ICC. + * __attribute__((aligned(1))) is supported by gcc and clang. Originally the + * documentation claimed that it only increased the alignment, but actually it + * can decrease it on gcc, clang, and icc: + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69502, + * https://gcc.godbolt.org/z/xYez1j67Y. */ #ifdef XXH_OLD_NAMES typedef union { xxh_u32 u32; xxh_u64 u64; } __attribute__((packed)) unalign64; #endif static xxh_u64 XXH_read64(const void* ptr) { - typedef union { xxh_u32 u32; xxh_u64 u64; } __attribute__((packed)) xxh_unalign64; - return ((const xxh_unalign64*)ptr)->u64; + typedef __attribute__((aligned(1))) xxh_u64 xxh_unalign64; + return *((const xxh_unalign64*)ptr); } #else /* * Portable and safe solution. Generally efficient. - * see: https://stackoverflow.com/a/32095106/646947 + * see: https://fastcompression.blogspot.com/2015/08/accessing-unaligned-memory.html */ static xxh_u64 XXH_read64(const void* memPtr) { xxh_u64 val; - memcpy(&val, memPtr, sizeof(val)); + XXH_memcpy(&val, memPtr, sizeof(val)); return val; } @@ -1643,7 +3015,7 @@ static xxh_u64 XXH_read64(const void* memPtr) #elif XXH_GCC_VERSION >= 403 # define XXH_swap64 __builtin_bswap64 #else -static xxh_u64 XXH_swap64 (xxh_u64 x) +static xxh_u64 XXH_swap64(xxh_u64 x) { return ((x << 56) & 0xff00000000000000ULL) | ((x << 40) & 0x00ff000000000000ULL) | @@ -1709,12 +3081,20 @@ XXH_readLE64_align(const void* ptr, XXH_alignment align) /******* xxh64 *******/ - -static const xxh_u64 XXH_PRIME64_1 = 0x9E3779B185EBCA87ULL; /* 0b1001111000110111011110011011000110000101111010111100101010000111 */ -static const xxh_u64 XXH_PRIME64_2 = 0xC2B2AE3D27D4EB4FULL; /* 0b1100001010110010101011100011110100100111110101001110101101001111 */ -static const xxh_u64 XXH_PRIME64_3 = 0x165667B19E3779F9ULL; /* 0b0001011001010110011001111011000110011110001101110111100111111001 */ -static const xxh_u64 XXH_PRIME64_4 = 0x85EBCA77C2B2AE63ULL; /* 0b1000010111101011110010100111011111000010101100101010111001100011 */ -static const xxh_u64 XXH_PRIME64_5 = 0x27D4EB2F165667C5ULL; /* 0b0010011111010100111010110010111100010110010101100110011111000101 */ +/*! + * @} + * @defgroup XXH64_impl XXH64 implementation + * @ingroup impl + * + * Details on the XXH64 implementation. + * @{ + */ +/* #define rather that static const, to be used as initializers */ +#define XXH_PRIME64_1 0x9E3779B185EBCA87ULL /*!< 0b1001111000110111011110011011000110000101111010111100101010000111 */ +#define XXH_PRIME64_2 0xC2B2AE3D27D4EB4FULL /*!< 0b1100001010110010101011100011110100100111110101001110101101001111 */ +#define XXH_PRIME64_3 0x165667B19E3779F9ULL /*!< 0b0001011001010110011001111011000110011110001101110111100111111001 */ +#define XXH_PRIME64_4 0x85EBCA77C2B2AE63ULL /*!< 0b1000010111101011110010100111011111000010101100101010111001100011 */ +#define XXH_PRIME64_5 0x27D4EB2F165667C5ULL /*!< 0b0010011111010100111010110010111100010110010101100110011111000101 */ #ifdef XXH_OLD_NAMES # define PRIME64_1 XXH_PRIME64_1 @@ -1724,6 +3104,7 @@ static const xxh_u64 XXH_PRIME64_5 = 0x27D4EB2F165667C5ULL; /* 0b0010011111010 # define PRIME64_5 XXH_PRIME64_5 #endif +/*! @copydoc XXH32_round */ static xxh_u64 XXH64_round(xxh_u64 acc, xxh_u64 input) { acc += input * XXH_PRIME64_2; @@ -1740,142 +3121,59 @@ static xxh_u64 XXH64_mergeRound(xxh_u64 acc, xxh_u64 val) return acc; } -static xxh_u64 XXH64_avalanche(xxh_u64 h64) +/*! @copydoc XXH32_avalanche */ +static xxh_u64 XXH64_avalanche(xxh_u64 hash) { - h64 ^= h64 >> 33; - h64 *= XXH_PRIME64_2; - h64 ^= h64 >> 29; - h64 *= XXH_PRIME64_3; - h64 ^= h64 >> 32; - return h64; + hash ^= hash >> 33; + hash *= XXH_PRIME64_2; + hash ^= hash >> 29; + hash *= XXH_PRIME64_3; + hash ^= hash >> 32; + return hash; } #define XXH_get64bits(p) XXH_readLE64_align(p, align) -static xxh_u64 -XXH64_finalize(xxh_u64 h64, const xxh_u8* ptr, size_t len, XXH_alignment align) -{ -#define XXH_PROCESS1_64 do { \ - h64 ^= (*ptr++) * XXH_PRIME64_5; \ - h64 = XXH_rotl64(h64, 11) * XXH_PRIME64_1; \ -} while (0) - -#define XXH_PROCESS4_64 do { \ - h64 ^= (xxh_u64)(XXH_get32bits(ptr)) * XXH_PRIME64_1; \ - ptr += 4; \ - h64 = XXH_rotl64(h64, 23) * XXH_PRIME64_2 + XXH_PRIME64_3; \ -} while (0) - -#define XXH_PROCESS8_64 do { \ - xxh_u64 const k1 = XXH64_round(0, XXH_get64bits(ptr)); \ - ptr += 8; \ - h64 ^= k1; \ - h64 = XXH_rotl64(h64,27) * XXH_PRIME64_1 + XXH_PRIME64_4; \ -} while (0) - - /* Rerolled version for 32-bit targets is faster and much smaller. */ - if (XXH_REROLL || XXH_REROLL_XXH64) { - len &= 31; - while (len >= 8) { - XXH_PROCESS8_64; - len -= 8; - } - if (len >= 4) { - XXH_PROCESS4_64; - len -= 4; - } - while (len > 0) { - XXH_PROCESS1_64; - --len; - } - return XXH64_avalanche(h64); - } else { - switch(len & 31) { - case 24: XXH_PROCESS8_64; - /* fallthrough */ - case 16: XXH_PROCESS8_64; - /* fallthrough */ - case 8: XXH_PROCESS8_64; - return XXH64_avalanche(h64); - - case 28: XXH_PROCESS8_64; - /* fallthrough */ - case 20: XXH_PROCESS8_64; - /* fallthrough */ - case 12: XXH_PROCESS8_64; - /* fallthrough */ - case 4: XXH_PROCESS4_64; - return XXH64_avalanche(h64); - - case 25: XXH_PROCESS8_64; - /* fallthrough */ - case 17: XXH_PROCESS8_64; - /* fallthrough */ - case 9: XXH_PROCESS8_64; - XXH_PROCESS1_64; - return XXH64_avalanche(h64); - - case 29: XXH_PROCESS8_64; - /* fallthrough */ - case 21: XXH_PROCESS8_64; - /* fallthrough */ - case 13: XXH_PROCESS8_64; - /* fallthrough */ - case 5: XXH_PROCESS4_64; - XXH_PROCESS1_64; - return XXH64_avalanche(h64); - - case 26: XXH_PROCESS8_64; - /* fallthrough */ - case 18: XXH_PROCESS8_64; - /* fallthrough */ - case 10: XXH_PROCESS8_64; - XXH_PROCESS1_64; - XXH_PROCESS1_64; - return XXH64_avalanche(h64); - - case 30: XXH_PROCESS8_64; - /* fallthrough */ - case 22: XXH_PROCESS8_64; - /* fallthrough */ - case 14: XXH_PROCESS8_64; - /* fallthrough */ - case 6: XXH_PROCESS4_64; - XXH_PROCESS1_64; - XXH_PROCESS1_64; - return XXH64_avalanche(h64); - - case 27: XXH_PROCESS8_64; - /* fallthrough */ - case 19: XXH_PROCESS8_64; - /* fallthrough */ - case 11: XXH_PROCESS8_64; - XXH_PROCESS1_64; - XXH_PROCESS1_64; - XXH_PROCESS1_64; - return XXH64_avalanche(h64); - - case 31: XXH_PROCESS8_64; - /* fallthrough */ - case 23: XXH_PROCESS8_64; - /* fallthrough */ - case 15: XXH_PROCESS8_64; - /* fallthrough */ - case 7: XXH_PROCESS4_64; - /* fallthrough */ - case 3: XXH_PROCESS1_64; - /* fallthrough */ - case 2: XXH_PROCESS1_64; - /* fallthrough */ - case 1: XXH_PROCESS1_64; - /* fallthrough */ - case 0: return XXH64_avalanche(h64); - } +/*! + * @internal + * @brief Processes the last 0-31 bytes of @p ptr. + * + * There may be up to 31 bytes remaining to consume from the input. + * This final stage will digest them to ensure that all input bytes are present + * in the final mix. + * + * @param hash The hash to finalize. + * @param ptr The pointer to the remaining input. + * @param len The remaining length, modulo 32. + * @param align Whether @p ptr is aligned. + * @return The finalized hash + * @see XXH32_finalize(). + */ +static XXH_PUREF xxh_u64 +XXH64_finalize(xxh_u64 hash, const xxh_u8* ptr, size_t len, XXH_alignment align) +{ + if (ptr==NULL) XXH_ASSERT(len == 0); + len &= 31; + while (len >= 8) { + xxh_u64 const k1 = XXH64_round(0, XXH_get64bits(ptr)); + ptr += 8; + hash ^= k1; + hash = XXH_rotl64(hash,27) * XXH_PRIME64_1 + XXH_PRIME64_4; + len -= 8; } - /* impossible to reach */ - XXH_ASSERT(0); - return 0; /* unreachable, but some compilers complain without it */ + if (len >= 4) { + hash ^= (xxh_u64)(XXH_get32bits(ptr)) * XXH_PRIME64_1; + ptr += 4; + hash = XXH_rotl64(hash, 23) * XXH_PRIME64_2 + XXH_PRIME64_3; + len -= 4; + } + while (len > 0) { + hash ^= (*ptr++) * XXH_PRIME64_5; + hash = XXH_rotl64(hash, 11) * XXH_PRIME64_1; + --len; + } + return XXH64_avalanche(hash); } #ifdef XXH_OLD_NAMES @@ -1888,21 +3186,23 @@ XXH64_finalize(xxh_u64 h64, const xxh_u8* ptr, size_t len, XXH_alignment align) # undef XXH_PROCESS8_64 #endif -XXH_FORCE_INLINE xxh_u64 +/*! + * @internal + * @brief The implementation for @ref XXH64(). + * + * @param input , len , seed Directly passed from @ref XXH64(). + * @param align Whether @p input is aligned. + * @return The calculated hash. + */ +XXH_FORCE_INLINE XXH_PUREF xxh_u64 XXH64_endian_align(const xxh_u8* input, size_t len, xxh_u64 seed, XXH_alignment align) { - const xxh_u8* bEnd = input + len; xxh_u64 h64; - -#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) - if (input==NULL) { - len=0; - bEnd=input=(const xxh_u8*)(size_t)32; - } -#endif + if (input==NULL) XXH_ASSERT(len == 0); if (len>=32) { - const xxh_u8* const limit = bEnd - 32; + const xxh_u8* const bEnd = input + len; + const xxh_u8* const limit = bEnd - 31; xxh_u64 v1 = seed + XXH_PRIME64_1 + XXH_PRIME64_2; xxh_u64 v2 = seed + XXH_PRIME64_2; xxh_u64 v3 = seed + 0; @@ -1913,7 +3213,7 @@ XXH64_endian_align(const xxh_u8* input, size_t len, xxh_u64 seed, XXH_alignment v2 = XXH64_round(v2, XXH_get64bits(input)); input+=8; v3 = XXH64_round(v3, XXH_get64bits(input)); input+=8; v4 = XXH64_round(v4, XXH_get64bits(input)); input+=8; - } while (input<=limit); + } while (input= 2 /* Simple version, good for code maintenance, but unfortunately slow for small inputs */ XXH64_state_t state; XXH64_reset(&state, seed); XXH64_update(&state, (const xxh_u8*)input, len); return XXH64_digest(&state); - #else - if (XXH_FORCE_ALIGN_CHECK) { if ((((size_t)input) & 7)==0) { /* Input is aligned, let's leverage the speed advantage */ return XXH64_endian_align((const xxh_u8*)input, len, seed, XXH_aligned); @@ -1953,44 +3252,45 @@ XXH_PUBLIC_API XXH64_hash_t XXH64 (const void* input, size_t len, XXH64_hash_t s } /******* Hash Streaming *******/ - +#ifndef XXH_NO_STREAM +/*! @ingroup XXH64_family*/ XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void) { return (XXH64_state_t*)XXH_malloc(sizeof(XXH64_state_t)); } +/*! @ingroup XXH64_family */ XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr) { XXH_free(statePtr); return XXH_OK; } -XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dstState, const XXH64_state_t* srcState) +/*! @ingroup XXH64_family */ +XXH_PUBLIC_API void XXH64_copyState(XXH_NOESCAPE XXH64_state_t* dstState, const XXH64_state_t* srcState) { - memcpy(dstState, srcState, sizeof(*dstState)); + XXH_memcpy(dstState, srcState, sizeof(*dstState)); } -XXH_PUBLIC_API XXH_errorcode XXH64_reset(XXH64_state_t* statePtr, XXH64_hash_t seed) +/*! @ingroup XXH64_family */ +XXH_PUBLIC_API XXH_errorcode XXH64_reset(XXH_NOESCAPE XXH64_state_t* statePtr, XXH64_hash_t seed) { - XXH64_state_t state; /* use a local state to memcpy() in order to avoid strict-aliasing warnings */ - memset(&state, 0, sizeof(state)); - state.v1 = seed + XXH_PRIME64_1 + XXH_PRIME64_2; - state.v2 = seed + XXH_PRIME64_2; - state.v3 = seed + 0; - state.v4 = seed - XXH_PRIME64_1; - /* do not write into reserved64, might be removed in a future version */ - memcpy(statePtr, &state, sizeof(state) - sizeof(state.reserved64)); + XXH_ASSERT(statePtr != NULL); + memset(statePtr, 0, sizeof(*statePtr)); + statePtr->v[0] = seed + XXH_PRIME64_1 + XXH_PRIME64_2; + statePtr->v[1] = seed + XXH_PRIME64_2; + statePtr->v[2] = seed + 0; + statePtr->v[3] = seed - XXH_PRIME64_1; return XXH_OK; } +/*! @ingroup XXH64_family */ XXH_PUBLIC_API XXH_errorcode -XXH64_update (XXH64_state_t* state, const void* input, size_t len) +XXH64_update (XXH_NOESCAPE XXH64_state_t* state, XXH_NOESCAPE const void* input, size_t len) { - if (input==NULL) -#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + if (input==NULL) { + XXH_ASSERT(len == 0); return XXH_OK; -#else - return XXH_ERROR; -#endif + } { const xxh_u8* p = (const xxh_u8*)input; const xxh_u8* const bEnd = p + len; @@ -2005,32 +3305,24 @@ XXH64_update (XXH64_state_t* state, const void* input, size_t len) if (state->memsize) { /* tmp buffer is full */ XXH_memcpy(((xxh_u8*)state->mem64) + state->memsize, input, 32-state->memsize); - state->v1 = XXH64_round(state->v1, XXH_readLE64(state->mem64+0)); - state->v2 = XXH64_round(state->v2, XXH_readLE64(state->mem64+1)); - state->v3 = XXH64_round(state->v3, XXH_readLE64(state->mem64+2)); - state->v4 = XXH64_round(state->v4, XXH_readLE64(state->mem64+3)); - p += 32-state->memsize; + state->v[0] = XXH64_round(state->v[0], XXH_readLE64(state->mem64+0)); + state->v[1] = XXH64_round(state->v[1], XXH_readLE64(state->mem64+1)); + state->v[2] = XXH64_round(state->v[2], XXH_readLE64(state->mem64+2)); + state->v[3] = XXH64_round(state->v[3], XXH_readLE64(state->mem64+3)); + p += 32 - state->memsize; state->memsize = 0; } if (p+32 <= bEnd) { const xxh_u8* const limit = bEnd - 32; - xxh_u64 v1 = state->v1; - xxh_u64 v2 = state->v2; - xxh_u64 v3 = state->v3; - xxh_u64 v4 = state->v4; do { - v1 = XXH64_round(v1, XXH_readLE64(p)); p+=8; - v2 = XXH64_round(v2, XXH_readLE64(p)); p+=8; - v3 = XXH64_round(v3, XXH_readLE64(p)); p+=8; - v4 = XXH64_round(v4, XXH_readLE64(p)); p+=8; + state->v[0] = XXH64_round(state->v[0], XXH_readLE64(p)); p+=8; + state->v[1] = XXH64_round(state->v[1], XXH_readLE64(p)); p+=8; + state->v[2] = XXH64_round(state->v[2], XXH_readLE64(p)); p+=8; + state->v[3] = XXH64_round(state->v[3], XXH_readLE64(p)); p+=8; } while (p<=limit); - state->v1 = v1; - state->v2 = v2; - state->v3 = v3; - state->v4 = v4; } if (p < bEnd) { @@ -2043,58 +3335,72 @@ XXH64_update (XXH64_state_t* state, const void* input, size_t len) } -XXH_PUBLIC_API XXH64_hash_t XXH64_digest (const XXH64_state_t* state) +/*! @ingroup XXH64_family */ +XXH_PUBLIC_API XXH64_hash_t XXH64_digest(XXH_NOESCAPE const XXH64_state_t* state) { xxh_u64 h64; if (state->total_len >= 32) { - xxh_u64 const v1 = state->v1; - xxh_u64 const v2 = state->v2; - xxh_u64 const v3 = state->v3; - xxh_u64 const v4 = state->v4; - - h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18); - h64 = XXH64_mergeRound(h64, v1); - h64 = XXH64_mergeRound(h64, v2); - h64 = XXH64_mergeRound(h64, v3); - h64 = XXH64_mergeRound(h64, v4); + h64 = XXH_rotl64(state->v[0], 1) + XXH_rotl64(state->v[1], 7) + XXH_rotl64(state->v[2], 12) + XXH_rotl64(state->v[3], 18); + h64 = XXH64_mergeRound(h64, state->v[0]); + h64 = XXH64_mergeRound(h64, state->v[1]); + h64 = XXH64_mergeRound(h64, state->v[2]); + h64 = XXH64_mergeRound(h64, state->v[3]); } else { - h64 = state->v3 /*seed*/ + XXH_PRIME64_5; + h64 = state->v[2] /*seed*/ + XXH_PRIME64_5; } h64 += (xxh_u64) state->total_len; return XXH64_finalize(h64, (const xxh_u8*)state->mem64, (size_t)state->total_len, XXH_aligned); } - +#endif /* !XXH_NO_STREAM */ /******* Canonical representation *******/ -XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash) +/*! @ingroup XXH64_family */ +XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH_NOESCAPE XXH64_canonical_t* dst, XXH64_hash_t hash) { XXH_STATIC_ASSERT(sizeof(XXH64_canonical_t) == sizeof(XXH64_hash_t)); if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap64(hash); - memcpy(dst, &hash, sizeof(*dst)); + XXH_memcpy(dst, &hash, sizeof(*dst)); } -XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src) +/*! @ingroup XXH64_family */ +XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(XXH_NOESCAPE const XXH64_canonical_t* src) { return XXH_readBE64(src); } - +#ifndef XXH_NO_XXH3 /* ********************************************************************* * XXH3 * New generation hash designed for speed on small keys and vectorization ************************************************************************ */ +/*! + * @} + * @defgroup XXH3_impl XXH3 implementation + * @ingroup impl + * @{ + */ /* === Compiler specifics === */ -#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* >= C99 */ +#if ((defined(sun) || defined(__sun)) && __cplusplus) /* Solaris includes __STDC_VERSION__ with C++. Tested with GCC 5.5 */ +# define XXH_RESTRICT /* disable */ +#elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* >= C99 */ # define XXH_RESTRICT restrict +#elif (defined (__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))) \ + || (defined (__clang__)) \ + || (defined (_MSC_VER) && (_MSC_VER >= 1400)) \ + || (defined (__INTEL_COMPILER) && (__INTEL_COMPILER >= 1300)) +/* + * There are a LOT more compilers that recognize __restrict but this + * covers the major ones. + */ +# define XXH_RESTRICT __restrict #else -/* Note: it might be useful to define __restrict or __restrict__ for some C++ compilers */ # define XXH_RESTRICT /* disable */ #endif @@ -2108,17 +3414,33 @@ XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src # define XXH_unlikely(x) (x) #endif -#if defined(__GNUC__) -# if defined(__AVX2__) -# include -# elif defined(__SSE2__) -# include -# elif defined(__ARM_NEON__) || defined(__ARM_NEON) +#ifndef XXH_HAS_INCLUDE +# ifdef __has_include +# define XXH_HAS_INCLUDE(x) __has_include(x) +# else +# define XXH_HAS_INCLUDE(x) 0 +# endif +#endif + +#if defined(__GNUC__) || defined(__clang__) +# if defined(__ARM_FEATURE_SVE) +# include +# endif +# if defined(__ARM_NEON__) || defined(__ARM_NEON) \ + || (defined(_M_ARM) && _M_ARM >= 7) \ + || defined(_M_ARM64) || defined(_M_ARM64EC) \ + || (defined(__wasm_simd128__) && XXH_HAS_INCLUDE()) /* WASM SIMD128 via SIMDe */ # define inline __inline__ /* circumvent a clang bug */ # include # undef inline +# elif defined(__AVX2__) +# include +# elif defined(__SSE2__) +# include # endif -#elif defined(_MSC_VER) +#endif + +#if defined(_MSC_VER) # include #endif @@ -2198,25 +3520,87 @@ XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src /* ========================================== * Vectorization detection * ========================================== */ -#define XXH_SCALAR 0 /* Portable scalar version */ -#define XXH_SSE2 1 /* SSE2 for Pentium 4 and all x86_64 */ -#define XXH_AVX2 2 /* AVX2 for Haswell and Bulldozer */ -#define XXH_AVX512 3 /* AVX512 for Skylake and Icelake */ -#define XXH_NEON 4 /* NEON for most ARMv7-A and all AArch64 */ -#define XXH_VSX 5 /* VSX and ZVector for POWER8/z13 */ + +#ifdef XXH_DOXYGEN +/*! + * @ingroup tuning + * @brief Overrides the vectorization implementation chosen for XXH3. + * + * Can be defined to 0 to disable SIMD or any of the values mentioned in + * @ref XXH_VECTOR_TYPE. + * + * If this is not defined, it uses predefined macros to determine the best + * implementation. + */ +# define XXH_VECTOR XXH_SCALAR +/*! + * @ingroup tuning + * @brief Possible values for @ref XXH_VECTOR. + * + * Note that these are actually implemented as macros. + * + * If this is not defined, it is detected automatically. + * internal macro XXH_X86DISPATCH overrides this. + */ +enum XXH_VECTOR_TYPE /* fake enum */ { + XXH_SCALAR = 0, /*!< Portable scalar version */ + XXH_SSE2 = 1, /*!< + * SSE2 for Pentium 4, Opteron, all x86_64. + * + * @note SSE2 is also guaranteed on Windows 10, macOS, and + * Android x86. + */ + XXH_AVX2 = 2, /*!< AVX2 for Haswell and Bulldozer */ + XXH_AVX512 = 3, /*!< AVX512 for Skylake and Icelake */ + XXH_NEON = 4, /*!< + * NEON for most ARMv7-A, all AArch64, and WASM SIMD128 + * via the SIMDeverywhere polyfill provided with the + * Emscripten SDK. + */ + XXH_VSX = 5, /*!< VSX and ZVector for POWER8/z13 (64-bit) */ + XXH_SVE = 6, /*!< SVE for some ARMv8-A and ARMv9-A */ +}; +/*! + * @ingroup tuning + * @brief Selects the minimum alignment for XXH3's accumulators. + * + * When using SIMD, this should match the alignment required for said vector + * type, so, for example, 32 for AVX2. + * + * Default: Auto detected. + */ +# define XXH_ACC_ALIGN 8 +#endif + +/* Actual definition */ +#ifndef XXH_DOXYGEN +# define XXH_SCALAR 0 +# define XXH_SSE2 1 +# define XXH_AVX2 2 +# define XXH_AVX512 3 +# define XXH_NEON 4 +# define XXH_VSX 5 +# define XXH_SVE 6 +#endif #ifndef XXH_VECTOR /* can be defined on command line */ -# if defined(__AVX512F__) +# if defined(__ARM_FEATURE_SVE) +# define XXH_VECTOR XXH_SVE +# elif ( \ + defined(__ARM_NEON__) || defined(__ARM_NEON) /* gcc */ \ + || defined(_M_ARM) || defined(_M_ARM64) || defined(_M_ARM64EC) /* msvc */ \ + || (defined(__wasm_simd128__) && XXH_HAS_INCLUDE()) /* wasm simd128 via SIMDe */ \ + ) && ( \ + defined(_WIN32) || defined(__LITTLE_ENDIAN__) /* little endian only */ \ + || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) \ + ) +# define XXH_VECTOR XXH_NEON +# elif defined(__AVX512F__) # define XXH_VECTOR XXH_AVX512 # elif defined(__AVX2__) # define XXH_VECTOR XXH_AVX2 # elif defined(__SSE2__) || defined(_M_AMD64) || defined(_M_X64) || (defined(_M_IX86_FP) && (_M_IX86_FP == 2)) # define XXH_VECTOR XXH_SSE2 -# elif defined(__GNUC__) /* msvc support maybe later */ \ - && (defined(__ARM_NEON__) || defined(__ARM_NEON)) \ - && (defined(__LITTLE_ENDIAN__) /* We only support little endian NEON */ \ - || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) -# define XXH_VECTOR XXH_NEON # elif (defined(__PPC64__) && defined(__POWER8_VECTOR__)) \ || (defined(__s390x__) && defined(__VEC__)) \ && defined(__GNUC__) /* TODO: IBM XL */ @@ -2226,6 +3610,17 @@ XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src # endif #endif +/* __ARM_FEATURE_SVE is only supported by GCC & Clang. */ +#if (XXH_VECTOR == XXH_SVE) && !defined(__ARM_FEATURE_SVE) +# ifdef _MSC_VER +# pragma warning(once : 4606) +# else +# warning "__ARM_FEATURE_SVE isn't supported. Use SCALAR instead." +# endif +# undef XXH_VECTOR +# define XXH_VECTOR XXH_SCALAR +#endif + /* * Controls the alignment of the accumulator, * for compatibility with aligned vector loads, which are usually faster. @@ -2245,16 +3640,26 @@ XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src # define XXH_ACC_ALIGN 16 # elif XXH_VECTOR == XXH_AVX512 /* avx512 */ # define XXH_ACC_ALIGN 64 +# elif XXH_VECTOR == XXH_SVE /* sve */ +# define XXH_ACC_ALIGN 64 # endif #endif #if defined(XXH_X86DISPATCH) || XXH_VECTOR == XXH_SSE2 \ || XXH_VECTOR == XXH_AVX2 || XXH_VECTOR == XXH_AVX512 # define XXH_SEC_ALIGN XXH_ACC_ALIGN +#elif XXH_VECTOR == XXH_SVE +# define XXH_SEC_ALIGN XXH_ACC_ALIGN #else # define XXH_SEC_ALIGN 8 #endif +#if defined(__GNUC__) || defined(__clang__) +# define XXH_ALIASING __attribute__((may_alias)) +#else +# define XXH_ALIASING /* nothing */ +#endif + /* * UGLY HACK: * GCC usually generates the best code with -O3 for xxHash. @@ -2278,111 +3683,130 @@ XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src */ #if XXH_VECTOR == XXH_AVX2 /* AVX2 */ \ && defined(__GNUC__) && !defined(__clang__) /* GCC, not Clang */ \ - && defined(__OPTIMIZE__) && !defined(__OPTIMIZE_SIZE__) /* respect -O0 and -Os */ + && defined(__OPTIMIZE__) && XXH_SIZE_OPT <= 0 /* respect -O0 and -Os */ # pragma GCC push_options # pragma GCC optimize("-O2") #endif - #if XXH_VECTOR == XXH_NEON + /* - * NEON's setup for vmlal_u32 is a little more complicated than it is on - * SSE2, AVX2, and VSX. - * - * While PMULUDQ and VMULEUW both perform a mask, VMLAL.U32 performs an upcast. - * - * To do the same operation, the 128-bit 'Q' register needs to be split into - * two 64-bit 'D' registers, performing this operation:: - * - * [ a | b ] - * | '---------. .--------' | - * | x | - * | .---------' '--------. | - * [ a & 0xFFFFFFFF | b & 0xFFFFFFFF ],[ a >> 32 | b >> 32 ] - * - * Due to significant changes in aarch64, the fastest method for aarch64 is - * completely different than the fastest method for ARMv7-A. + * UGLY HACK: While AArch64 GCC on Linux does not seem to care, on macOS, GCC -O3 + * optimizes out the entire hashLong loop because of the aliasing violation. * - * ARMv7-A treats D registers as unions overlaying Q registers, so modifying - * D11 will modify the high half of Q5. This is similar to how modifying AH - * will only affect bits 8-15 of AX on x86. - * - * VZIP takes two registers, and puts even lanes in one register and odd lanes - * in the other. + * However, GCC is also inefficient at load-store optimization with vld1q/vst1q, + * so the only option is to mark it as aliasing. + */ +typedef uint64x2_t xxh_aliasing_uint64x2_t XXH_ALIASING; + +/*! + * @internal + * @brief `vld1q_u64` but faster and alignment-safe. * - * On ARMv7-A, this strangely modifies both parameters in place instead of - * taking the usual 3-operand form. + * On AArch64, unaligned access is always safe, but on ARMv7-a, it is only + * *conditionally* safe (`vld1` has an alignment bit like `movdq[ua]` in x86). * - * Therefore, if we want to do this, we can simply use a D-form VZIP.32 on the - * lower and upper halves of the Q register to end up with the high and low - * halves where we want - all in one instruction. + * GCC for AArch64 sees `vld1q_u8` as an intrinsic instead of a load, so it + * prohibits load-store optimizations. Therefore, a direct dereference is used. * - * vzip.32 d10, d11 @ d10 = { d10[0], d11[0] }; d11 = { d10[1], d11[1] } + * Otherwise, `vld1q_u8` is used with `vreinterpretq_u8_u64` to do a safe + * unaligned load. + */ +#if defined(__aarch64__) && defined(__GNUC__) && !defined(__clang__) +XXH_FORCE_INLINE uint64x2_t XXH_vld1q_u64(void const* ptr) /* silence -Wcast-align */ +{ + return *(xxh_aliasing_uint64x2_t const *)ptr; +} +#else +XXH_FORCE_INLINE uint64x2_t XXH_vld1q_u64(void const* ptr) +{ + return vreinterpretq_u64_u8(vld1q_u8((uint8_t const*)ptr)); +} +#endif + +/*! + * @internal + * @brief `vmlal_u32` on low and high halves of a vector. * - * Unfortunately we need inline assembly for this: Instructions modifying two - * registers at once is not possible in GCC or Clang's IR, and they have to - * create a copy. + * This is a workaround for AArch64 GCC < 11 which implemented arm_neon.h with + * inline assembly and were therefore incapable of merging the `vget_{low, high}_u32` + * with `vmlal_u32`. + */ +#if defined(__aarch64__) && defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 11 +XXH_FORCE_INLINE uint64x2_t +XXH_vmlal_low_u32(uint64x2_t acc, uint32x4_t lhs, uint32x4_t rhs) +{ + /* Inline assembly is the only way */ + __asm__("umlal %0.2d, %1.2s, %2.2s" : "+w" (acc) : "w" (lhs), "w" (rhs)); + return acc; +} +XXH_FORCE_INLINE uint64x2_t +XXH_vmlal_high_u32(uint64x2_t acc, uint32x4_t lhs, uint32x4_t rhs) +{ + /* This intrinsic works as expected */ + return vmlal_high_u32(acc, lhs, rhs); +} +#else +/* Portable intrinsic versions */ +XXH_FORCE_INLINE uint64x2_t +XXH_vmlal_low_u32(uint64x2_t acc, uint32x4_t lhs, uint32x4_t rhs) +{ + return vmlal_u32(acc, vget_low_u32(lhs), vget_low_u32(rhs)); +} +/*! @copydoc XXH_vmlal_low_u32 + * Assume the compiler converts this to vmlal_high_u32 on aarch64 */ +XXH_FORCE_INLINE uint64x2_t +XXH_vmlal_high_u32(uint64x2_t acc, uint32x4_t lhs, uint32x4_t rhs) +{ + return vmlal_u32(acc, vget_high_u32(lhs), vget_high_u32(rhs)); +} +#endif + +/*! + * @ingroup tuning + * @brief Controls the NEON to scalar ratio for XXH3 * - * aarch64 requires a different approach. + * This can be set to 2, 4, 6, or 8. * - * In order to make it easier to write a decent compiler for aarch64, many - * quirks were removed, such as conditional execution. + * ARM Cortex CPUs are _very_ sensitive to how their pipelines are used. * - * NEON was also affected by this. + * For example, the Cortex-A73 can dispatch 3 micro-ops per cycle, but only 2 of those + * can be NEON. If you are only using NEON instructions, you are only using 2/3 of the CPU + * bandwidth. * - * aarch64 cannot access the high bits of a Q-form register, and writes to a - * D-form register zero the high bits, similar to how writes to W-form scalar - * registers (or DWORD registers on x86_64) work. + * This is even more noticeable on the more advanced cores like the Cortex-A76 which + * can dispatch 8 micro-ops per cycle, but still only 2 NEON micro-ops at once. * - * The formerly free vget_high intrinsics now require a vext (with a few - * exceptions) + * Therefore, to make the most out of the pipeline, it is beneficial to run 6 NEON lanes + * and 2 scalar lanes, which is chosen by default. * - * Additionally, VZIP was replaced by ZIP1 and ZIP2, which are the equivalent - * of PUNPCKL* and PUNPCKH* in SSE, respectively, in order to only modify one - * operand. + * This does not apply to Apple processors or 32-bit processors, which run better with + * full NEON. These will default to 8. Additionally, size-optimized builds run 8 lanes. * - * The equivalent of the VZIP.32 on the lower and upper halves would be this - * mess: + * This change benefits CPUs with large micro-op buffers without negatively affecting + * most other CPUs: * - * ext v2.4s, v0.4s, v0.4s, #2 // v2 = { v0[2], v0[3], v0[0], v0[1] } - * zip1 v1.2s, v0.2s, v2.2s // v1 = { v0[0], v2[0] } - * zip2 v0.2s, v0.2s, v1.2s // v0 = { v0[1], v2[1] } + * | Chipset | Dispatch type | NEON only | 6:2 hybrid | Diff. | + * |:----------------------|:--------------------|----------:|-----------:|------:| + * | Snapdragon 730 (A76) | 2 NEON/8 micro-ops | 8.8 GB/s | 10.1 GB/s | ~16% | + * | Snapdragon 835 (A73) | 2 NEON/3 micro-ops | 5.1 GB/s | 5.3 GB/s | ~5% | + * | Marvell PXA1928 (A53) | In-order dual-issue | 1.9 GB/s | 1.9 GB/s | 0% | + * | Apple M1 | 4 NEON/8 micro-ops | 37.3 GB/s | 36.1 GB/s | ~-3% | * - * Instead, we use a literal downcast, vmovn_u64 (XTN), and vshrn_n_u64 (SHRN): + * It also seems to fix some bad codegen on GCC, making it almost as fast as clang. * - * shrn v1.2s, v0.2d, #32 // v1 = (uint32x2_t)(v0 >> 32); - * xtn v0.2s, v0.2d // v0 = (uint32x2_t)(v0 & 0xFFFFFFFF); + * When using WASM SIMD128, if this is 2 or 6, SIMDe will scalarize 2 of the lanes meaning + * it effectively becomes worse 4. * - * This is available on ARMv7-A, but is less efficient than a single VZIP.32. + * @see XXH3_accumulate_512_neon() */ - -/* - * Function-like macro: - * void XXH_SPLIT_IN_PLACE(uint64x2_t &in, uint32x2_t &outLo, uint32x2_t &outHi) - * { - * outLo = (uint32x2_t)(in & 0xFFFFFFFF); - * outHi = (uint32x2_t)(in >> 32); - * in = UNDEFINED; - * } - */ -# if !defined(XXH_NO_VZIP_HACK) /* define to disable */ \ - && defined(__GNUC__) \ - && !defined(__aarch64__) && !defined(__arm64__) -# define XXH_SPLIT_IN_PLACE(in, outLo, outHi) \ - do { \ - /* Undocumented GCC/Clang operand modifier: %e0 = lower D half, %f0 = upper D half */ \ - /* https://github.com/gcc-mirror/gcc/blob/38cf91e5/gcc/config/arm/arm.c#L22486 */ \ - /* https://github.com/llvm-mirror/llvm/blob/2c4ca683/lib/Target/ARM/ARMAsmPrinter.cpp#L399 */ \ - __asm__("vzip.32 %e0, %f0" : "+w" (in)); \ - (outLo) = vget_low_u32 (vreinterpretq_u32_u64(in)); \ - (outHi) = vget_high_u32(vreinterpretq_u32_u64(in)); \ - } while (0) -# else -# define XXH_SPLIT_IN_PLACE(in, outLo, outHi) \ - do { \ - (outLo) = vmovn_u64 (in); \ - (outHi) = vshrn_n_u64 ((in), 32); \ - } while (0) +# ifndef XXH3_NEON_LANES +# if (defined(__aarch64__) || defined(__arm64__) || defined(_M_ARM64) || defined(_M_ARM64EC)) \ + && !defined(__APPLE__) && XXH_SIZE_OPT <= 0 +# define XXH3_NEON_LANES 6 +# else +# define XXH3_NEON_LANES XXH_ACC_NB +# endif # endif #endif /* XXH_VECTOR == XXH_NEON */ @@ -2395,27 +3819,42 @@ XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src * inconsistent intrinsics, spotty coverage, and multiple endiannesses. */ #if XXH_VECTOR == XXH_VSX +/* Annoyingly, these headers _may_ define three macros: `bool`, `vector`, + * and `pixel`. This is a problem for obvious reasons. + * + * These keywords are unnecessary; the spec literally says they are + * equivalent to `__bool`, `__vector`, and `__pixel` and may be undef'd + * after including the header. + * + * We use pragma push_macro/pop_macro to keep the namespace clean. */ +# pragma push_macro("bool") +# pragma push_macro("vector") +# pragma push_macro("pixel") +/* silence potential macro redefined warnings */ +# undef bool +# undef vector +# undef pixel + # if defined(__s390x__) # include # else -/* gcc's altivec.h can have the unwanted consequence to unconditionally - * #define bool, vector, and pixel keywords, - * with bad consequences for programs already using these keywords for other purposes. - * The paragraph defining these macros is skipped when __APPLE_ALTIVEC__ is defined. - * __APPLE_ALTIVEC__ is _generally_ defined automatically by the compiler, - * but it seems that, in some cases, it isn't. - * Force the build macro to be defined, so that keywords are not altered. - */ -# if defined(__GNUC__) && !defined(__APPLE_ALTIVEC__) -# define __APPLE_ALTIVEC__ -# endif # include # endif +/* Restore the original macro values, if applicable. */ +# pragma pop_macro("pixel") +# pragma pop_macro("vector") +# pragma pop_macro("bool") + typedef __vector unsigned long long xxh_u64x2; typedef __vector unsigned char xxh_u8x16; typedef __vector unsigned xxh_u32x4; +/* + * UGLY HACK: Similar to aarch64 macOS GCC, s390x GCC has the same aliasing issue. + */ +typedef xxh_u64x2 xxh_aliasing_u64x2 XXH_ALIASING; + # ifndef XXH_VSX_BE # if defined(__BIG_ENDIAN__) \ || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) @@ -2429,10 +3868,12 @@ typedef __vector unsigned xxh_u32x4; # endif /* !defined(XXH_VSX_BE) */ # if XXH_VSX_BE -/* A wrapper for POWER9's vec_revb. */ # if defined(__POWER9_VECTOR__) || (defined(__clang__) && defined(__s390x__)) # define XXH_vec_revb vec_revb # else +/*! + * A polyfill for POWER9's vec_revb(). + */ XXH_FORCE_INLINE xxh_u64x2 XXH_vec_revb(xxh_u64x2 val) { xxh_u8x16 const vByteSwap = { 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, @@ -2442,13 +3883,13 @@ XXH_FORCE_INLINE xxh_u64x2 XXH_vec_revb(xxh_u64x2 val) # endif # endif /* XXH_VSX_BE */ -/* - * Performs an unaligned load and byte swaps it on big endian. +/*! + * Performs an unaligned vector load and byte swaps it on big endian. */ XXH_FORCE_INLINE xxh_u64x2 XXH_vec_loadu(const void *ptr) { xxh_u64x2 ret; - memcpy(&ret, ptr, sizeof(xxh_u64x2)); + XXH_memcpy(&ret, ptr, sizeof(xxh_u64x2)); # if XXH_VSX_BE ret = XXH_vec_revb(ret); # endif @@ -2465,8 +3906,9 @@ XXH_FORCE_INLINE xxh_u64x2 XXH_vec_loadu(const void *ptr) /* s390x is always big endian, no issue on this platform */ # define XXH_vec_mulo vec_mulo # define XXH_vec_mule vec_mule -# elif defined(__clang__) && XXH_HAS_BUILTIN(__builtin_altivec_vmuleuw) +# elif defined(__clang__) && XXH_HAS_BUILTIN(__builtin_altivec_vmuleuw) && !defined(__ibmxl__) /* Clang has a better way to control this, we can just use the builtin which doesn't swap. */ + /* The IBM XL Compiler (which defined __clang__) only implements the vec_* operations */ # define XXH_vec_mulo __builtin_altivec_vmulouw # define XXH_vec_mule __builtin_altivec_vmuleuw # else @@ -2487,13 +3929,28 @@ XXH_FORCE_INLINE xxh_u64x2 XXH_vec_mule(xxh_u32x4 a, xxh_u32x4 b) # endif /* XXH_vec_mulo, XXH_vec_mule */ #endif /* XXH_VECTOR == XXH_VSX */ +#if XXH_VECTOR == XXH_SVE +#define ACCRND(acc, offset) \ +do { \ + svuint64_t input_vec = svld1_u64(mask, xinput + offset); \ + svuint64_t secret_vec = svld1_u64(mask, xsecret + offset); \ + svuint64_t mixed = sveor_u64_x(mask, secret_vec, input_vec); \ + svuint64_t swapped = svtbl_u64(input_vec, kSwap); \ + svuint64_t mixed_lo = svextw_u64_x(mask, mixed); \ + svuint64_t mixed_hi = svlsr_n_u64_x(mask, mixed, 32); \ + svuint64_t mul = svmad_u64_x(mask, mixed_lo, mixed_hi, swapped); \ + acc = svadd_u64_x(mask, acc, mul); \ +} while (0) +#endif /* XXH_VECTOR == XXH_SVE */ /* prefetch * can be disabled, by declaring XXH_NO_PREFETCH build macro */ #if defined(XXH_NO_PREFETCH) # define XXH_PREFETCH(ptr) (void)(ptr) /* disabled */ #else -# if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_I86)) /* _mm_prefetch() is not defined outside of x86/x64 */ +# if XXH_SIZE_OPT >= 1 +# define XXH_PREFETCH(ptr) (void)(ptr) +# elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_IX86)) /* _mm_prefetch() not defined outside of x86/x64 */ # include /* https://msdn.microsoft.com/fr-fr/library/84szxsww(v=vs.90).aspx */ # define XXH_PREFETCH(ptr) _mm_prefetch((const char*)(ptr), _MM_HINT_T0) # elif defined(__GNUC__) && ( (__GNUC__ >= 4) || ( (__GNUC__ == 3) && (__GNUC_MINOR__ >= 1) ) ) @@ -2514,7 +3971,7 @@ XXH_FORCE_INLINE xxh_u64x2 XXH_vec_mule(xxh_u32x4 a, xxh_u32x4 b) # error "default keyset is not large enough" #endif -/* Pseudorandom secret taken directly from FARSH */ +/*! Pseudorandom secret taken directly from FARSH. */ XXH_ALIGN(64) static const xxh_u8 XXH3_kSecret[XXH_SECRET_DEFAULT_SIZE] = { 0xb8, 0xfe, 0x6c, 0x39, 0x23, 0xa4, 0x4b, 0xbe, 0x7c, 0x01, 0x81, 0x2c, 0xf7, 0x21, 0xad, 0x1c, 0xde, 0xd4, 0x6d, 0xe9, 0x83, 0x90, 0x97, 0xdb, 0x72, 0x40, 0xa4, 0xa4, 0xb7, 0xb3, 0x67, 0x1f, @@ -2530,29 +3987,36 @@ XXH_ALIGN(64) static const xxh_u8 XXH3_kSecret[XXH_SECRET_DEFAULT_SIZE] = { 0x45, 0xcb, 0x3a, 0x8f, 0x95, 0x16, 0x04, 0x28, 0xaf, 0xd7, 0xfb, 0xca, 0xbb, 0x4b, 0x40, 0x7e, }; +static const xxh_u64 PRIME_MX1 = 0x165667919E3779F9ULL; /*!< 0b0001011001010110011001111001000110011110001101110111100111111001 */ +static const xxh_u64 PRIME_MX2 = 0x9FB21C651E98DF25ULL; /*!< 0b1001111110110010000111000110010100011110100110001101111100100101 */ #ifdef XXH_OLD_NAMES # define kSecret XXH3_kSecret #endif -/* - * Calculates a 32-bit to 64-bit long multiply. +#ifdef XXH_DOXYGEN +/*! + * @brief Calculates a 32-bit to 64-bit long multiply. + * + * Implemented as a macro. * - * Wraps __emulu on MSVC x86 because it tends to call __allmul when it doesn't + * Wraps `__emulu` on MSVC x86 because it tends to call `__allmul` when it doesn't * need to (but it shouldn't need to anyways, it is about 7 instructions to do - * a 64x64 multiply...). Since we know that this will _always_ emit MULL, we + * a 64x64 multiply...). Since we know that this will _always_ emit `MULL`, we * use that instead of the normal method. * * If you are compiling for platforms like Thumb-1 and don't have a better option, * you may also want to write your own long multiply routine here. * - * XXH_FORCE_INLINE xxh_u64 XXH_mult32to64(xxh_u64 x, xxh_u64 y) - * { - * return (x & 0xFFFFFFFF) * (y & 0xFFFFFFFF); - * } + * @param x, y Numbers to be multiplied + * @return 64-bit product of the low 32 bits of @p x and @p y. */ -#if defined(_MSC_VER) && defined(_M_IX86) -# include +XXH_FORCE_INLINE xxh_u64 +XXH_mult32to64(xxh_u64 x, xxh_u64 y) +{ + return (x & 0xFFFFFFFF) * (y & 0xFFFFFFFF); +} +#elif defined(_MSC_VER) && defined(_M_IX86) # define XXH_mult32to64(x, y) __emulu((unsigned)(x), (unsigned)(y)) #else /* @@ -2565,10 +4029,14 @@ XXH_ALIGN(64) static const xxh_u8 XXH3_kSecret[XXH_SECRET_DEFAULT_SIZE] = { # define XXH_mult32to64(x, y) ((xxh_u64)(xxh_u32)(x) * (xxh_u64)(xxh_u32)(y)) #endif -/* - * Calculates a 64->128-bit long multiply. +/*! + * @brief Calculates a 64->128-bit long multiply. + * + * Uses `__uint128_t` and `_umul128` if available, otherwise uses a scalar + * version. * - * Uses __uint128_t and _umul128 if available, otherwise uses a scalar version. + * @param lhs , rhs The 64-bit integers to be multiplied + * @return The 128-bit result represented in an @ref XXH128_hash_t. */ static XXH128_hash_t XXH_mult64to128(xxh_u64 lhs, xxh_u64 rhs) @@ -2588,7 +4056,7 @@ XXH_mult64to128(xxh_u64 lhs, xxh_u64 rhs) * In that case it is best to use the portable one. * https://github.com/Cyan4973/xxHash/issues/211#issuecomment-515575677 */ -#if defined(__GNUC__) && !defined(__wasm__) \ +#if (defined(__GNUC__) || defined(__clang__)) && !defined(__wasm__) \ && defined(__SIZEOF_INT128__) \ || (defined(_INTEGRAL_MAX_BITS) && _INTEGRAL_MAX_BITS >= 128) @@ -2605,7 +4073,7 @@ XXH_mult64to128(xxh_u64 lhs, xxh_u64 rhs) * * This compiles to single operand MUL on x64. */ -#elif defined(_M_X64) || defined(_M_IA64) +#elif (defined(_M_X64) || defined(_M_IA64)) && !defined(_M_ARM64EC) #ifndef _MSC_VER # pragma intrinsic(_umul128) @@ -2617,6 +4085,21 @@ XXH_mult64to128(xxh_u64 lhs, xxh_u64 rhs) r128.high64 = product_high; return r128; + /* + * MSVC for ARM64's __umulh method. + * + * This compiles to the same MUL + UMULH as GCC/Clang's __uint128_t method. + */ +#elif defined(_M_ARM64) || defined(_M_ARM64EC) + +#ifndef _MSC_VER +# pragma intrinsic(__umulh) +#endif + XXH128_hash_t r128; + r128.low64 = lhs * rhs; + r128.high64 = __umulh(lhs, rhs); + return r128; + #else /* * Portable scalar method. Optimized for 32-bit and 64-bit ALUs. @@ -2679,11 +4162,15 @@ XXH_mult64to128(xxh_u64 lhs, xxh_u64 rhs) #endif } -/* - * Does a 64-bit to 128-bit multiply, then XOR folds it. +/*! + * @brief Calculates a 64-bit to 128-bit multiply, then XOR folds it. * * The reason for the separate function is to prevent passing too many structs * around by value. This will hopefully inline the multiply, but we don't force it. + * + * @param lhs , rhs The 64-bit integers to multiply + * @return The low 64 bits of the product XOR'd by the high 64 bits. + * @see XXH_mult64to128() */ static xxh_u64 XXH3_mul128_fold64(xxh_u64 lhs, xxh_u64 rhs) @@ -2692,8 +4179,8 @@ XXH3_mul128_fold64(xxh_u64 lhs, xxh_u64 rhs) return product.low64 ^ product.high64; } -/* Seems to produce slightly better code on GCC for some reason. */ -XXH_FORCE_INLINE xxh_u64 XXH_xorshift64(xxh_u64 v64, int shift) +/*! Seems to produce slightly better code on GCC for some reason. */ +XXH_FORCE_INLINE XXH_CONSTF xxh_u64 XXH_xorshift64(xxh_u64 v64, int shift) { XXH_ASSERT(0 <= shift && shift < 64); return v64 ^ (v64 >> shift); @@ -2706,7 +4193,7 @@ XXH_FORCE_INLINE xxh_u64 XXH_xorshift64(xxh_u64 v64, int shift) static XXH64_hash_t XXH3_avalanche(xxh_u64 h64) { h64 = XXH_xorshift64(h64, 37); - h64 *= 0x165667919E3779F9ULL; + h64 *= PRIME_MX1; h64 = XXH_xorshift64(h64, 32); return h64; } @@ -2720,9 +4207,9 @@ static XXH64_hash_t XXH3_rrmxmx(xxh_u64 h64, xxh_u64 len) { /* this mix is inspired by Pelle Evensen's rrmxmx */ h64 ^= XXH_rotl64(h64, 49) ^ XXH_rotl64(h64, 24); - h64 *= 0x9FB21C651E98DF25ULL; + h64 *= PRIME_MX2; h64 ^= (h64 >> 35) + len ; - h64 *= 0x9FB21C651E98DF25ULL; + h64 *= PRIME_MX2; return XXH_xorshift64(h64, 28); } @@ -2760,7 +4247,7 @@ static XXH64_hash_t XXH3_rrmxmx(xxh_u64 h64, xxh_u64 len) * * This adds an extra layer of strength for custom secrets. */ -XXH_FORCE_INLINE XXH64_hash_t +XXH_FORCE_INLINE XXH_PUREF XXH64_hash_t XXH3_len_1to3_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) { XXH_ASSERT(input != NULL); @@ -2782,12 +4269,12 @@ XXH3_len_1to3_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_h } } -XXH_FORCE_INLINE XXH64_hash_t +XXH_FORCE_INLINE XXH_PUREF XXH64_hash_t XXH3_len_4to8_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) { XXH_ASSERT(input != NULL); XXH_ASSERT(secret != NULL); - XXH_ASSERT(4 <= len && len < 8); + XXH_ASSERT(4 <= len && len <= 8); seed ^= (xxh_u64)XXH_swap32((xxh_u32)seed) << 32; { xxh_u32 const input1 = XXH_readLE32(input); xxh_u32 const input2 = XXH_readLE32(input + len - 4); @@ -2798,12 +4285,12 @@ XXH3_len_4to8_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_h } } -XXH_FORCE_INLINE XXH64_hash_t +XXH_FORCE_INLINE XXH_PUREF XXH64_hash_t XXH3_len_9to16_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) { XXH_ASSERT(input != NULL); XXH_ASSERT(secret != NULL); - XXH_ASSERT(8 <= len && len <= 16); + XXH_ASSERT(9 <= len && len <= 16); { xxh_u64 const bitflip1 = (XXH_readLE64(secret+24) ^ XXH_readLE64(secret+32)) + seed; xxh_u64 const bitflip2 = (XXH_readLE64(secret+40) ^ XXH_readLE64(secret+48)) - seed; xxh_u64 const input_lo = XXH_readLE64(input) ^ bitflip1; @@ -2815,7 +4302,7 @@ XXH3_len_9to16_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_ } } -XXH_FORCE_INLINE XXH64_hash_t +XXH_FORCE_INLINE XXH_PUREF XXH64_hash_t XXH3_len_0to16_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) { XXH_ASSERT(len <= 16); @@ -2873,7 +4360,7 @@ XXH_FORCE_INLINE xxh_u64 XXH3_mix16B(const xxh_u8* XXH_RESTRICT input, * GCC generates much better scalar code than Clang for the rest of XXH3, * which is why finding a more optimal codepath is an interest. */ - __asm__ ("" : "+r" (seed64)); + XXH_COMPILER_GUARD(seed64); #endif { xxh_u64 const input_lo = XXH_readLE64(input); xxh_u64 const input_hi = XXH_readLE64(input+8); @@ -2885,7 +4372,7 @@ XXH_FORCE_INLINE xxh_u64 XXH3_mix16B(const xxh_u8* XXH_RESTRICT input, } /* For mid range keys, XXH3 uses a Mum-hash variant. */ -XXH_FORCE_INLINE XXH64_hash_t +XXH_FORCE_INLINE XXH_PUREF XXH64_hash_t XXH3_len_17to128_64b(const xxh_u8* XXH_RESTRICT input, size_t len, const xxh_u8* XXH_RESTRICT secret, size_t secretSize, XXH64_hash_t seed) @@ -2894,6 +4381,14 @@ XXH3_len_17to128_64b(const xxh_u8* XXH_RESTRICT input, size_t len, XXH_ASSERT(16 < len && len <= 128); { xxh_u64 acc = len * XXH_PRIME64_1; +#if XXH_SIZE_OPT >= 1 + /* Smaller and cleaner, but slightly slower. */ + unsigned int i = (unsigned int)(len - 1) / 32; + do { + acc += XXH3_mix16B(input+16 * i, secret+32*i, seed); + acc += XXH3_mix16B(input+len-16*(i+1), secret+32*i+16, seed); + } while (i-- != 0); +#else if (len > 32) { if (len > 64) { if (len > 96) { @@ -2908,14 +4403,14 @@ XXH3_len_17to128_64b(const xxh_u8* XXH_RESTRICT input, size_t len, } acc += XXH3_mix16B(input+0, secret+0, seed); acc += XXH3_mix16B(input+len-16, secret+16, seed); - +#endif return XXH3_avalanche(acc); } } #define XXH3_MIDSIZE_MAX 240 -XXH_NO_INLINE XXH64_hash_t +XXH_NO_INLINE XXH_PUREF XXH64_hash_t XXH3_len_129to240_64b(const xxh_u8* XXH_RESTRICT input, size_t len, const xxh_u8* XXH_RESTRICT secret, size_t secretSize, XXH64_hash_t seed) @@ -2927,13 +4422,17 @@ XXH3_len_129to240_64b(const xxh_u8* XXH_RESTRICT input, size_t len, #define XXH3_MIDSIZE_LASTOFFSET 17 { xxh_u64 acc = len * XXH_PRIME64_1; - int const nbRounds = (int)len / 16; - int i; + xxh_u64 acc_end; + unsigned int const nbRounds = (unsigned int)len / 16; + unsigned int i; + XXH_ASSERT(128 < len && len <= XXH3_MIDSIZE_MAX); for (i=0; i<8; i++) { acc += XXH3_mix16B(input+(16*i), secret+(16*i), seed); } - acc = XXH3_avalanche(acc); + /* last bytes */ + acc_end = XXH3_mix16B(input + len - 16, secret + XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET, seed); XXH_ASSERT(nbRounds >= 8); + acc = XXH3_avalanche(acc); #if defined(__clang__) /* Clang */ \ && (defined(__ARM_NEON) || defined(__ARM_NEON__)) /* NEON */ \ && !defined(XXH_ENABLE_AUTOVECTORIZE) /* Define to disable */ @@ -2960,11 +4459,13 @@ XXH3_len_129to240_64b(const xxh_u8* XXH_RESTRICT input, size_t len, #pragma clang loop vectorize(disable) #endif for (i=8 ; i < nbRounds; i++) { - acc += XXH3_mix16B(input+(16*i), secret+(16*(i-8)) + XXH3_MIDSIZE_STARTOFFSET, seed); + /* + * Prevents clang for unrolling the acc loop and interleaving with this one. + */ + XXH_COMPILER_GUARD(acc); + acc_end += XXH3_mix16B(input+(16*i), secret+(16*(i-8)) + XXH3_MIDSIZE_STARTOFFSET, seed); } - /* last bytes */ - acc += XXH3_mix16B(input + len - 16, secret + XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET, seed); - return XXH3_avalanche(acc); + return XXH3_avalanche(acc + acc_end); } } @@ -2980,10 +4481,51 @@ XXH3_len_129to240_64b(const xxh_u8* XXH_RESTRICT input, size_t len, # define ACC_NB XXH_ACC_NB #endif +#ifndef XXH_PREFETCH_DIST +# ifdef __clang__ +# define XXH_PREFETCH_DIST 320 +# else +# if (XXH_VECTOR == XXH_AVX512) +# define XXH_PREFETCH_DIST 512 +# else +# define XXH_PREFETCH_DIST 384 +# endif +# endif /* __clang__ */ +#endif /* XXH_PREFETCH_DIST */ + +/* + * These macros are to generate an XXH3_accumulate() function. + * The two arguments select the name suffix and target attribute. + * + * The name of this symbol is XXH3_accumulate_() and it calls + * XXH3_accumulate_512_(). + * + * It may be useful to hand implement this function if the compiler fails to + * optimize the inline function. + */ +#define XXH3_ACCUMULATE_TEMPLATE(name) \ +void \ +XXH3_accumulate_##name(xxh_u64* XXH_RESTRICT acc, \ + const xxh_u8* XXH_RESTRICT input, \ + const xxh_u8* XXH_RESTRICT secret, \ + size_t nbStripes) \ +{ \ + size_t n; \ + for (n = 0; n < nbStripes; n++ ) { \ + const xxh_u8* const in = input + n*XXH_STRIPE_LEN; \ + XXH_PREFETCH(in + XXH_PREFETCH_DIST); \ + XXH3_accumulate_512_##name( \ + acc, \ + in, \ + secret + n*XXH_SECRET_CONSUME_RATE); \ + } \ +} + + XXH_FORCE_INLINE void XXH_writeLE64(void* dst, xxh_u64 v64) { if (!XXH_CPU_LITTLE_ENDIAN) v64 = XXH_swap64(v64); - memcpy(dst, &v64, sizeof(v64)); + XXH_memcpy(dst, &v64, sizeof(v64)); } /* Several intrinsic functions below are supposed to accept __int64 as argument, @@ -3000,6 +4542,7 @@ XXH_FORCE_INLINE void XXH_writeLE64(void* dst, xxh_u64 v64) typedef long long xxh_i64; #endif + /* * XXH3_accumulate_512 is the tightest loop for long inputs, and it is the most optimized. * @@ -3023,7 +4566,8 @@ XXH_FORCE_INLINE void XXH_writeLE64(void* dst, xxh_u64 v64) * Both XXH3_64bits and XXH3_128bits use this subroutine. */ -#if (XXH_VECTOR == XXH_AVX512) || defined(XXH_X86DISPATCH) +#if (XXH_VECTOR == XXH_AVX512) \ + || (defined(XXH_DISPATCH_AVX512) && XXH_DISPATCH_AVX512 != 0) #ifndef XXH_TARGET_AVX512 # define XXH_TARGET_AVX512 /* disable attribute target */ @@ -3034,7 +4578,7 @@ XXH3_accumulate_512_avx512(void* XXH_RESTRICT acc, const void* XXH_RESTRICT input, const void* XXH_RESTRICT secret) { - XXH_ALIGN(64) __m512i* const xacc = (__m512i *) acc; + __m512i* const xacc = (__m512i *) acc; XXH_ASSERT((((size_t)acc) & 63) == 0); XXH_STATIC_ASSERT(XXH_STRIPE_LEN == sizeof(__m512i)); @@ -3046,7 +4590,7 @@ XXH3_accumulate_512_avx512(void* XXH_RESTRICT acc, /* data_key = data_vec ^ key_vec; */ __m512i const data_key = _mm512_xor_si512 (data_vec, key_vec); /* data_key_lo = data_key >> 32; */ - __m512i const data_key_lo = _mm512_shuffle_epi32 (data_key, (_MM_PERM_ENUM)_MM_SHUFFLE(0, 3, 0, 1)); + __m512i const data_key_lo = _mm512_srli_epi64 (data_key, 32); /* product = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */ __m512i const product = _mm512_mul_epu32 (data_key, data_key_lo); /* xacc[0] += swap(data_vec); */ @@ -3056,6 +4600,7 @@ XXH3_accumulate_512_avx512(void* XXH_RESTRICT acc, *xacc = _mm512_add_epi64(product, sum); } } +XXH_FORCE_INLINE XXH_TARGET_AVX512 XXH3_ACCUMULATE_TEMPLATE(avx512) /* * XXH3_scrambleAcc: Scrambles the accumulators to improve mixing. @@ -3083,19 +4628,18 @@ XXH3_scrambleAcc_avx512(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) { XXH_ASSERT((((size_t)acc) & 63) == 0); XXH_STATIC_ASSERT(XXH_STRIPE_LEN == sizeof(__m512i)); - { XXH_ALIGN(64) __m512i* const xacc = (__m512i*) acc; + { __m512i* const xacc = (__m512i*) acc; const __m512i prime32 = _mm512_set1_epi32((int)XXH_PRIME32_1); /* xacc[0] ^= (xacc[0] >> 47) */ __m512i const acc_vec = *xacc; __m512i const shifted = _mm512_srli_epi64 (acc_vec, 47); - __m512i const data_vec = _mm512_xor_si512 (acc_vec, shifted); /* xacc[0] ^= secret; */ __m512i const key_vec = _mm512_loadu_si512 (secret); - __m512i const data_key = _mm512_xor_si512 (data_vec, key_vec); + __m512i const data_key = _mm512_ternarylogic_epi32(key_vec, acc_vec, shifted, 0x96 /* key_vec ^ acc_vec ^ shifted */); /* xacc[0] *= XXH_PRIME32_1; */ - __m512i const data_key_hi = _mm512_shuffle_epi32 (data_key, (_MM_PERM_ENUM)_MM_SHUFFLE(0, 3, 0, 1)); + __m512i const data_key_hi = _mm512_srli_epi64 (data_key, 32); __m512i const prod_lo = _mm512_mul_epu32 (data_key, prime32); __m512i const prod_hi = _mm512_mul_epu32 (data_key_hi, prime32); *xacc = _mm512_add_epi64(prod_lo, _mm512_slli_epi64(prod_hi, 32)); @@ -3110,26 +4654,23 @@ XXH3_initCustomSecret_avx512(void* XXH_RESTRICT customSecret, xxh_u64 seed64) XXH_ASSERT(((size_t)customSecret & 63) == 0); (void)(&XXH_writeLE64); { int const nbRounds = XXH_SECRET_DEFAULT_SIZE / sizeof(__m512i); - __m512i const seed = _mm512_mask_set1_epi64(_mm512_set1_epi64((xxh_i64)seed64), 0xAA, -(xxh_i64)seed64); + __m512i const seed_pos = _mm512_set1_epi64((xxh_i64)seed64); + __m512i const seed = _mm512_mask_sub_epi64(seed_pos, 0xAA, _mm512_set1_epi8(0), seed_pos); - XXH_ALIGN(64) const __m512i* const src = (const __m512i*) XXH3_kSecret; - XXH_ALIGN(64) __m512i* const dest = ( __m512i*) customSecret; + const __m512i* const src = (const __m512i*) ((const void*) XXH3_kSecret); + __m512i* const dest = ( __m512i*) customSecret; int i; + XXH_ASSERT(((size_t)src & 63) == 0); /* control alignment */ + XXH_ASSERT(((size_t)dest & 63) == 0); for (i=0; i < nbRounds; ++i) { - /* GCC has a bug, _mm512_stream_load_si512 accepts 'void*', not 'void const*', - * this will warn "discards ‘const’ qualifier". */ - union { - XXH_ALIGN(64) const __m512i* cp; - XXH_ALIGN(64) void* p; - } remote_const_void; - remote_const_void.cp = src + i; - dest[i] = _mm512_add_epi64(_mm512_stream_load_si512(remote_const_void.p), seed); + dest[i] = _mm512_add_epi64(_mm512_load_si512(src + i), seed); } } } #endif -#if (XXH_VECTOR == XXH_AVX2) || defined(XXH_X86DISPATCH) +#if (XXH_VECTOR == XXH_AVX2) \ + || (defined(XXH_DISPATCH_AVX2) && XXH_DISPATCH_AVX2 != 0) #ifndef XXH_TARGET_AVX2 # define XXH_TARGET_AVX2 /* disable attribute target */ @@ -3141,7 +4682,7 @@ XXH3_accumulate_512_avx2( void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) { XXH_ASSERT((((size_t)acc) & 31) == 0); - { XXH_ALIGN(32) __m256i* const xacc = (__m256i *) acc; + { __m256i* const xacc = (__m256i *) acc; /* Unaligned. This is mainly for pointer arithmetic, and because * _mm256_loadu_si256 requires a const __m256i * pointer for some reason. */ const __m256i* const xinput = (const __m256i *) input; @@ -3158,7 +4699,7 @@ XXH3_accumulate_512_avx2( void* XXH_RESTRICT acc, /* data_key = data_vec ^ key_vec; */ __m256i const data_key = _mm256_xor_si256 (data_vec, key_vec); /* data_key_lo = data_key >> 32; */ - __m256i const data_key_lo = _mm256_shuffle_epi32 (data_key, _MM_SHUFFLE(0, 3, 0, 1)); + __m256i const data_key_lo = _mm256_srli_epi64 (data_key, 32); /* product = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */ __m256i const product = _mm256_mul_epu32 (data_key, data_key_lo); /* xacc[i] += swap(data_vec); */ @@ -3168,12 +4709,13 @@ XXH3_accumulate_512_avx2( void* XXH_RESTRICT acc, xacc[i] = _mm256_add_epi64(product, sum); } } } +XXH_FORCE_INLINE XXH_TARGET_AVX2 XXH3_ACCUMULATE_TEMPLATE(avx2) XXH_FORCE_INLINE XXH_TARGET_AVX2 void XXH3_scrambleAcc_avx2(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) { XXH_ASSERT((((size_t)acc) & 31) == 0); - { XXH_ALIGN(32) __m256i* const xacc = (__m256i*) acc; + { __m256i* const xacc = (__m256i*) acc; /* Unaligned. This is mainly for pointer arithmetic, and because * _mm256_loadu_si256 requires a const __m256i * pointer for some reason. */ const __m256i* const xsecret = (const __m256i *) secret; @@ -3190,7 +4732,7 @@ XXH3_scrambleAcc_avx2(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) __m256i const data_key = _mm256_xor_si256 (data_vec, key_vec); /* xacc[i] *= XXH_PRIME32_1; */ - __m256i const data_key_hi = _mm256_shuffle_epi32 (data_key, _MM_SHUFFLE(0, 3, 0, 1)); + __m256i const data_key_hi = _mm256_srli_epi64 (data_key, 32); __m256i const prod_lo = _mm256_mul_epu32 (data_key, prime32); __m256i const prod_hi = _mm256_mul_epu32 (data_key_hi, prime32); xacc[i] = _mm256_add_epi64(prod_lo, _mm256_slli_epi64(prod_hi, 32)); @@ -3205,36 +4747,35 @@ XXH_FORCE_INLINE XXH_TARGET_AVX2 void XXH3_initCustomSecret_avx2(void* XXH_RESTR XXH_STATIC_ASSERT(XXH_SEC_ALIGN <= 64); (void)(&XXH_writeLE64); XXH_PREFETCH(customSecret); - { __m256i const seed = _mm256_set_epi64x(-(xxh_i64)seed64, (xxh_i64)seed64, -(xxh_i64)seed64, (xxh_i64)seed64); + { __m256i const seed = _mm256_set_epi64x((xxh_i64)(0U - seed64), (xxh_i64)seed64, (xxh_i64)(0U - seed64), (xxh_i64)seed64); - XXH_ALIGN(64) const __m256i* const src = (const __m256i*) XXH3_kSecret; - XXH_ALIGN(64) __m256i* dest = ( __m256i*) customSecret; + const __m256i* const src = (const __m256i*) ((const void*) XXH3_kSecret); + __m256i* dest = ( __m256i*) customSecret; # if defined(__GNUC__) || defined(__clang__) /* * On GCC & Clang, marking 'dest' as modified will cause the compiler: * - do not extract the secret from sse registers in the internal loop * - use less common registers, and avoid pushing these reg into stack - * The asm hack causes Clang to assume that XXH3_kSecretPtr aliases with - * customSecret, and on aarch64, this prevented LDP from merging two - * loads together for free. Putting the loads together before the stores - * properly generates LDP. */ - __asm__("" : "+r" (dest)); + XXH_COMPILER_GUARD(dest); # endif + XXH_ASSERT(((size_t)src & 31) == 0); /* control alignment */ + XXH_ASSERT(((size_t)dest & 31) == 0); /* GCC -O2 need unroll loop manually */ - dest[0] = _mm256_add_epi64(_mm256_stream_load_si256(src+0), seed); - dest[1] = _mm256_add_epi64(_mm256_stream_load_si256(src+1), seed); - dest[2] = _mm256_add_epi64(_mm256_stream_load_si256(src+2), seed); - dest[3] = _mm256_add_epi64(_mm256_stream_load_si256(src+3), seed); - dest[4] = _mm256_add_epi64(_mm256_stream_load_si256(src+4), seed); - dest[5] = _mm256_add_epi64(_mm256_stream_load_si256(src+5), seed); + dest[0] = _mm256_add_epi64(_mm256_load_si256(src+0), seed); + dest[1] = _mm256_add_epi64(_mm256_load_si256(src+1), seed); + dest[2] = _mm256_add_epi64(_mm256_load_si256(src+2), seed); + dest[3] = _mm256_add_epi64(_mm256_load_si256(src+3), seed); + dest[4] = _mm256_add_epi64(_mm256_load_si256(src+4), seed); + dest[5] = _mm256_add_epi64(_mm256_load_si256(src+5), seed); } } #endif +/* x86dispatch always generates SSE2 */ #if (XXH_VECTOR == XXH_SSE2) || defined(XXH_X86DISPATCH) #ifndef XXH_TARGET_SSE2 @@ -3248,7 +4789,7 @@ XXH3_accumulate_512_sse2( void* XXH_RESTRICT acc, { /* SSE2 is just a half-scale version of the AVX2 version. */ XXH_ASSERT((((size_t)acc) & 15) == 0); - { XXH_ALIGN(16) __m128i* const xacc = (__m128i *) acc; + { __m128i* const xacc = (__m128i *) acc; /* Unaligned. This is mainly for pointer arithmetic, and because * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */ const __m128i* const xinput = (const __m128i *) input; @@ -3275,12 +4816,13 @@ XXH3_accumulate_512_sse2( void* XXH_RESTRICT acc, xacc[i] = _mm_add_epi64(product, sum); } } } +XXH_FORCE_INLINE XXH_TARGET_SSE2 XXH3_ACCUMULATE_TEMPLATE(sse2) XXH_FORCE_INLINE XXH_TARGET_SSE2 void XXH3_scrambleAcc_sse2(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) { XXH_ASSERT((((size_t)acc) & 15) == 0); - { XXH_ALIGN(16) __m128i* const xacc = (__m128i*) acc; + { __m128i* const xacc = (__m128i*) acc; /* Unaligned. This is mainly for pointer arithmetic, and because * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */ const __m128i* const xsecret = (const __m128i *) secret; @@ -3312,27 +4854,29 @@ XXH_FORCE_INLINE XXH_TARGET_SSE2 void XXH3_initCustomSecret_sse2(void* XXH_RESTR { int const nbRounds = XXH_SECRET_DEFAULT_SIZE / sizeof(__m128i); # if defined(_MSC_VER) && defined(_M_IX86) && _MSC_VER < 1900 - // MSVC 32bit mode does not support _mm_set_epi64x before 2015 - XXH_ALIGN(16) const xxh_i64 seed64x2[2] = { (xxh_i64)seed64, -(xxh_i64)seed64 }; + /* MSVC 32bit mode does not support _mm_set_epi64x before 2015 */ + XXH_ALIGN(16) const xxh_i64 seed64x2[2] = { (xxh_i64)seed64, (xxh_i64)(0U - seed64) }; __m128i const seed = _mm_load_si128((__m128i const*)seed64x2); # else - __m128i const seed = _mm_set_epi64x(-(xxh_i64)seed64, (xxh_i64)seed64); + __m128i const seed = _mm_set_epi64x((xxh_i64)(0U - seed64), (xxh_i64)seed64); # endif int i; - XXH_ALIGN(64) const float* const src = (float const*) XXH3_kSecret; - XXH_ALIGN(XXH_SEC_ALIGN) __m128i* dest = (__m128i*) customSecret; + const void* const src16 = XXH3_kSecret; + __m128i* dst16 = (__m128i*) customSecret; # if defined(__GNUC__) || defined(__clang__) /* * On GCC & Clang, marking 'dest' as modified will cause the compiler: * - do not extract the secret from sse registers in the internal loop * - use less common registers, and avoid pushing these reg into stack */ - __asm__("" : "+r" (dest)); + XXH_COMPILER_GUARD(dst16); # endif + XXH_ASSERT(((size_t)src16 & 15) == 0); /* control alignment */ + XXH_ASSERT(((size_t)dst16 & 15) == 0); for (i=0; i < nbRounds; ++i) { - dest[i] = _mm_add_epi64(_mm_castps_si128(_mm_load_ps(src+i*4)), seed); + dst16[i] = _mm_add_epi64(_mm_load_si128((const __m128i *)src16+i), seed); } } } @@ -3340,96 +4884,222 @@ XXH_FORCE_INLINE XXH_TARGET_SSE2 void XXH3_initCustomSecret_sse2(void* XXH_RESTR #if (XXH_VECTOR == XXH_NEON) +/* forward declarations for the scalar routines */ +XXH_FORCE_INLINE void +XXH3_scalarRound(void* XXH_RESTRICT acc, void const* XXH_RESTRICT input, + void const* XXH_RESTRICT secret, size_t lane); + +XXH_FORCE_INLINE void +XXH3_scalarScrambleRound(void* XXH_RESTRICT acc, + void const* XXH_RESTRICT secret, size_t lane); + +/*! + * @internal + * @brief The bulk processing loop for NEON and WASM SIMD128. + * + * The NEON code path is actually partially scalar when running on AArch64. This + * is to optimize the pipelining and can have up to 15% speedup depending on the + * CPU, and it also mitigates some GCC codegen issues. + * + * @see XXH3_NEON_LANES for configuring this and details about this optimization. + * + * NEON's 32-bit to 64-bit long multiply takes a half vector of 32-bit + * integers instead of the other platforms which mask full 64-bit vectors, + * so the setup is more complicated than just shifting right. + * + * Additionally, there is an optimization for 4 lanes at once noted below. + * + * Since, as stated, the most optimal amount of lanes for Cortexes is 6, + * there needs to be *three* versions of the accumulate operation used + * for the remaining 2 lanes. + * + * WASM's SIMD128 uses SIMDe's arm_neon.h polyfill because the intrinsics overlap + * nearly perfectly. + */ + XXH_FORCE_INLINE void XXH3_accumulate_512_neon( void* XXH_RESTRICT acc, const void* XXH_RESTRICT input, const void* XXH_RESTRICT secret) { XXH_ASSERT((((size_t)acc) & 15) == 0); - { - XXH_ALIGN(16) uint64x2_t* const xacc = (uint64x2_t *) acc; + XXH_STATIC_ASSERT(XXH3_NEON_LANES > 0 && XXH3_NEON_LANES <= XXH_ACC_NB && XXH3_NEON_LANES % 2 == 0); + { /* GCC for darwin arm64 does not like aliasing here */ + xxh_aliasing_uint64x2_t* const xacc = (xxh_aliasing_uint64x2_t*) acc; /* We don't use a uint32x4_t pointer because it causes bus errors on ARMv7. */ - uint8_t const* const xinput = (const uint8_t *) input; - uint8_t const* const xsecret = (const uint8_t *) secret; + uint8_t const* xinput = (const uint8_t *) input; + uint8_t const* xsecret = (const uint8_t *) secret; size_t i; - for (i=0; i < XXH_STRIPE_LEN / sizeof(uint64x2_t); i++) { +#ifdef __wasm_simd128__ + /* + * On WASM SIMD128, Clang emits direct address loads when XXH3_kSecret + * is constant propagated, which results in it converting it to this + * inside the loop: + * + * a = v128.load(XXH3_kSecret + 0 + $secret_offset, offset = 0) + * b = v128.load(XXH3_kSecret + 16 + $secret_offset, offset = 0) + * ... + * + * This requires a full 32-bit address immediate (and therefore a 6 byte + * instruction) as well as an add for each offset. + * + * Putting an asm guard prevents it from folding (at the cost of losing + * the alignment hint), and uses the free offset in `v128.load` instead + * of adding secret_offset each time which overall reduces code size by + * about a kilobyte and improves performance. + */ + XXH_COMPILER_GUARD(xsecret); +#endif + /* Scalar lanes use the normal scalarRound routine */ + for (i = XXH3_NEON_LANES; i < XXH_ACC_NB; i++) { + XXH3_scalarRound(acc, input, secret, i); + } + i = 0; + /* 4 NEON lanes at a time. */ + for (; i+1 < XXH3_NEON_LANES / 2; i+=2) { /* data_vec = xinput[i]; */ - uint8x16_t data_vec = vld1q_u8(xinput + (i * 16)); + uint64x2_t data_vec_1 = XXH_vld1q_u64(xinput + (i * 16)); + uint64x2_t data_vec_2 = XXH_vld1q_u64(xinput + ((i+1) * 16)); /* key_vec = xsecret[i]; */ - uint8x16_t key_vec = vld1q_u8(xsecret + (i * 16)); - uint64x2_t data_key; - uint32x2_t data_key_lo, data_key_hi; - /* xacc[i] += swap(data_vec); */ - uint64x2_t const data64 = vreinterpretq_u64_u8(data_vec); - uint64x2_t const swapped = vextq_u64(data64, data64, 1); - xacc[i] = vaddq_u64 (xacc[i], swapped); + uint64x2_t key_vec_1 = XXH_vld1q_u64(xsecret + (i * 16)); + uint64x2_t key_vec_2 = XXH_vld1q_u64(xsecret + ((i+1) * 16)); + /* data_swap = swap(data_vec) */ + uint64x2_t data_swap_1 = vextq_u64(data_vec_1, data_vec_1, 1); + uint64x2_t data_swap_2 = vextq_u64(data_vec_2, data_vec_2, 1); /* data_key = data_vec ^ key_vec; */ - data_key = vreinterpretq_u64_u8(veorq_u8(data_vec, key_vec)); - /* data_key_lo = (uint32x2_t) (data_key & 0xFFFFFFFF); - * data_key_hi = (uint32x2_t) (data_key >> 32); - * data_key = UNDEFINED; */ - XXH_SPLIT_IN_PLACE(data_key, data_key_lo, data_key_hi); - /* xacc[i] += (uint64x2_t) data_key_lo * (uint64x2_t) data_key_hi; */ - xacc[i] = vmlal_u32 (xacc[i], data_key_lo, data_key_hi); + uint64x2_t data_key_1 = veorq_u64(data_vec_1, key_vec_1); + uint64x2_t data_key_2 = veorq_u64(data_vec_2, key_vec_2); + /* + * If we reinterpret the 64x2 vectors as 32x4 vectors, we can use a + * de-interleave operation for 4 lanes in 1 step with `vuzpq_u32` to + * get one vector with the low 32 bits of each lane, and one vector + * with the high 32 bits of each lane. + * + * The intrinsic returns a double vector because the original ARMv7-a + * instruction modified both arguments in place. AArch64 and SIMD128 emit + * two instructions from this intrinsic. + * + * [ dk11L | dk11H | dk12L | dk12H ] -> [ dk11L | dk12L | dk21L | dk22L ] + * [ dk21L | dk21H | dk22L | dk22H ] -> [ dk11H | dk12H | dk21H | dk22H ] + */ + uint32x4x2_t unzipped = vuzpq_u32( + vreinterpretq_u32_u64(data_key_1), + vreinterpretq_u32_u64(data_key_2) + ); + /* data_key_lo = data_key & 0xFFFFFFFF */ + uint32x4_t data_key_lo = unzipped.val[0]; + /* data_key_hi = data_key >> 32 */ + uint32x4_t data_key_hi = unzipped.val[1]; + /* + * Then, we can split the vectors horizontally and multiply which, as for most + * widening intrinsics, have a variant that works on both high half vectors + * for free on AArch64. A similar instruction is available on SIMD128. + * + * sum = data_swap + (u64x2) data_key_lo * (u64x2) data_key_hi + */ + uint64x2_t sum_1 = XXH_vmlal_low_u32(data_swap_1, data_key_lo, data_key_hi); + uint64x2_t sum_2 = XXH_vmlal_high_u32(data_swap_2, data_key_lo, data_key_hi); + /* + * Clang reorders + * a += b * c; // umlal swap.2d, dkl.2s, dkh.2s + * c += a; // add acc.2d, acc.2d, swap.2d + * to + * c += a; // add acc.2d, acc.2d, swap.2d + * c += b * c; // umlal acc.2d, dkl.2s, dkh.2s + * + * While it would make sense in theory since the addition is faster, + * for reasons likely related to umlal being limited to certain NEON + * pipelines, this is worse. A compiler guard fixes this. + */ + XXH_COMPILER_GUARD_CLANG_NEON(sum_1); + XXH_COMPILER_GUARD_CLANG_NEON(sum_2); + /* xacc[i] = acc_vec + sum; */ + xacc[i] = vaddq_u64(xacc[i], sum_1); + xacc[i+1] = vaddq_u64(xacc[i+1], sum_2); + } + /* Operate on the remaining NEON lanes 2 at a time. */ + for (; i < XXH3_NEON_LANES / 2; i++) { + /* data_vec = xinput[i]; */ + uint64x2_t data_vec = XXH_vld1q_u64(xinput + (i * 16)); + /* key_vec = xsecret[i]; */ + uint64x2_t key_vec = XXH_vld1q_u64(xsecret + (i * 16)); + /* acc_vec_2 = swap(data_vec) */ + uint64x2_t data_swap = vextq_u64(data_vec, data_vec, 1); + /* data_key = data_vec ^ key_vec; */ + uint64x2_t data_key = veorq_u64(data_vec, key_vec); + /* For two lanes, just use VMOVN and VSHRN. */ + /* data_key_lo = data_key & 0xFFFFFFFF; */ + uint32x2_t data_key_lo = vmovn_u64(data_key); + /* data_key_hi = data_key >> 32; */ + uint32x2_t data_key_hi = vshrn_n_u64(data_key, 32); + /* sum = data_swap + (u64x2) data_key_lo * (u64x2) data_key_hi; */ + uint64x2_t sum = vmlal_u32(data_swap, data_key_lo, data_key_hi); + /* Same Clang workaround as before */ + XXH_COMPILER_GUARD_CLANG_NEON(sum); + /* xacc[i] = acc_vec + sum; */ + xacc[i] = vaddq_u64 (xacc[i], sum); } } } +XXH_FORCE_INLINE XXH3_ACCUMULATE_TEMPLATE(neon) XXH_FORCE_INLINE void XXH3_scrambleAcc_neon(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) { XXH_ASSERT((((size_t)acc) & 15) == 0); - { uint64x2_t* xacc = (uint64x2_t*) acc; + { xxh_aliasing_uint64x2_t* xacc = (xxh_aliasing_uint64x2_t*) acc; uint8_t const* xsecret = (uint8_t const*) secret; - uint32x2_t prime = vdup_n_u32 (XXH_PRIME32_1); size_t i; - for (i=0; i < XXH_STRIPE_LEN/sizeof(uint64x2_t); i++) { + /* WASM uses operator overloads and doesn't need these. */ +#ifndef __wasm_simd128__ + /* { prime32_1, prime32_1 } */ + uint32x2_t const kPrimeLo = vdup_n_u32(XXH_PRIME32_1); + /* { 0, prime32_1, 0, prime32_1 } */ + uint32x4_t const kPrimeHi = vreinterpretq_u32_u64(vdupq_n_u64((xxh_u64)XXH_PRIME32_1 << 32)); +#endif + + /* AArch64 uses both scalar and neon at the same time */ + for (i = XXH3_NEON_LANES; i < XXH_ACC_NB; i++) { + XXH3_scalarScrambleRound(acc, secret, i); + } + for (i=0; i < XXH3_NEON_LANES / 2; i++) { /* xacc[i] ^= (xacc[i] >> 47); */ uint64x2_t acc_vec = xacc[i]; - uint64x2_t shifted = vshrq_n_u64 (acc_vec, 47); - uint64x2_t data_vec = veorq_u64 (acc_vec, shifted); + uint64x2_t shifted = vshrq_n_u64(acc_vec, 47); + uint64x2_t data_vec = veorq_u64(acc_vec, shifted); /* xacc[i] ^= xsecret[i]; */ - uint8x16_t key_vec = vld1q_u8(xsecret + (i * 16)); - uint64x2_t data_key = veorq_u64(data_vec, vreinterpretq_u64_u8(key_vec)); - + uint64x2_t key_vec = XXH_vld1q_u64(xsecret + (i * 16)); + uint64x2_t data_key = veorq_u64(data_vec, key_vec); /* xacc[i] *= XXH_PRIME32_1 */ - uint32x2_t data_key_lo, data_key_hi; - /* data_key_lo = (uint32x2_t) (xacc[i] & 0xFFFFFFFF); - * data_key_hi = (uint32x2_t) (xacc[i] >> 32); - * xacc[i] = UNDEFINED; */ - XXH_SPLIT_IN_PLACE(data_key, data_key_lo, data_key_hi); - { /* - * prod_hi = (data_key >> 32) * XXH_PRIME32_1; - * - * Avoid vmul_u32 + vshll_n_u32 since Clang 6 and 7 will - * incorrectly "optimize" this: - * tmp = vmul_u32(vmovn_u64(a), vmovn_u64(b)); - * shifted = vshll_n_u32(tmp, 32); - * to this: - * tmp = "vmulq_u64"(a, b); // no such thing! - * shifted = vshlq_n_u64(tmp, 32); - * - * However, unlike SSE, Clang lacks a 64-bit multiply routine - * for NEON, and it scalarizes two 64-bit multiplies instead. - * - * vmull_u32 has the same timing as vmul_u32, and it avoids - * this bug completely. - * See https://bugs.llvm.org/show_bug.cgi?id=39967 - */ - uint64x2_t prod_hi = vmull_u32 (data_key_hi, prime); - /* xacc[i] = prod_hi << 32; */ - xacc[i] = vshlq_n_u64(prod_hi, 32); - /* xacc[i] += (prod_hi & 0xFFFFFFFF) * XXH_PRIME32_1; */ - xacc[i] = vmlal_u32(xacc[i], data_key_lo, prime); - } - } } +#ifdef __wasm_simd128__ + /* SIMD128 has multiply by u64x2, use it instead of expanding and scalarizing */ + xacc[i] = data_key * XXH_PRIME32_1; +#else + /* + * Expanded version with portable NEON intrinsics + * + * lo(x) * lo(y) + (hi(x) * lo(y) << 32) + * + * prod_hi = hi(data_key) * lo(prime) << 32 + * + * Since we only need 32 bits of this multiply a trick can be used, reinterpreting the vector + * as a uint32x4_t and multiplying by { 0, prime, 0, prime } to cancel out the unwanted bits + * and avoid the shift. + */ + uint32x4_t prod_hi = vmulq_u32 (vreinterpretq_u32_u64(data_key), kPrimeHi); + /* Extract low bits for vmlal_u32 */ + uint32x2_t data_key_lo = vmovn_u64(data_key); + /* xacc[i] = prod_hi + lo(data_key) * XXH_PRIME32_1; */ + xacc[i] = vmlal_u32(vreinterpretq_u64_u32(prod_hi), data_key_lo, kPrimeLo); +#endif + } + } } - #endif #if (XXH_VECTOR == XXH_VSX) @@ -3439,39 +5109,44 @@ XXH3_accumulate_512_vsx( void* XXH_RESTRICT acc, const void* XXH_RESTRICT input, const void* XXH_RESTRICT secret) { - xxh_u64x2* const xacc = (xxh_u64x2*) acc; /* presumed aligned */ - xxh_u64x2 const* const xinput = (xxh_u64x2 const*) input; /* no alignment restriction */ - xxh_u64x2 const* const xsecret = (xxh_u64x2 const*) secret; /* no alignment restriction */ + /* presumed aligned */ + xxh_aliasing_u64x2* const xacc = (xxh_aliasing_u64x2*) acc; + xxh_u8 const* const xinput = (xxh_u8 const*) input; /* no alignment restriction */ + xxh_u8 const* const xsecret = (xxh_u8 const*) secret; /* no alignment restriction */ xxh_u64x2 const v32 = { 32, 32 }; size_t i; for (i = 0; i < XXH_STRIPE_LEN / sizeof(xxh_u64x2); i++) { /* data_vec = xinput[i]; */ - xxh_u64x2 const data_vec = XXH_vec_loadu(xinput + i); + xxh_u64x2 const data_vec = XXH_vec_loadu(xinput + 16*i); /* key_vec = xsecret[i]; */ - xxh_u64x2 const key_vec = XXH_vec_loadu(xsecret + i); + xxh_u64x2 const key_vec = XXH_vec_loadu(xsecret + 16*i); xxh_u64x2 const data_key = data_vec ^ key_vec; /* shuffled = (data_key << 32) | (data_key >> 32); */ xxh_u32x4 const shuffled = (xxh_u32x4)vec_rl(data_key, v32); /* product = ((xxh_u64x2)data_key & 0xFFFFFFFF) * ((xxh_u64x2)shuffled & 0xFFFFFFFF); */ xxh_u64x2 const product = XXH_vec_mulo((xxh_u32x4)data_key, shuffled); - xacc[i] += product; + /* acc_vec = xacc[i]; */ + xxh_u64x2 acc_vec = xacc[i]; + acc_vec += product; /* swap high and low halves */ #ifdef __s390x__ - xacc[i] += vec_permi(data_vec, data_vec, 2); + acc_vec += vec_permi(data_vec, data_vec, 2); #else - xacc[i] += vec_xxpermdi(data_vec, data_vec, 2); + acc_vec += vec_xxpermdi(data_vec, data_vec, 2); #endif + xacc[i] = acc_vec; } } +XXH_FORCE_INLINE XXH3_ACCUMULATE_TEMPLATE(vsx) XXH_FORCE_INLINE void XXH3_scrambleAcc_vsx(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) { XXH_ASSERT((((size_t)acc) & 15) == 0); - { xxh_u64x2* const xacc = (xxh_u64x2*) acc; - const xxh_u64x2* const xsecret = (const xxh_u64x2*) secret; + { xxh_aliasing_u64x2* const xacc = (xxh_aliasing_u64x2*) acc; + const xxh_u8* const xsecret = (const xxh_u8*) secret; /* constants */ xxh_u64x2 const v32 = { 32, 32 }; xxh_u64x2 const v47 = { 47, 47 }; @@ -3483,7 +5158,7 @@ XXH3_scrambleAcc_vsx(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) xxh_u64x2 const data_vec = acc_vec ^ (acc_vec >> v47); /* xacc[i] ^= xsecret[i]; */ - xxh_u64x2 const key_vec = XXH_vec_loadu(xsecret + i); + xxh_u64x2 const key_vec = XXH_vec_loadu(xsecret + 16*i); xxh_u64x2 const data_key = data_vec ^ key_vec; /* xacc[i] *= XXH_PRIME32_1 */ @@ -3497,40 +5172,233 @@ XXH3_scrambleAcc_vsx(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) #endif +#if (XXH_VECTOR == XXH_SVE) + +XXH_FORCE_INLINE void +XXH3_accumulate_512_sve( void* XXH_RESTRICT acc, + const void* XXH_RESTRICT input, + const void* XXH_RESTRICT secret) +{ + uint64_t *xacc = (uint64_t *)acc; + const uint64_t *xinput = (const uint64_t *)(const void *)input; + const uint64_t *xsecret = (const uint64_t *)(const void *)secret; + svuint64_t kSwap = sveor_n_u64_z(svptrue_b64(), svindex_u64(0, 1), 1); + uint64_t element_count = svcntd(); + if (element_count >= 8) { + svbool_t mask = svptrue_pat_b64(SV_VL8); + svuint64_t vacc = svld1_u64(mask, xacc); + ACCRND(vacc, 0); + svst1_u64(mask, xacc, vacc); + } else if (element_count == 2) { /* sve128 */ + svbool_t mask = svptrue_pat_b64(SV_VL2); + svuint64_t acc0 = svld1_u64(mask, xacc + 0); + svuint64_t acc1 = svld1_u64(mask, xacc + 2); + svuint64_t acc2 = svld1_u64(mask, xacc + 4); + svuint64_t acc3 = svld1_u64(mask, xacc + 6); + ACCRND(acc0, 0); + ACCRND(acc1, 2); + ACCRND(acc2, 4); + ACCRND(acc3, 6); + svst1_u64(mask, xacc + 0, acc0); + svst1_u64(mask, xacc + 2, acc1); + svst1_u64(mask, xacc + 4, acc2); + svst1_u64(mask, xacc + 6, acc3); + } else { + svbool_t mask = svptrue_pat_b64(SV_VL4); + svuint64_t acc0 = svld1_u64(mask, xacc + 0); + svuint64_t acc1 = svld1_u64(mask, xacc + 4); + ACCRND(acc0, 0); + ACCRND(acc1, 4); + svst1_u64(mask, xacc + 0, acc0); + svst1_u64(mask, xacc + 4, acc1); + } +} + +XXH_FORCE_INLINE void +XXH3_accumulate_sve(xxh_u64* XXH_RESTRICT acc, + const xxh_u8* XXH_RESTRICT input, + const xxh_u8* XXH_RESTRICT secret, + size_t nbStripes) +{ + if (nbStripes != 0) { + uint64_t *xacc = (uint64_t *)acc; + const uint64_t *xinput = (const uint64_t *)(const void *)input; + const uint64_t *xsecret = (const uint64_t *)(const void *)secret; + svuint64_t kSwap = sveor_n_u64_z(svptrue_b64(), svindex_u64(0, 1), 1); + uint64_t element_count = svcntd(); + if (element_count >= 8) { + svbool_t mask = svptrue_pat_b64(SV_VL8); + svuint64_t vacc = svld1_u64(mask, xacc + 0); + do { + /* svprfd(svbool_t, void *, enum svfprop); */ + svprfd(mask, xinput + 128, SV_PLDL1STRM); + ACCRND(vacc, 0); + xinput += 8; + xsecret += 1; + nbStripes--; + } while (nbStripes != 0); + + svst1_u64(mask, xacc + 0, vacc); + } else if (element_count == 2) { /* sve128 */ + svbool_t mask = svptrue_pat_b64(SV_VL2); + svuint64_t acc0 = svld1_u64(mask, xacc + 0); + svuint64_t acc1 = svld1_u64(mask, xacc + 2); + svuint64_t acc2 = svld1_u64(mask, xacc + 4); + svuint64_t acc3 = svld1_u64(mask, xacc + 6); + do { + svprfd(mask, xinput + 128, SV_PLDL1STRM); + ACCRND(acc0, 0); + ACCRND(acc1, 2); + ACCRND(acc2, 4); + ACCRND(acc3, 6); + xinput += 8; + xsecret += 1; + nbStripes--; + } while (nbStripes != 0); + + svst1_u64(mask, xacc + 0, acc0); + svst1_u64(mask, xacc + 2, acc1); + svst1_u64(mask, xacc + 4, acc2); + svst1_u64(mask, xacc + 6, acc3); + } else { + svbool_t mask = svptrue_pat_b64(SV_VL4); + svuint64_t acc0 = svld1_u64(mask, xacc + 0); + svuint64_t acc1 = svld1_u64(mask, xacc + 4); + do { + svprfd(mask, xinput + 128, SV_PLDL1STRM); + ACCRND(acc0, 0); + ACCRND(acc1, 4); + xinput += 8; + xsecret += 1; + nbStripes--; + } while (nbStripes != 0); + + svst1_u64(mask, xacc + 0, acc0); + svst1_u64(mask, xacc + 4, acc1); + } + } +} + +#endif + /* scalar variants - universal */ +#if defined(__aarch64__) && (defined(__GNUC__) || defined(__clang__)) +/* + * In XXH3_scalarRound(), GCC and Clang have a similar codegen issue, where they + * emit an excess mask and a full 64-bit multiply-add (MADD X-form). + * + * While this might not seem like much, as AArch64 is a 64-bit architecture, only + * big Cortex designs have a full 64-bit multiplier. + * + * On the little cores, the smaller 32-bit multiplier is used, and full 64-bit + * multiplies expand to 2-3 multiplies in microcode. This has a major penalty + * of up to 4 latency cycles and 2 stall cycles in the multiply pipeline. + * + * Thankfully, AArch64 still provides the 32-bit long multiply-add (UMADDL) which does + * not have this penalty and does the mask automatically. + */ +XXH_FORCE_INLINE xxh_u64 +XXH_mult32to64_add64(xxh_u64 lhs, xxh_u64 rhs, xxh_u64 acc) +{ + xxh_u64 ret; + /* note: %x = 64-bit register, %w = 32-bit register */ + __asm__("umaddl %x0, %w1, %w2, %x3" : "=r" (ret) : "r" (lhs), "r" (rhs), "r" (acc)); + return ret; +} +#else +XXH_FORCE_INLINE xxh_u64 +XXH_mult32to64_add64(xxh_u64 lhs, xxh_u64 rhs, xxh_u64 acc) +{ + return XXH_mult32to64((xxh_u32)lhs, (xxh_u32)rhs) + acc; +} +#endif + +/*! + * @internal + * @brief Scalar round for @ref XXH3_accumulate_512_scalar(). + * + * This is extracted to its own function because the NEON path uses a combination + * of NEON and scalar. + */ +XXH_FORCE_INLINE void +XXH3_scalarRound(void* XXH_RESTRICT acc, + void const* XXH_RESTRICT input, + void const* XXH_RESTRICT secret, + size_t lane) +{ + xxh_u64* xacc = (xxh_u64*) acc; + xxh_u8 const* xinput = (xxh_u8 const*) input; + xxh_u8 const* xsecret = (xxh_u8 const*) secret; + XXH_ASSERT(lane < XXH_ACC_NB); + XXH_ASSERT(((size_t)acc & (XXH_ACC_ALIGN-1)) == 0); + { + xxh_u64 const data_val = XXH_readLE64(xinput + lane * 8); + xxh_u64 const data_key = data_val ^ XXH_readLE64(xsecret + lane * 8); + xacc[lane ^ 1] += data_val; /* swap adjacent lanes */ + xacc[lane] = XXH_mult32to64_add64(data_key /* & 0xFFFFFFFF */, data_key >> 32, xacc[lane]); + } +} + +/*! + * @internal + * @brief Processes a 64 byte block of data using the scalar path. + */ XXH_FORCE_INLINE void XXH3_accumulate_512_scalar(void* XXH_RESTRICT acc, const void* XXH_RESTRICT input, const void* XXH_RESTRICT secret) { - XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64* const xacc = (xxh_u64*) acc; /* presumed aligned */ - const xxh_u8* const xinput = (const xxh_u8*) input; /* no alignment restriction */ - const xxh_u8* const xsecret = (const xxh_u8*) secret; /* no alignment restriction */ size_t i; - XXH_ASSERT(((size_t)acc & (XXH_ACC_ALIGN-1)) == 0); + /* ARM GCC refuses to unroll this loop, resulting in a 24% slowdown on ARMv6. */ +#if defined(__GNUC__) && !defined(__clang__) \ + && (defined(__arm__) || defined(__thumb2__)) \ + && defined(__ARM_FEATURE_UNALIGNED) /* no unaligned access just wastes bytes */ \ + && XXH_SIZE_OPT <= 0 +# pragma GCC unroll 8 +#endif for (i=0; i < XXH_ACC_NB; i++) { - xxh_u64 const data_val = XXH_readLE64(xinput + 8*i); - xxh_u64 const data_key = data_val ^ XXH_readLE64(xsecret + i*8); - xacc[i ^ 1] += data_val; /* swap adjacent lanes */ - xacc[i] += XXH_mult32to64(data_key & 0xFFFFFFFF, data_key >> 32); + XXH3_scalarRound(acc, input, secret, i); } } +XXH_FORCE_INLINE XXH3_ACCUMULATE_TEMPLATE(scalar) +/*! + * @internal + * @brief Scalar scramble step for @ref XXH3_scrambleAcc_scalar(). + * + * This is extracted to its own function because the NEON path uses a combination + * of NEON and scalar. + */ XXH_FORCE_INLINE void -XXH3_scrambleAcc_scalar(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) +XXH3_scalarScrambleRound(void* XXH_RESTRICT acc, + void const* XXH_RESTRICT secret, + size_t lane) { - XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64* const xacc = (xxh_u64*) acc; /* presumed aligned */ + xxh_u64* const xacc = (xxh_u64*) acc; /* presumed aligned */ const xxh_u8* const xsecret = (const xxh_u8*) secret; /* no alignment restriction */ - size_t i; XXH_ASSERT((((size_t)acc) & (XXH_ACC_ALIGN-1)) == 0); - for (i=0; i < XXH_ACC_NB; i++) { - xxh_u64 const key64 = XXH_readLE64(xsecret + 8*i); - xxh_u64 acc64 = xacc[i]; + XXH_ASSERT(lane < XXH_ACC_NB); + { + xxh_u64 const key64 = XXH_readLE64(xsecret + lane * 8); + xxh_u64 acc64 = xacc[lane]; acc64 = XXH_xorshift64(acc64, 47); acc64 ^= key64; acc64 *= XXH_PRIME32_1; - xacc[i] = acc64; + xacc[lane] = acc64; + } +} + +/*! + * @internal + * @brief Scrambles the accumulators after a large chunk has been read + */ +XXH_FORCE_INLINE void +XXH3_scrambleAcc_scalar(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) +{ + size_t i; + for (i=0; i < XXH_ACC_NB; i++) { + XXH3_scalarScrambleRound(acc, secret, i); } } @@ -3545,15 +5413,16 @@ XXH3_initCustomSecret_scalar(void* XXH_RESTRICT customSecret, xxh_u64 seed64) const xxh_u8* kSecretPtr = XXH3_kSecret; XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 15) == 0); -#if defined(__clang__) && defined(__aarch64__) +#if defined(__GNUC__) && defined(__aarch64__) /* * UGLY HACK: - * Clang generates a bunch of MOV/MOVK pairs for aarch64, and they are + * GCC and Clang generate a bunch of MOV/MOVK pairs for aarch64, and they are * placed sequentially, in order, at the top of the unrolled loop. * * While MOVK is great for generating constants (2 cycles for a 64-bit - * constant compared to 4 cycles for LDR), long MOVK chains stall the - * integer pipelines: + * constant compared to 4 cycles for LDR), it fights for bandwidth with + * the arithmetic instructions. + * * I L S * MOVK * MOVK @@ -3562,7 +5431,7 @@ XXH3_initCustomSecret_scalar(void* XXH_RESTRICT customSecret, xxh_u64 seed64) * ADD * SUB STR * STR - * By forcing loads from memory (as the asm line causes Clang to assume + * By forcing loads from memory (as the asm line causes the compiler to assume * that XXH3_kSecretPtr has been changed), the pipelines are used more * efficiently: * I L S @@ -3570,23 +5439,20 @@ XXH3_initCustomSecret_scalar(void* XXH_RESTRICT customSecret, xxh_u64 seed64) * ADD LDR * SUB STR * STR + * + * See XXH3_NEON_LANES for details on the pipsline. + * * XXH3_64bits_withSeed, len == 256, Snapdragon 835 * without hack: 2654.4 MB/s * with hack: 3202.9 MB/s */ - __asm__("" : "+r" (kSecretPtr)); + XXH_COMPILER_GUARD(kSecretPtr); #endif - /* - * Note: in debug mode, this overrides the asm optimization - * and Clang will emit MOVK chains again. - */ - XXH_ASSERT(kSecretPtr == XXH3_kSecret); - { int const nbRounds = XXH_SECRET_DEFAULT_SIZE / 16; int i; for (i=0; i < nbRounds; i++) { /* - * The asm hack causes Clang to assume that kSecretPtr aliases with + * The asm hack causes the compiler to assume that kSecretPtr aliases with * customSecret, and on aarch64, this prevented LDP from merging two * loads together for free. Putting the loads together before the stores * properly generates LDP. @@ -3599,7 +5465,7 @@ XXH3_initCustomSecret_scalar(void* XXH_RESTRICT customSecret, xxh_u64 seed64) } -typedef void (*XXH3_f_accumulate_512)(void* XXH_RESTRICT, const void*, const void*); +typedef void (*XXH3_f_accumulate)(xxh_u64* XXH_RESTRICT, const xxh_u8* XXH_RESTRICT, const xxh_u8* XXH_RESTRICT, size_t); typedef void (*XXH3_f_scrambleAcc)(void* XXH_RESTRICT, const void*); typedef void (*XXH3_f_initCustomSecret)(void* XXH_RESTRICT, xxh_u64); @@ -3607,82 +5473,63 @@ typedef void (*XXH3_f_initCustomSecret)(void* XXH_RESTRICT, xxh_u64); #if (XXH_VECTOR == XXH_AVX512) #define XXH3_accumulate_512 XXH3_accumulate_512_avx512 +#define XXH3_accumulate XXH3_accumulate_avx512 #define XXH3_scrambleAcc XXH3_scrambleAcc_avx512 #define XXH3_initCustomSecret XXH3_initCustomSecret_avx512 #elif (XXH_VECTOR == XXH_AVX2) #define XXH3_accumulate_512 XXH3_accumulate_512_avx2 +#define XXH3_accumulate XXH3_accumulate_avx2 #define XXH3_scrambleAcc XXH3_scrambleAcc_avx2 #define XXH3_initCustomSecret XXH3_initCustomSecret_avx2 #elif (XXH_VECTOR == XXH_SSE2) #define XXH3_accumulate_512 XXH3_accumulate_512_sse2 +#define XXH3_accumulate XXH3_accumulate_sse2 #define XXH3_scrambleAcc XXH3_scrambleAcc_sse2 #define XXH3_initCustomSecret XXH3_initCustomSecret_sse2 #elif (XXH_VECTOR == XXH_NEON) #define XXH3_accumulate_512 XXH3_accumulate_512_neon +#define XXH3_accumulate XXH3_accumulate_neon #define XXH3_scrambleAcc XXH3_scrambleAcc_neon #define XXH3_initCustomSecret XXH3_initCustomSecret_scalar #elif (XXH_VECTOR == XXH_VSX) #define XXH3_accumulate_512 XXH3_accumulate_512_vsx +#define XXH3_accumulate XXH3_accumulate_vsx #define XXH3_scrambleAcc XXH3_scrambleAcc_vsx #define XXH3_initCustomSecret XXH3_initCustomSecret_scalar +#elif (XXH_VECTOR == XXH_SVE) +#define XXH3_accumulate_512 XXH3_accumulate_512_sve +#define XXH3_accumulate XXH3_accumulate_sve +#define XXH3_scrambleAcc XXH3_scrambleAcc_scalar +#define XXH3_initCustomSecret XXH3_initCustomSecret_scalar + #else /* scalar */ #define XXH3_accumulate_512 XXH3_accumulate_512_scalar +#define XXH3_accumulate XXH3_accumulate_scalar #define XXH3_scrambleAcc XXH3_scrambleAcc_scalar #define XXH3_initCustomSecret XXH3_initCustomSecret_scalar #endif - - -#ifndef XXH_PREFETCH_DIST -# ifdef __clang__ -# define XXH_PREFETCH_DIST 320 -# else -# if (XXH_VECTOR == XXH_AVX512) -# define XXH_PREFETCH_DIST 512 -# else -# define XXH_PREFETCH_DIST 384 -# endif -# endif /* __clang__ */ -#endif /* XXH_PREFETCH_DIST */ - -/* - * XXH3_accumulate() - * Loops over XXH3_accumulate_512(). - * Assumption: nbStripes will not overflow the secret size - */ -XXH_FORCE_INLINE void -XXH3_accumulate( xxh_u64* XXH_RESTRICT acc, - const xxh_u8* XXH_RESTRICT input, - const xxh_u8* XXH_RESTRICT secret, - size_t nbStripes, - XXH3_f_accumulate_512 f_acc512) -{ - size_t n; - for (n = 0; n < nbStripes; n++ ) { - const xxh_u8* const in = input + n*XXH_STRIPE_LEN; - XXH_PREFETCH(in + XXH_PREFETCH_DIST); - f_acc512(acc, - in, - secret + n*XXH_SECRET_CONSUME_RATE); - } -} +#if XXH_SIZE_OPT >= 1 /* don't do SIMD for initialization */ +# undef XXH3_initCustomSecret +# define XXH3_initCustomSecret XXH3_initCustomSecret_scalar +#endif XXH_FORCE_INLINE void XXH3_hashLong_internal_loop(xxh_u64* XXH_RESTRICT acc, const xxh_u8* XXH_RESTRICT input, size_t len, const xxh_u8* XXH_RESTRICT secret, size_t secretSize, - XXH3_f_accumulate_512 f_acc512, + XXH3_f_accumulate f_acc, XXH3_f_scrambleAcc f_scramble) { size_t const nbStripesPerBlock = (secretSize - XXH_STRIPE_LEN) / XXH_SECRET_CONSUME_RATE; @@ -3694,7 +5541,7 @@ XXH3_hashLong_internal_loop(xxh_u64* XXH_RESTRICT acc, XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); for (n = 0; n < nb_blocks; n++) { - XXH3_accumulate(acc, input + n*block_len, secret, nbStripesPerBlock, f_acc512); + f_acc(acc, input + n*block_len, secret, nbStripesPerBlock); f_scramble(acc, secret + secretSize - XXH_STRIPE_LEN); } @@ -3702,12 +5549,12 @@ XXH3_hashLong_internal_loop(xxh_u64* XXH_RESTRICT acc, XXH_ASSERT(len > XXH_STRIPE_LEN); { size_t const nbStripes = ((len - 1) - (block_len * nb_blocks)) / XXH_STRIPE_LEN; XXH_ASSERT(nbStripes <= (secretSize / XXH_SECRET_CONSUME_RATE)); - XXH3_accumulate(acc, input + nb_blocks*block_len, secret, nbStripes, f_acc512); + f_acc(acc, input + nb_blocks*block_len, secret, nbStripes); /* last stripe */ { const xxh_u8* const p = input + len - XXH_STRIPE_LEN; #define XXH_SECRET_LASTACC_START 7 /* not aligned on 8, last secret is different from acc & scrambler */ - f_acc512(acc, p, secret + secretSize - XXH_STRIPE_LEN - XXH_SECRET_LASTACC_START); + XXH3_accumulate_512(acc, p, secret + secretSize - XXH_STRIPE_LEN - XXH_SECRET_LASTACC_START); } } } @@ -3739,7 +5586,7 @@ XXH3_mergeAccs(const xxh_u64* XXH_RESTRICT acc, const xxh_u8* XXH_RESTRICT secre * without hack: 2063.7 MB/s * with hack: 2560.7 MB/s */ - __asm__("" : "+r" (result64)); + XXH_COMPILER_GUARD(result64); #endif } @@ -3752,12 +5599,12 @@ XXH3_mergeAccs(const xxh_u64* XXH_RESTRICT acc, const xxh_u8* XXH_RESTRICT secre XXH_FORCE_INLINE XXH64_hash_t XXH3_hashLong_64b_internal(const void* XXH_RESTRICT input, size_t len, const void* XXH_RESTRICT secret, size_t secretSize, - XXH3_f_accumulate_512 f_acc512, + XXH3_f_accumulate f_acc, XXH3_f_scrambleAcc f_scramble) { XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[XXH_ACC_NB] = XXH3_INIT_ACC; - XXH3_hashLong_internal_loop(acc, (const xxh_u8*)input, len, (const xxh_u8*)secret, secretSize, f_acc512, f_scramble); + XXH3_hashLong_internal_loop(acc, (const xxh_u8*)input, len, (const xxh_u8*)secret, secretSize, f_acc, f_scramble); /* converge into final hash */ XXH_STATIC_ASSERT(sizeof(acc) == 64); @@ -3768,29 +5615,32 @@ XXH3_hashLong_64b_internal(const void* XXH_RESTRICT input, size_t len, } /* - * It's important for performance that XXH3_hashLong is not inlined. + * It's important for performance to transmit secret's size (when it's static) + * so that the compiler can properly optimize the vectorized loop. + * This makes a big performance difference for "medium" keys (<1 KB) when using AVX instruction set. + * When the secret size is unknown, or on GCC 12 where the mix of NO_INLINE and FORCE_INLINE + * breaks -Og, this is XXH_NO_INLINE. */ -XXH_NO_INLINE XXH64_hash_t +XXH3_WITH_SECRET_INLINE XXH64_hash_t XXH3_hashLong_64b_withSecret(const void* XXH_RESTRICT input, size_t len, XXH64_hash_t seed64, const xxh_u8* XXH_RESTRICT secret, size_t secretLen) { (void)seed64; - return XXH3_hashLong_64b_internal(input, len, secret, secretLen, XXH3_accumulate_512, XXH3_scrambleAcc); + return XXH3_hashLong_64b_internal(input, len, secret, secretLen, XXH3_accumulate, XXH3_scrambleAcc); } /* - * It's important for performance that XXH3_hashLong is not inlined. - * Since the function is not inlined, the compiler may not be able to understand that, - * in some scenarios, its `secret` argument is actually a compile time constant. - * This variant enforces that the compiler can detect that, - * and uses this opportunity to streamline the generated code for better performance. + * It's preferable for performance that XXH3_hashLong is not inlined, + * as it results in a smaller function for small data, easier to the instruction cache. + * Note that inside this no_inline function, we do inline the internal loop, + * and provide a statically defined secret size to allow optimization of vector loop. */ -XXH_NO_INLINE XXH64_hash_t +XXH_NO_INLINE XXH_PUREF XXH64_hash_t XXH3_hashLong_64b_default(const void* XXH_RESTRICT input, size_t len, XXH64_hash_t seed64, const xxh_u8* XXH_RESTRICT secret, size_t secretLen) { (void)seed64; (void)secret; (void)secretLen; - return XXH3_hashLong_64b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_accumulate_512, XXH3_scrambleAcc); + return XXH3_hashLong_64b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_accumulate, XXH3_scrambleAcc); } /* @@ -3807,18 +5657,20 @@ XXH3_hashLong_64b_default(const void* XXH_RESTRICT input, size_t len, XXH_FORCE_INLINE XXH64_hash_t XXH3_hashLong_64b_withSeed_internal(const void* input, size_t len, XXH64_hash_t seed, - XXH3_f_accumulate_512 f_acc512, + XXH3_f_accumulate f_acc, XXH3_f_scrambleAcc f_scramble, XXH3_f_initCustomSecret f_initSec) { +#if XXH_SIZE_OPT <= 0 if (seed == 0) return XXH3_hashLong_64b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret), - f_acc512, f_scramble); + f_acc, f_scramble); +#endif { XXH_ALIGN(XXH_SEC_ALIGN) xxh_u8 secret[XXH_SECRET_DEFAULT_SIZE]; f_initSec(secret, seed); return XXH3_hashLong_64b_internal(input, len, secret, sizeof(secret), - f_acc512, f_scramble); + f_acc, f_scramble); } } @@ -3826,12 +5678,12 @@ XXH3_hashLong_64b_withSeed_internal(const void* input, size_t len, * It's important for performance that XXH3_hashLong is not inlined. */ XXH_NO_INLINE XXH64_hash_t -XXH3_hashLong_64b_withSeed(const void* input, size_t len, - XXH64_hash_t seed, const xxh_u8* secret, size_t secretLen) +XXH3_hashLong_64b_withSeed(const void* XXH_RESTRICT input, size_t len, + XXH64_hash_t seed, const xxh_u8* XXH_RESTRICT secret, size_t secretLen) { (void)secret; (void)secretLen; return XXH3_hashLong_64b_withSeed_internal(input, len, seed, - XXH3_accumulate_512, XXH3_scrambleAcc, XXH3_initCustomSecret); + XXH3_accumulate, XXH3_scrambleAcc, XXH3_initCustomSecret); } @@ -3863,26 +5715,37 @@ XXH3_64bits_internal(const void* XXH_RESTRICT input, size_t len, /* === Public entry point === */ -XXH_PUBLIC_API XXH64_hash_t XXH3_64bits(const void* input, size_t len) +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH64_hash_t XXH3_64bits(XXH_NOESCAPE const void* input, size_t length) +{ + return XXH3_64bits_internal(input, length, 0, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_64b_default); +} + +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH64_hash_t +XXH3_64bits_withSecret(XXH_NOESCAPE const void* input, size_t length, XXH_NOESCAPE const void* secret, size_t secretSize) { - return XXH3_64bits_internal(input, len, 0, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_64b_default); + return XXH3_64bits_internal(input, length, 0, secret, secretSize, XXH3_hashLong_64b_withSecret); } +/*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH64_hash_t -XXH3_64bits_withSecret(const void* input, size_t len, const void* secret, size_t secretSize) +XXH3_64bits_withSeed(XXH_NOESCAPE const void* input, size_t length, XXH64_hash_t seed) { - return XXH3_64bits_internal(input, len, 0, secret, secretSize, XXH3_hashLong_64b_withSecret); + return XXH3_64bits_internal(input, length, seed, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_64b_withSeed); } XXH_PUBLIC_API XXH64_hash_t -XXH3_64bits_withSeed(const void* input, size_t len, XXH64_hash_t seed) +XXH3_64bits_withSecretandSeed(XXH_NOESCAPE const void* input, size_t length, XXH_NOESCAPE const void* secret, size_t secretSize, XXH64_hash_t seed) { - return XXH3_64bits_internal(input, len, seed, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_64b_withSeed); + if (length <= XXH3_MIDSIZE_MAX) + return XXH3_64bits_internal(input, length, seed, XXH3_kSecret, sizeof(XXH3_kSecret), NULL); + return XXH3_hashLong_64b_withSecret(input, length, seed, (const xxh_u8*)secret, secretSize); } /* === XXH3 streaming === */ - +#ifndef XXH_NO_STREAM /* * Malloc's a pointer that is always aligned to align. * @@ -3906,7 +5769,7 @@ XXH3_64bits_withSeed(const void* input, size_t len, XXH64_hash_t seed) * * Align must be a power of 2 and 8 <= align <= 128. */ -static void* XXH_alignedMalloc(size_t s, size_t align) +static XXH_MALLOCF void* XXH_alignedMalloc(size_t s, size_t align) { XXH_ASSERT(align <= 128 && align >= 8); /* range check */ XXH_ASSERT((align & (align-1)) == 0); /* power of 2 */ @@ -3948,6 +5811,13 @@ static void XXH_alignedFree(void* p) XXH_free(base); } } +/*! @ingroup XXH3_family */ +/*! + * @brief Allocate an @ref XXH3_state_t. + * + * Must be freed with XXH3_freeState(). + * @return An allocated XXH3_state_t on success, `NULL` on failure. + */ XXH_PUBLIC_API XXH3_state_t* XXH3_createState(void) { XXH3_state_t* const state = (XXH3_state_t*)XXH_alignedMalloc(sizeof(XXH3_state_t), 64); @@ -3956,22 +5826,31 @@ XXH_PUBLIC_API XXH3_state_t* XXH3_createState(void) return state; } +/*! @ingroup XXH3_family */ +/*! + * @brief Frees an @ref XXH3_state_t. + * + * Must be allocated with XXH3_createState(). + * @param statePtr A pointer to an @ref XXH3_state_t allocated with @ref XXH3_createState(). + * @return XXH_OK. + */ XXH_PUBLIC_API XXH_errorcode XXH3_freeState(XXH3_state_t* statePtr) { XXH_alignedFree(statePtr); return XXH_OK; } +/*! @ingroup XXH3_family */ XXH_PUBLIC_API void -XXH3_copyState(XXH3_state_t* dst_state, const XXH3_state_t* src_state) +XXH3_copyState(XXH_NOESCAPE XXH3_state_t* dst_state, XXH_NOESCAPE const XXH3_state_t* src_state) { - memcpy(dst_state, src_state, sizeof(*dst_state)); + XXH_memcpy(dst_state, src_state, sizeof(*dst_state)); } static void -XXH3_64bits_reset_internal(XXH3_state_t* statePtr, - XXH64_hash_t seed, - const void* secret, size_t secretSize) +XXH3_reset_internal(XXH3_state_t* statePtr, + XXH64_hash_t seed, + const void* secret, size_t secretSize) { size_t const initStart = offsetof(XXH3_state_t, bufferedSize); size_t const initLength = offsetof(XXH3_state_t, nbStripesPerBlock) - initStart; @@ -3988,95 +5867,153 @@ XXH3_64bits_reset_internal(XXH3_state_t* statePtr, statePtr->acc[6] = XXH_PRIME64_5; statePtr->acc[7] = XXH_PRIME32_1; statePtr->seed = seed; + statePtr->useSeed = (seed != 0); statePtr->extSecret = (const unsigned char*)secret; XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); statePtr->secretLimit = secretSize - XXH_STRIPE_LEN; statePtr->nbStripesPerBlock = statePtr->secretLimit / XXH_SECRET_CONSUME_RATE; } +/*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH_errorcode -XXH3_64bits_reset(XXH3_state_t* statePtr) +XXH3_64bits_reset(XXH_NOESCAPE XXH3_state_t* statePtr) { if (statePtr == NULL) return XXH_ERROR; - XXH3_64bits_reset_internal(statePtr, 0, XXH3_kSecret, XXH_SECRET_DEFAULT_SIZE); + XXH3_reset_internal(statePtr, 0, XXH3_kSecret, XXH_SECRET_DEFAULT_SIZE); return XXH_OK; } +/*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH_errorcode -XXH3_64bits_reset_withSecret(XXH3_state_t* statePtr, const void* secret, size_t secretSize) +XXH3_64bits_reset_withSecret(XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* secret, size_t secretSize) { if (statePtr == NULL) return XXH_ERROR; - XXH3_64bits_reset_internal(statePtr, 0, secret, secretSize); + XXH3_reset_internal(statePtr, 0, secret, secretSize); if (secret == NULL) return XXH_ERROR; if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR; return XXH_OK; } +/*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH_errorcode -XXH3_64bits_reset_withSeed(XXH3_state_t* statePtr, XXH64_hash_t seed) +XXH3_64bits_reset_withSeed(XXH_NOESCAPE XXH3_state_t* statePtr, XXH64_hash_t seed) { if (statePtr == NULL) return XXH_ERROR; if (seed==0) return XXH3_64bits_reset(statePtr); - if (seed != statePtr->seed) XXH3_initCustomSecret(statePtr->customSecret, seed); - XXH3_64bits_reset_internal(statePtr, seed, NULL, XXH_SECRET_DEFAULT_SIZE); + if ((seed != statePtr->seed) || (statePtr->extSecret != NULL)) + XXH3_initCustomSecret(statePtr->customSecret, seed); + XXH3_reset_internal(statePtr, seed, NULL, XXH_SECRET_DEFAULT_SIZE); return XXH_OK; } -/* Note : when XXH3_consumeStripes() is invoked, - * there must be a guarantee that at least one more byte must be consumed from input - * so that the function can blindly consume all stripes using the "normal" secret segment */ -XXH_FORCE_INLINE void +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH_errorcode +XXH3_64bits_reset_withSecretandSeed(XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* secret, size_t secretSize, XXH64_hash_t seed64) +{ + if (statePtr == NULL) return XXH_ERROR; + if (secret == NULL) return XXH_ERROR; + if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR; + XXH3_reset_internal(statePtr, seed64, secret, secretSize); + statePtr->useSeed = 1; /* always, even if seed64==0 */ + return XXH_OK; +} + +/*! + * @internal + * @brief Processes a large input for XXH3_update() and XXH3_digest_long(). + * + * Unlike XXH3_hashLong_internal_loop(), this can process data that overlaps a block. + * + * @param acc Pointer to the 8 accumulator lanes + * @param nbStripesSoFarPtr In/out pointer to the number of leftover stripes in the block* + * @param nbStripesPerBlock Number of stripes in a block + * @param input Input pointer + * @param nbStripes Number of stripes to process + * @param secret Secret pointer + * @param secretLimit Offset of the last block in @p secret + * @param f_acc Pointer to an XXH3_accumulate implementation + * @param f_scramble Pointer to an XXH3_scrambleAcc implementation + * @return Pointer past the end of @p input after processing + */ +XXH_FORCE_INLINE const xxh_u8 * XXH3_consumeStripes(xxh_u64* XXH_RESTRICT acc, size_t* XXH_RESTRICT nbStripesSoFarPtr, size_t nbStripesPerBlock, const xxh_u8* XXH_RESTRICT input, size_t nbStripes, const xxh_u8* XXH_RESTRICT secret, size_t secretLimit, - XXH3_f_accumulate_512 f_acc512, + XXH3_f_accumulate f_acc, XXH3_f_scrambleAcc f_scramble) { - XXH_ASSERT(nbStripes <= nbStripesPerBlock); /* can handle max 1 scramble per invocation */ - XXH_ASSERT(*nbStripesSoFarPtr < nbStripesPerBlock); - if (nbStripesPerBlock - *nbStripesSoFarPtr <= nbStripes) { - /* need a scrambling operation */ - size_t const nbStripesToEndofBlock = nbStripesPerBlock - *nbStripesSoFarPtr; - size_t const nbStripesAfterBlock = nbStripes - nbStripesToEndofBlock; - XXH3_accumulate(acc, input, secret + nbStripesSoFarPtr[0] * XXH_SECRET_CONSUME_RATE, nbStripesToEndofBlock, f_acc512); - f_scramble(acc, secret + secretLimit); - XXH3_accumulate(acc, input + nbStripesToEndofBlock * XXH_STRIPE_LEN, secret, nbStripesAfterBlock, f_acc512); - *nbStripesSoFarPtr = nbStripesAfterBlock; - } else { - XXH3_accumulate(acc, input, secret + nbStripesSoFarPtr[0] * XXH_SECRET_CONSUME_RATE, nbStripes, f_acc512); + const xxh_u8* initialSecret = secret + *nbStripesSoFarPtr * XXH_SECRET_CONSUME_RATE; + /* Process full blocks */ + if (nbStripes >= (nbStripesPerBlock - *nbStripesSoFarPtr)) { + /* Process the initial partial block... */ + size_t nbStripesThisIter = nbStripesPerBlock - *nbStripesSoFarPtr; + + do { + /* Accumulate and scramble */ + f_acc(acc, input, initialSecret, nbStripesThisIter); + f_scramble(acc, secret + secretLimit); + input += nbStripesThisIter * XXH_STRIPE_LEN; + nbStripes -= nbStripesThisIter; + /* Then continue the loop with the full block size */ + nbStripesThisIter = nbStripesPerBlock; + initialSecret = secret; + } while (nbStripes >= nbStripesPerBlock); + *nbStripesSoFarPtr = 0; + } + /* Process a partial block */ + if (nbStripes > 0) { + f_acc(acc, input, initialSecret, nbStripes); + input += nbStripes * XXH_STRIPE_LEN; *nbStripesSoFarPtr += nbStripes; } + /* Return end pointer */ + return input; } +#ifndef XXH3_STREAM_USE_STACK +# if XXH_SIZE_OPT <= 0 && !defined(__clang__) /* clang doesn't need additional stack space */ +# define XXH3_STREAM_USE_STACK 1 +# endif +#endif /* * Both XXH3_64bits_update and XXH3_128bits_update use this routine. */ XXH_FORCE_INLINE XXH_errorcode -XXH3_update(XXH3_state_t* state, - const xxh_u8* input, size_t len, - XXH3_f_accumulate_512 f_acc512, +XXH3_update(XXH3_state_t* XXH_RESTRICT const state, + const xxh_u8* XXH_RESTRICT input, size_t len, + XXH3_f_accumulate f_acc, XXH3_f_scrambleAcc f_scramble) { - if (input==NULL) -#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + if (input==NULL) { + XXH_ASSERT(len == 0); return XXH_OK; -#else - return XXH_ERROR; -#endif + } + XXH_ASSERT(state != NULL); { const xxh_u8* const bEnd = input + len; const unsigned char* const secret = (state->extSecret == NULL) ? state->customSecret : state->extSecret; - +#if defined(XXH3_STREAM_USE_STACK) && XXH3_STREAM_USE_STACK >= 1 + /* For some reason, gcc and MSVC seem to suffer greatly + * when operating accumulators directly into state. + * Operating into stack space seems to enable proper optimization. + * clang, on the other hand, doesn't seem to need this trick */ + XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[8]; + XXH_memcpy(acc, state->acc, sizeof(acc)); +#else + xxh_u64* XXH_RESTRICT const acc = state->acc; +#endif state->totalLen += len; + XXH_ASSERT(state->bufferedSize <= XXH3_INTERNALBUFFER_SIZE); - if (state->bufferedSize + len <= XXH3_INTERNALBUFFER_SIZE) { /* fill in tmp buffer */ + /* small input : just fill in tmp buffer */ + if (len <= XXH3_INTERNALBUFFER_SIZE - state->bufferedSize) { XXH_memcpy(state->buffer + state->bufferedSize, input, len); state->bufferedSize += (XXH32_hash_t)len; return XXH_OK; } - /* total input is now > XXH3_INTERNALBUFFER_SIZE */ + /* total input is now > XXH3_INTERNALBUFFER_SIZE */ #define XXH3_INTERNALBUFFER_STRIPES (XXH3_INTERNALBUFFER_SIZE / XXH_STRIPE_LEN) XXH_STATIC_ASSERT(XXH3_INTERNALBUFFER_SIZE % XXH_STRIPE_LEN == 0); /* clean multiple */ @@ -4088,44 +6025,45 @@ XXH3_update(XXH3_state_t* state, size_t const loadSize = XXH3_INTERNALBUFFER_SIZE - state->bufferedSize; XXH_memcpy(state->buffer + state->bufferedSize, input, loadSize); input += loadSize; - XXH3_consumeStripes(state->acc, + XXH3_consumeStripes(acc, &state->nbStripesSoFar, state->nbStripesPerBlock, state->buffer, XXH3_INTERNALBUFFER_STRIPES, secret, state->secretLimit, - f_acc512, f_scramble); + f_acc, f_scramble); state->bufferedSize = 0; } XXH_ASSERT(input < bEnd); + if (bEnd - input > XXH3_INTERNALBUFFER_SIZE) { + size_t nbStripes = (size_t)(bEnd - 1 - input) / XXH_STRIPE_LEN; + input = XXH3_consumeStripes(acc, + &state->nbStripesSoFar, state->nbStripesPerBlock, + input, nbStripes, + secret, state->secretLimit, + f_acc, f_scramble); + XXH_memcpy(state->buffer + sizeof(state->buffer) - XXH_STRIPE_LEN, input - XXH_STRIPE_LEN, XXH_STRIPE_LEN); - /* Consume input by a multiple of internal buffer size */ - if (input+XXH3_INTERNALBUFFER_SIZE < bEnd) { - const xxh_u8* const limit = bEnd - XXH3_INTERNALBUFFER_SIZE; - do { - XXH3_consumeStripes(state->acc, - &state->nbStripesSoFar, state->nbStripesPerBlock, - input, XXH3_INTERNALBUFFER_STRIPES, - secret, state->secretLimit, - f_acc512, f_scramble); - input += XXH3_INTERNALBUFFER_SIZE; - } while (inputbuffer + sizeof(state->buffer) - XXH_STRIPE_LEN, input - XXH_STRIPE_LEN, XXH_STRIPE_LEN); } - XXH_ASSERT(input < bEnd); - /* Some remaining input (always) : buffer it */ + XXH_ASSERT(input < bEnd); + XXH_ASSERT(bEnd - input <= XXH3_INTERNALBUFFER_SIZE); + XXH_ASSERT(state->bufferedSize == 0); XXH_memcpy(state->buffer, input, (size_t)(bEnd-input)); state->bufferedSize = (XXH32_hash_t)(bEnd-input); +#if defined(XXH3_STREAM_USE_STACK) && XXH3_STREAM_USE_STACK >= 1 + /* save stack accumulators into state */ + XXH_memcpy(state->acc, acc, sizeof(acc)); +#endif } return XXH_OK; } +/*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH_errorcode -XXH3_64bits_update(XXH3_state_t* state, const void* input, size_t len) +XXH3_64bits_update(XXH_NOESCAPE XXH3_state_t* state, XXH_NOESCAPE const void* input, size_t len) { return XXH3_update(state, (const xxh_u8*)input, len, - XXH3_accumulate_512, XXH3_scrambleAcc); + XXH3_accumulate, XXH3_scrambleAcc); } @@ -4134,36 +6072,40 @@ XXH3_digest_long (XXH64_hash_t* acc, const XXH3_state_t* state, const unsigned char* secret) { + xxh_u8 lastStripe[XXH_STRIPE_LEN]; + const xxh_u8* lastStripePtr; + /* * Digest on a local copy. This way, the state remains unaltered, and it can * continue ingesting more input afterwards. */ - memcpy(acc, state->acc, sizeof(state->acc)); + XXH_memcpy(acc, state->acc, sizeof(state->acc)); if (state->bufferedSize >= XXH_STRIPE_LEN) { + /* Consume remaining stripes then point to remaining data in buffer */ size_t const nbStripes = (state->bufferedSize - 1) / XXH_STRIPE_LEN; size_t nbStripesSoFar = state->nbStripesSoFar; XXH3_consumeStripes(acc, &nbStripesSoFar, state->nbStripesPerBlock, state->buffer, nbStripes, secret, state->secretLimit, - XXH3_accumulate_512, XXH3_scrambleAcc); - /* last stripe */ - XXH3_accumulate_512(acc, - state->buffer + state->bufferedSize - XXH_STRIPE_LEN, - secret + state->secretLimit - XXH_SECRET_LASTACC_START); + XXH3_accumulate, XXH3_scrambleAcc); + lastStripePtr = state->buffer + state->bufferedSize - XXH_STRIPE_LEN; } else { /* bufferedSize < XXH_STRIPE_LEN */ - xxh_u8 lastStripe[XXH_STRIPE_LEN]; + /* Copy to temp buffer */ size_t const catchupSize = XXH_STRIPE_LEN - state->bufferedSize; XXH_ASSERT(state->bufferedSize > 0); /* there is always some input buffered */ - memcpy(lastStripe, state->buffer + sizeof(state->buffer) - catchupSize, catchupSize); - memcpy(lastStripe + catchupSize, state->buffer, state->bufferedSize); - XXH3_accumulate_512(acc, - lastStripe, - secret + state->secretLimit - XXH_SECRET_LASTACC_START); + XXH_memcpy(lastStripe, state->buffer + sizeof(state->buffer) - catchupSize, catchupSize); + XXH_memcpy(lastStripe + catchupSize, state->buffer, state->bufferedSize); + lastStripePtr = lastStripe; } + /* Last stripe */ + XXH3_accumulate_512(acc, + lastStripePtr, + secret + state->secretLimit - XXH_SECRET_LASTACC_START); } -XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_digest (const XXH3_state_t* state) +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_digest (XXH_NOESCAPE const XXH3_state_t* state) { const unsigned char* const secret = (state->extSecret == NULL) ? state->customSecret : state->extSecret; if (state->totalLen > XXH3_MIDSIZE_MAX) { @@ -4174,56 +6116,12 @@ XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_digest (const XXH3_state_t* state) (xxh_u64)state->totalLen * XXH_PRIME64_1); } /* totalLen <= XXH3_MIDSIZE_MAX: digesting a short input */ - if (state->seed) + if (state->useSeed) return XXH3_64bits_withSeed(state->buffer, (size_t)state->totalLen, state->seed); return XXH3_64bits_withSecret(state->buffer, (size_t)(state->totalLen), secret, state->secretLimit + XXH_STRIPE_LEN); } - - -#define XXH_MIN(x, y) (((x) > (y)) ? (y) : (x)) - -XXH_PUBLIC_API void -XXH3_generateSecret(void* secretBuffer, const void* customSeed, size_t customSeedSize) -{ - XXH_ASSERT(secretBuffer != NULL); - if (customSeedSize == 0) { - memcpy(secretBuffer, XXH3_kSecret, XXH_SECRET_DEFAULT_SIZE); - return; - } - XXH_ASSERT(customSeed != NULL); - - { size_t const segmentSize = sizeof(XXH128_hash_t); - size_t const nbSegments = XXH_SECRET_DEFAULT_SIZE / segmentSize; - XXH128_canonical_t scrambler; - XXH64_hash_t seeds[12]; - size_t segnb; - XXH_ASSERT(nbSegments == 12); - XXH_ASSERT(segmentSize * nbSegments == XXH_SECRET_DEFAULT_SIZE); /* exact multiple */ - XXH128_canonicalFromHash(&scrambler, XXH128(customSeed, customSeedSize, 0)); - - /* - * Copy customSeed to seeds[], truncating or repeating as necessary. - */ - { size_t toFill = XXH_MIN(customSeedSize, sizeof(seeds)); - size_t filled = toFill; - memcpy(seeds, customSeed, toFill); - while (filled < sizeof(seeds)) { - toFill = XXH_MIN(filled, sizeof(seeds) - filled); - memcpy((char*)seeds + filled, seeds, toFill); - filled += toFill; - } } - - /* generate secret */ - memcpy(secretBuffer, &scrambler, sizeof(scrambler)); - for (segnb=1; segnb < nbSegments; segnb++) { - size_t const segmentStart = segnb * segmentSize; - XXH128_canonical_t segment; - XXH128_canonicalFromHash(&segment, - XXH128(&scrambler, sizeof(scrambler), XXH_readLE64(seeds + segnb) + segnb) ); - memcpy((char*)secretBuffer + segmentStart, &segment, sizeof(segment)); - } } -} +#endif /* !XXH_NO_STREAM */ /* ========================================== @@ -4243,7 +6141,7 @@ XXH3_generateSecret(void* secretBuffer, const void* customSeed, size_t customSee * fast for a _128-bit_ hash on 32-bit (it usually clears XXH64). */ -XXH_FORCE_INLINE XXH128_hash_t +XXH_FORCE_INLINE XXH_PUREF XXH128_hash_t XXH3_len_1to3_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) { /* A doubled version of 1to3_64b with different constants. */ @@ -4272,7 +6170,7 @@ XXH3_len_1to3_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_ } } -XXH_FORCE_INLINE XXH128_hash_t +XXH_FORCE_INLINE XXH_PUREF XXH128_hash_t XXH3_len_4to8_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) { XXH_ASSERT(input != NULL); @@ -4292,14 +6190,14 @@ XXH3_len_4to8_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_ m128.low64 ^= (m128.high64 >> 3); m128.low64 = XXH_xorshift64(m128.low64, 35); - m128.low64 *= 0x9FB21C651E98DF25ULL; + m128.low64 *= PRIME_MX2; m128.low64 = XXH_xorshift64(m128.low64, 28); m128.high64 = XXH3_avalanche(m128.high64); return m128; } } -XXH_FORCE_INLINE XXH128_hash_t +XXH_FORCE_INLINE XXH_PUREF XXH128_hash_t XXH3_len_9to16_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) { XXH_ASSERT(input != NULL); @@ -4374,7 +6272,7 @@ XXH3_len_9to16_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64 /* * Assumption: `secret` size is >= XXH3_SECRET_SIZE_MIN */ -XXH_FORCE_INLINE XXH128_hash_t +XXH_FORCE_INLINE XXH_PUREF XXH128_hash_t XXH3_len_0to16_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) { XXH_ASSERT(len <= 16); @@ -4405,7 +6303,7 @@ XXH128_mix32B(XXH128_hash_t acc, const xxh_u8* input_1, const xxh_u8* input_2, } -XXH_FORCE_INLINE XXH128_hash_t +XXH_FORCE_INLINE XXH_PUREF XXH128_hash_t XXH3_len_17to128_128b(const xxh_u8* XXH_RESTRICT input, size_t len, const xxh_u8* XXH_RESTRICT secret, size_t secretSize, XXH64_hash_t seed) @@ -4416,6 +6314,16 @@ XXH3_len_17to128_128b(const xxh_u8* XXH_RESTRICT input, size_t len, { XXH128_hash_t acc; acc.low64 = len * XXH_PRIME64_1; acc.high64 = 0; + +#if XXH_SIZE_OPT >= 1 + { + /* Smaller, but slightly slower. */ + unsigned int i = (unsigned int)(len - 1) / 32; + do { + acc = XXH128_mix32B(acc, input+16*i, input+len-16*(i+1), secret+32*i, seed); + } while (i-- != 0); + } +#else if (len > 32) { if (len > 64) { if (len > 96) { @@ -4426,6 +6334,7 @@ XXH3_len_17to128_128b(const xxh_u8* XXH_RESTRICT input, size_t len, acc = XXH128_mix32B(acc, input+16, input+len-32, secret+32, seed); } acc = XXH128_mix32B(acc, input, input+len-16, secret, seed); +#endif { XXH128_hash_t h128; h128.low64 = acc.low64 + acc.high64; h128.high64 = (acc.low64 * XXH_PRIME64_1) @@ -4438,7 +6347,7 @@ XXH3_len_17to128_128b(const xxh_u8* XXH_RESTRICT input, size_t len, } } -XXH_NO_INLINE XXH128_hash_t +XXH_NO_INLINE XXH_PUREF XXH128_hash_t XXH3_len_129to240_128b(const xxh_u8* XXH_RESTRICT input, size_t len, const xxh_u8* XXH_RESTRICT secret, size_t secretSize, XXH64_hash_t seed) @@ -4447,25 +6356,34 @@ XXH3_len_129to240_128b(const xxh_u8* XXH_RESTRICT input, size_t len, XXH_ASSERT(128 < len && len <= XXH3_MIDSIZE_MAX); { XXH128_hash_t acc; - int const nbRounds = (int)len / 32; - int i; + unsigned i; acc.low64 = len * XXH_PRIME64_1; acc.high64 = 0; - for (i=0; i<4; i++) { + /* + * We set as `i` as offset + 32. We do this so that unchanged + * `len` can be used as upper bound. This reaches a sweet spot + * where both x86 and aarch64 get simple agen and good codegen + * for the loop. + */ + for (i = 32; i < 160; i += 32) { acc = XXH128_mix32B(acc, - input + (32 * i), - input + (32 * i) + 16, - secret + (32 * i), + input + i - 32, + input + i - 16, + secret + i - 32, seed); } acc.low64 = XXH3_avalanche(acc.low64); acc.high64 = XXH3_avalanche(acc.high64); - XXH_ASSERT(nbRounds >= 4); - for (i=4 ; i < nbRounds; i++) { + /* + * NB: `i <= len` will duplicate the last 32-bytes if + * len % 32 was zero. This is an unfortunate necessity to keep + * the hash result stable. + */ + for (i=160; i <= len; i += 32) { acc = XXH128_mix32B(acc, - input + (32 * i), - input + (32 * i) + 16, - secret + XXH3_MIDSIZE_STARTOFFSET + (32 * (i - 4)), + input + i - 32, + input + i - 16, + secret + XXH3_MIDSIZE_STARTOFFSET + i - 160, seed); } /* last bytes */ @@ -4473,7 +6391,7 @@ XXH3_len_129to240_128b(const xxh_u8* XXH_RESTRICT input, size_t len, input + len - 16, input + len - 32, secret + XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET - 16, - 0ULL - seed); + (XXH64_hash_t)0 - seed); { XXH128_hash_t h128; h128.low64 = acc.low64 + acc.high64; @@ -4490,12 +6408,12 @@ XXH3_len_129to240_128b(const xxh_u8* XXH_RESTRICT input, size_t len, XXH_FORCE_INLINE XXH128_hash_t XXH3_hashLong_128b_internal(const void* XXH_RESTRICT input, size_t len, const xxh_u8* XXH_RESTRICT secret, size_t secretSize, - XXH3_f_accumulate_512 f_acc512, + XXH3_f_accumulate f_acc, XXH3_f_scrambleAcc f_scramble) { XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[XXH_ACC_NB] = XXH3_INIT_ACC; - XXH3_hashLong_internal_loop(acc, (const xxh_u8*)input, len, secret, secretSize, f_acc512, f_scramble); + XXH3_hashLong_internal_loop(acc, (const xxh_u8*)input, len, secret, secretSize, f_acc, f_scramble); /* converge into final hash */ XXH_STATIC_ASSERT(sizeof(acc) == 64); @@ -4513,46 +6431,50 @@ XXH3_hashLong_128b_internal(const void* XXH_RESTRICT input, size_t len, } /* - * It's important for performance that XXH3_hashLong is not inlined. + * It's important for performance that XXH3_hashLong() is not inlined. */ -XXH_NO_INLINE XXH128_hash_t +XXH_NO_INLINE XXH_PUREF XXH128_hash_t XXH3_hashLong_128b_default(const void* XXH_RESTRICT input, size_t len, XXH64_hash_t seed64, const void* XXH_RESTRICT secret, size_t secretLen) { (void)seed64; (void)secret; (void)secretLen; return XXH3_hashLong_128b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret), - XXH3_accumulate_512, XXH3_scrambleAcc); + XXH3_accumulate, XXH3_scrambleAcc); } /* - * It's important for performance that XXH3_hashLong is not inlined. + * It's important for performance to pass @p secretLen (when it's static) + * to the compiler, so that it can properly optimize the vectorized loop. + * + * When the secret size is unknown, or on GCC 12 where the mix of NO_INLINE and FORCE_INLINE + * breaks -Og, this is XXH_NO_INLINE. */ -XXH_NO_INLINE XXH128_hash_t +XXH3_WITH_SECRET_INLINE XXH128_hash_t XXH3_hashLong_128b_withSecret(const void* XXH_RESTRICT input, size_t len, XXH64_hash_t seed64, const void* XXH_RESTRICT secret, size_t secretLen) { (void)seed64; return XXH3_hashLong_128b_internal(input, len, (const xxh_u8*)secret, secretLen, - XXH3_accumulate_512, XXH3_scrambleAcc); + XXH3_accumulate, XXH3_scrambleAcc); } XXH_FORCE_INLINE XXH128_hash_t XXH3_hashLong_128b_withSeed_internal(const void* XXH_RESTRICT input, size_t len, XXH64_hash_t seed64, - XXH3_f_accumulate_512 f_acc512, + XXH3_f_accumulate f_acc, XXH3_f_scrambleAcc f_scramble, XXH3_f_initCustomSecret f_initSec) { if (seed64 == 0) return XXH3_hashLong_128b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret), - f_acc512, f_scramble); + f_acc, f_scramble); { XXH_ALIGN(XXH_SEC_ALIGN) xxh_u8 secret[XXH_SECRET_DEFAULT_SIZE]; f_initSec(secret, seed64); return XXH3_hashLong_128b_internal(input, len, (const xxh_u8*)secret, sizeof(secret), - f_acc512, f_scramble); + f_acc, f_scramble); } } @@ -4565,7 +6487,7 @@ XXH3_hashLong_128b_withSeed(const void* input, size_t len, { (void)secret; (void)secretLen; return XXH3_hashLong_128b_withSeed_internal(input, len, seed64, - XXH3_accumulate_512, XXH3_scrambleAcc, XXH3_initCustomSecret); + XXH3_accumulate, XXH3_scrambleAcc, XXH3_initCustomSecret); } typedef XXH128_hash_t (*XXH3_hashLong128_f)(const void* XXH_RESTRICT, size_t, @@ -4595,87 +6517,93 @@ XXH3_128bits_internal(const void* input, size_t len, /* === Public XXH128 API === */ -XXH_PUBLIC_API XXH128_hash_t XXH3_128bits(const void* input, size_t len) +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH128_hash_t XXH3_128bits(XXH_NOESCAPE const void* input, size_t len) { return XXH3_128bits_internal(input, len, 0, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_128b_default); } +/*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH128_hash_t -XXH3_128bits_withSecret(const void* input, size_t len, const void* secret, size_t secretSize) +XXH3_128bits_withSecret(XXH_NOESCAPE const void* input, size_t len, XXH_NOESCAPE const void* secret, size_t secretSize) { return XXH3_128bits_internal(input, len, 0, (const xxh_u8*)secret, secretSize, XXH3_hashLong_128b_withSecret); } +/*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH128_hash_t -XXH3_128bits_withSeed(const void* input, size_t len, XXH64_hash_t seed) +XXH3_128bits_withSeed(XXH_NOESCAPE const void* input, size_t len, XXH64_hash_t seed) { return XXH3_128bits_internal(input, len, seed, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_128b_withSeed); } +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH128_hash_t +XXH3_128bits_withSecretandSeed(XXH_NOESCAPE const void* input, size_t len, XXH_NOESCAPE const void* secret, size_t secretSize, XXH64_hash_t seed) +{ + if (len <= XXH3_MIDSIZE_MAX) + return XXH3_128bits_internal(input, len, seed, XXH3_kSecret, sizeof(XXH3_kSecret), NULL); + return XXH3_hashLong_128b_withSecret(input, len, seed, secret, secretSize); +} + +/*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH128_hash_t -XXH128(const void* input, size_t len, XXH64_hash_t seed) +XXH128(XXH_NOESCAPE const void* input, size_t len, XXH64_hash_t seed) { return XXH3_128bits_withSeed(input, len, seed); } /* === XXH3 128-bit streaming === */ - +#ifndef XXH_NO_STREAM /* - * All the functions are actually the same as for 64-bit streaming variant. - * The only difference is the finalizatiom routine. + * All initialization and update functions are identical to 64-bit streaming variant. + * The only difference is the finalization routine. */ -static void -XXH3_128bits_reset_internal(XXH3_state_t* statePtr, - XXH64_hash_t seed, - const void* secret, size_t secretSize) +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH_errorcode +XXH3_128bits_reset(XXH_NOESCAPE XXH3_state_t* statePtr) { - XXH3_64bits_reset_internal(statePtr, seed, secret, secretSize); + return XXH3_64bits_reset(statePtr); } +/*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH_errorcode -XXH3_128bits_reset(XXH3_state_t* statePtr) +XXH3_128bits_reset_withSecret(XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* secret, size_t secretSize) { - if (statePtr == NULL) return XXH_ERROR; - XXH3_128bits_reset_internal(statePtr, 0, XXH3_kSecret, XXH_SECRET_DEFAULT_SIZE); - return XXH_OK; + return XXH3_64bits_reset_withSecret(statePtr, secret, secretSize); } +/*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH_errorcode -XXH3_128bits_reset_withSecret(XXH3_state_t* statePtr, const void* secret, size_t secretSize) +XXH3_128bits_reset_withSeed(XXH_NOESCAPE XXH3_state_t* statePtr, XXH64_hash_t seed) { - if (statePtr == NULL) return XXH_ERROR; - XXH3_128bits_reset_internal(statePtr, 0, secret, secretSize); - if (secret == NULL) return XXH_ERROR; - if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR; - return XXH_OK; + return XXH3_64bits_reset_withSeed(statePtr, seed); } +/*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH_errorcode -XXH3_128bits_reset_withSeed(XXH3_state_t* statePtr, XXH64_hash_t seed) +XXH3_128bits_reset_withSecretandSeed(XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* secret, size_t secretSize, XXH64_hash_t seed) { - if (statePtr == NULL) return XXH_ERROR; - if (seed==0) return XXH3_128bits_reset(statePtr); - if (seed != statePtr->seed) XXH3_initCustomSecret(statePtr->customSecret, seed); - XXH3_128bits_reset_internal(statePtr, seed, NULL, XXH_SECRET_DEFAULT_SIZE); - return XXH_OK; + return XXH3_64bits_reset_withSecretandSeed(statePtr, secret, secretSize, seed); } +/*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH_errorcode -XXH3_128bits_update(XXH3_state_t* state, const void* input, size_t len) +XXH3_128bits_update(XXH_NOESCAPE XXH3_state_t* state, XXH_NOESCAPE const void* input, size_t len) { - return XXH3_update(state, (const xxh_u8*)input, len, - XXH3_accumulate_512, XXH3_scrambleAcc); + return XXH3_64bits_update(state, input, len); } -XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_digest (const XXH3_state_t* state) +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_digest (XXH_NOESCAPE const XXH3_state_t* state) { const unsigned char* const secret = (state->extSecret == NULL) ? state->customSecret : state->extSecret; if (state->totalLen > XXH3_MIDSIZE_MAX) { @@ -4699,12 +6627,13 @@ XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_digest (const XXH3_state_t* state) return XXH3_128bits_withSecret(state->buffer, (size_t)(state->totalLen), secret, state->secretLimit + XXH_STRIPE_LEN); } - +#endif /* !XXH_NO_STREAM */ /* 128-bit utility functions */ #include /* memcmp, memcpy */ /* return : 1 is equal, 0 if different */ +/*! @ingroup XXH3_family */ XXH_PUBLIC_API int XXH128_isEqual(XXH128_hash_t h1, XXH128_hash_t h2) { /* note : XXH128_hash_t is compact, it has no padding byte */ @@ -4712,10 +6641,11 @@ XXH_PUBLIC_API int XXH128_isEqual(XXH128_hash_t h1, XXH128_hash_t h2) } /* This prototype is compatible with stdlib's qsort(). - * return : >0 if *h128_1 > *h128_2 - * <0 if *h128_1 < *h128_2 - * =0 if *h128_1 == *h128_2 */ -XXH_PUBLIC_API int XXH128_cmp(const void* h128_1, const void* h128_2) + * @return : >0 if *h128_1 > *h128_2 + * <0 if *h128_1 < *h128_2 + * =0 if *h128_1 == *h128_2 */ +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API int XXH128_cmp(XXH_NOESCAPE const void* h128_1, XXH_NOESCAPE const void* h128_2) { XXH128_hash_t const h1 = *(const XXH128_hash_t*)h128_1; XXH128_hash_t const h2 = *(const XXH128_hash_t*)h128_2; @@ -4727,20 +6657,22 @@ XXH_PUBLIC_API int XXH128_cmp(const void* h128_1, const void* h128_2) /*====== Canonical representation ======*/ +/*! @ingroup XXH3_family */ XXH_PUBLIC_API void -XXH128_canonicalFromHash(XXH128_canonical_t* dst, XXH128_hash_t hash) +XXH128_canonicalFromHash(XXH_NOESCAPE XXH128_canonical_t* dst, XXH128_hash_t hash) { XXH_STATIC_ASSERT(sizeof(XXH128_canonical_t) == sizeof(XXH128_hash_t)); if (XXH_CPU_LITTLE_ENDIAN) { hash.high64 = XXH_swap64(hash.high64); hash.low64 = XXH_swap64(hash.low64); } - memcpy(dst, &hash.high64, sizeof(hash.high64)); - memcpy((char*)dst + sizeof(hash.high64), &hash.low64, sizeof(hash.low64)); + XXH_memcpy(dst, &hash.high64, sizeof(hash.high64)); + XXH_memcpy((char*)dst + sizeof(hash.high64), &hash.low64, sizeof(hash.low64)); } +/*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH128_hash_t -XXH128_hashFromCanonical(const XXH128_canonical_t* src) +XXH128_hashFromCanonical(XXH_NOESCAPE const XXH128_canonical_t* src) { XXH128_hash_t h; h.high64 = XXH_readBE64(src); @@ -4748,19 +6680,94 @@ XXH128_hashFromCanonical(const XXH128_canonical_t* src) return h; } + + +/* ========================================== + * Secret generators + * ========================================== + */ +#define XXH_MIN(x, y) (((x) > (y)) ? (y) : (x)) + +XXH_FORCE_INLINE void XXH3_combine16(void* dst, XXH128_hash_t h128) +{ + XXH_writeLE64( dst, XXH_readLE64(dst) ^ h128.low64 ); + XXH_writeLE64( (char*)dst+8, XXH_readLE64((char*)dst+8) ^ h128.high64 ); +} + +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH_errorcode +XXH3_generateSecret(XXH_NOESCAPE void* secretBuffer, size_t secretSize, XXH_NOESCAPE const void* customSeed, size_t customSeedSize) +{ +#if (XXH_DEBUGLEVEL >= 1) + XXH_ASSERT(secretBuffer != NULL); + XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); +#else + /* production mode, assert() are disabled */ + if (secretBuffer == NULL) return XXH_ERROR; + if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR; +#endif + + if (customSeedSize == 0) { + customSeed = XXH3_kSecret; + customSeedSize = XXH_SECRET_DEFAULT_SIZE; + } +#if (XXH_DEBUGLEVEL >= 1) + XXH_ASSERT(customSeed != NULL); +#else + if (customSeed == NULL) return XXH_ERROR; +#endif + + /* Fill secretBuffer with a copy of customSeed - repeat as needed */ + { size_t pos = 0; + while (pos < secretSize) { + size_t const toCopy = XXH_MIN((secretSize - pos), customSeedSize); + memcpy((char*)secretBuffer + pos, customSeed, toCopy); + pos += toCopy; + } } + + { size_t const nbSeg16 = secretSize / 16; + size_t n; + XXH128_canonical_t scrambler; + XXH128_canonicalFromHash(&scrambler, XXH128(customSeed, customSeedSize, 0)); + for (n=0; nnul 2>&1 +if errorlevel 1 ( + echo WARNING: GNU utilities in PATH can break MSVC + echo. + set FIND=findstr +) + +:: Anchor to the project file path, otherwise the arguments would be invalid: +:: +:: * In theory this is unnecessary, but it makes the process more transparent +:: to people unfamiliar with VS's not-so-well-explained behaviors +:: +pushd "%~dp0..\projects\msvc\" +if errorlevel 1 exit /b 101 + :: Validating arguments passed from Visual Studio to use as variables: :: :: * Arguments with conflicting syntax can terminate the script abruptly :: * 'GAS_EXTRA' argument isn't used in some cases :: +set GAS_SRCDIR= +set GAS_OBJDIR= set GAS_EXTRA= for /f "tokens=1,2*" %%A in ('echo %*') do ( -set GAS_SRCDIR=%%A -set GAS_OBJDIR=%%B -set GAS_EXTRA=%%C + set GAS_SRCDIR=%%A + set GAS_OBJDIR=%%B + set GAS_EXTRA=%%C ) set GAS_CLARG=%GAS_OBJDIR% if defined GAS_EXTRA set GAS_CLARG=%GAS_OBJDIR% %GAS_EXTRA% :: "Anti-PEBCAK": :: -:: * Ensures that "Item A" and "Item B" exist, and send an error signal if not -:: * The sole purpose of this code is to make the script fail outside VS +:: * Ensure that 'GAS_SRCDIR' and 'GAS_OBJDIR' exist or are defined and send an +:: error signal if not :: -if not exist mupen64plus-core.vcxproj exit /b 202 -if not exist "%GAS_SRCDIR%asm_defines.c" exit /b 303 +if not exist "%GAS_SRCDIR%asm_defines.c" exit /b 202 +if not defined GAS_OBJDIR exit /b 303 :: Legacy code adaptation for this script: :: :: * Delete the libraries previously generated by 'gawk' or this script -:: * CL's stuff/witchcraft, if all goes well it will generate "Item C" +:: * CL's stuff/witchcraft, if all goes well it will generate 'asm_defines.obj' :: del /f /q "%GAS_SRCDIR%asm_defines_*" 2>nul cl /c /Fo%GAS_CLARG% /I ..\..\src "%GAS_SRCDIR%asm_defines.c" -:: If "Item C" does not exist, send error +:: If 'asm_defines.obj' does not exist, send error if not exist "%GAS_OBJDIR%asm_defines.obj" exit /b 404 if "%USE_GAWK%" NEQ "1" goto native @@ -48,8 +67,8 @@ goto log :: Adaptation of 'gen_asm_defines.awk' / 'gen_asm_script.sh': :: -:: 1. Display 'asm_defines.obj' (aka "Item C") as a list, looking only -:: for patterns '@ASM_DEFINE' +:: 1. Display 'asm_defines.obj' as a list, looking only for patterns +:: '@ASM_DEFINE' :: 2. Sort for easy reading, the result is interpreted as an array :: with ' ' as default delimiter in the 'for' command :: 3. The for's 'tokens=2,3' ignores pattern "1" (@ASM_DEFINE) and anything @@ -57,9 +76,9 @@ goto log :: 4. Print current values and repeat steps 3 and 4 on the next line :: until EOL is reached :: -for /f "tokens=2,3" %%J in ('type "%GAS_OBJDIR%asm_defines.obj" ^| find "@ASM_DEFINE" ^| sort') do ( -echo %%define %%J ^(%%K^)>>"%GAS_SRCDIR%asm_defines_nasm.h" -echo #define %%J ^(%%K^)>>"%GAS_SRCDIR%asm_defines_gas.h" +for /f "tokens=2,3" %%J in ('type "%GAS_OBJDIR%asm_defines.obj" ^| %FIND% "@ASM_DEFINE" ^| sort') do ( + echo %%define %%J ^(%%K^)>>"%GAS_SRCDIR%asm_defines_nasm.h" + echo #define %%J ^(%%K^)>>"%GAS_SRCDIR%asm_defines_gas.h" ) :log From e572484bca1df9d8cc9214894d436dad135361d8 Mon Sep 17 00:00:00 2001 From: M4xw Date: Wed, 12 Jul 2023 13:17:18 +0200 Subject: [PATCH 10/58] First batch of fixes --- Makefile.common | 2 ++ custom/mupen64plus-core/plugin/plugin.h | 5 +++++ custom/mupen64plus-next_common.h | 1 + libretro/libretro.c | 21 ++++++++++++++++---- mupen64plus-core/src/api/frontend.c | 1 - mupen64plus-core/src/main/main.c | 12 +++++++----- mupen64plus-core/src/plugin/dummy_input.c | 24 +++++++++++++++++++++-- mupen64plus-core/src/plugin/dummy_input.h | 5 +++++ mupen64plus-core/src/plugin/plugin.c | 7 ++++++- 9 files changed, 65 insertions(+), 13 deletions(-) diff --git a/Makefile.common b/Makefile.common index b1425312..75b68aac 100644 --- a/Makefile.common +++ b/Makefile.common @@ -54,6 +54,7 @@ SOURCES_C = \ $(CORE_DIR)/src/device/cart/is_viewer.c \ $(CORE_DIR)/src/device/cart/sram.c \ $(CORE_DIR)/src/device/controllers/game_controller.c \ + $(CORE_DIR)/src/device/controllers/vru_controller.c \ $(CORE_DIR)/src/device/controllers/paks/biopak.c \ $(CORE_DIR)/src/device/controllers/paks/mempak.c \ $(CORE_DIR)/src/device/controllers/paks/rumblepak.c \ @@ -72,6 +73,7 @@ SOURCES_C = \ $(CORE_DIR)/src/device/r4300/cached_interp.c \ $(CORE_DIR)/src/device/r4300/cp0.c \ $(CORE_DIR)/src/device/r4300/cp1.c \ + $(CORE_DIR)/src/device/r4300/cp2.c \ $(CORE_DIR)/src/device/r4300/idec.c \ $(CORE_DIR)/src/device/r4300/interrupt.c \ $(CORE_DIR)/src/device/r4300/pure_interp.c \ diff --git a/custom/mupen64plus-core/plugin/plugin.h b/custom/mupen64plus-core/plugin/plugin.h index 75c49447..306c8c88 100644 --- a/custom/mupen64plus-core/plugin/plugin.h +++ b/custom/mupen64plus-core/plugin/plugin.h @@ -116,6 +116,11 @@ typedef struct _input_plugin_functions ptr_SDL_KeyDown keyDown; ptr_SDL_KeyUp keyUp; ptr_RenderCallback renderCallback; + ptr_SendVRUWord sendVRUWord; + ptr_SetMicState setMicState; + ptr_ReadVRUResults readVRUResults; + ptr_ClearVRUWords clearVRUWords; + ptr_SetVRUWordMask setVRUWordMask; } input_plugin_functions; extern input_plugin_functions input; diff --git a/custom/mupen64plus-next_common.h b/custom/mupen64plus-next_common.h index 96d3f6a0..01e968f5 100644 --- a/custom/mupen64plus-next_common.h +++ b/custom/mupen64plus-next_common.h @@ -65,6 +65,7 @@ extern bool libretro_swap_buffer; // Misc Globals extern CONTROL Controls[4]; +extern struct xoshiro256pp_state l_mpk_idgen; // Savestate globals extern bool retro_savestate_complete; diff --git a/libretro/libretro.c b/libretro/libretro.c index 0147d955..33e31269 100644 --- a/libretro/libretro.c +++ b/libretro/libretro.c @@ -1760,10 +1760,23 @@ static void format_saved_memory(void) format_sram(saved_memory.sram); format_eeprom(saved_memory.eeprom, EEPROM_MAX_SIZE); format_flashram(saved_memory.flashram); - format_mempak(saved_memory.mempack + 0 * MEMPAK_SIZE); - format_mempak(saved_memory.mempack + 1 * MEMPAK_SIZE); - format_mempak(saved_memory.mempack + 2 * MEMPAK_SIZE); - format_mempak(saved_memory.mempack + 3 * MEMPAK_SIZE); + + for (int i = 0; i < GAME_CONTROLLERS_COUNT; ++i) + { + // Generate a random serial ID + uint32_t serial[6]; + int k; + for (k = 0; k < 6; ++k) + { + serial[k] = xoshiro256pp_next(&l_mpk_idgen); + } + + format_mempak(saved_memory.mempack + i * MEMPAK_SIZE, + serial, + DEFAULT_MEMPAK_DEVICEID, + DEFAULT_MEMPAK_BANKS, + DEFAULT_MEMPAK_VERSION); + } } void context_reset(void) diff --git a/mupen64plus-core/src/api/frontend.c b/mupen64plus-core/src/api/frontend.c index e063c92e..ab410a04 100644 --- a/mupen64plus-core/src/api/frontend.c +++ b/mupen64plus-core/src/api/frontend.c @@ -161,7 +161,6 @@ EXPORT m64p_error CALL CoreDoCommand(m64p_command Command, int ParamInt, void *P if (rval == M64ERR_SUCCESS) { l_DiskOpen = 1; - ScreenshotRomOpen(); cheat_init(&g_cheat_ctx); } return rval; diff --git a/mupen64plus-core/src/main/main.c b/mupen64plus-core/src/main/main.c index 1318243f..a03494d3 100644 --- a/mupen64plus-core/src/main/main.c +++ b/mupen64plus-core/src/main/main.c @@ -148,7 +148,7 @@ static const struct pak_interface* l_ipaks[PAK_MAX_SIZE]; static size_t l_pak_type_idx[6]; /* PRNG state - used for Mempaks ID generation */ -static struct xoshiro256pp_state l_mpk_idgen; +struct xoshiro256pp_state l_mpk_idgen; /********************************************************************************************************* * static functions @@ -351,6 +351,7 @@ static void main_set_speedlimiter(int enable) l_MainSpeedLimit = enable ? 1 : 0; } +#if 0 void main_speedlimiter_toggle(void) { if (netplay_is_init()) @@ -370,6 +371,7 @@ void main_speedlimiter_toggle(void) else main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "Speed limiter disabled"); } +#endif static int main_is_paused(void) { @@ -880,7 +882,7 @@ static void open_mpk_file(struct file_storage* storage) storage->data = saved_memory.mempack; storage->size = MEMPAK_SIZE * 4; // If player 1 we send, otherwise we recieve - netplay_read_storage("MPK.bin", storage->data, storage->size); + netplay_read_storage("mempak.mpk", storage->data, storage->size); } } @@ -897,7 +899,7 @@ static void open_fla_file(struct file_storage* storage) storage->data = saved_memory.flashram; storage->size = FLASHRAM_SIZE; // If player 1 we send, otherwise we recieve - netplay_read_storage("FLA.bin", storage->data, storage->size); + netplay_read_storage("flashram.fla", storage->data, storage->size); } } @@ -914,7 +916,7 @@ static void open_sra_file(struct file_storage* storage) storage->data = saved_memory.sram; storage->size = SRAM_SIZE; // If player 1 we send, otherwise we recieve - netplay_read_storage("SRA.bin", storage->data, storage->size); + netplay_read_storage("saveram.sra", storage->data, storage->size); } } @@ -931,7 +933,7 @@ static void open_eep_file(struct file_storage* storage) storage->data = saved_memory.eeprom; storage->size = EEPROM_MAX_SIZE; // If player 1 we send, otherwise we recieve - netplay_read_storage("EEP.bin", storage->data, storage->size); + netplay_read_storage("eeprom.eep", storage->data, storage->size); } } diff --git a/mupen64plus-core/src/plugin/dummy_input.c b/mupen64plus-core/src/plugin/dummy_input.c index a9b910cd..6774e20b 100644 --- a/mupen64plus-core/src/plugin/dummy_input.c +++ b/mupen64plus-core/src/plugin/dummy_input.c @@ -47,12 +47,12 @@ m64p_error dummyinput_PluginGetVersion(m64p_plugin_type *PluginType, int *Plugin return M64ERR_SUCCESS; } -void dummyinput_InitiateControllers (CONTROL_INFO ControlInfo) +void dummyinput_InitiateControllers(CONTROL_INFO ControlInfo) { ControlInfo.Controls[0].Present = 1; } -void dummyinput_GetKeys(int Control, BUTTONS * Keys ) +void dummyinput_GetKeys(int Control, BUTTONS *Keys) { Keys->Value = 0x0000; } @@ -85,3 +85,23 @@ void dummyinput_SDL_KeyUp(int keymod, int keysym) void dummyinput_RenderCallback(void) { } + +void dummy_SendVRUWord(uint16_t length, uint16_t *word, uint8_t lang) +{ +} + +void dummy_SetMicState(int state) +{ +} + +void dummy_ReadVRUResults(uint16_t *error_flags, uint16_t *num_results, uint16_t *mic_level, uint16_t *voice_level, uint16_t *voice_length, uint16_t *matches) +{ +} + +void dummy_ClearVRUWords(uint8_t length) +{ +} + +void dummy_SetVRUWordMask(uint8_t length, uint8_t *mask) +{ +} diff --git a/mupen64plus-core/src/plugin/dummy_input.h b/mupen64plus-core/src/plugin/dummy_input.h index 30d01147..524b929c 100644 --- a/mupen64plus-core/src/plugin/dummy_input.h +++ b/mupen64plus-core/src/plugin/dummy_input.h @@ -38,6 +38,11 @@ extern void dummyinput_RomClosed(void); extern void dummyinput_SDL_KeyDown(int keymod, int keysym); extern void dummyinput_SDL_KeyUp(int keymod, int keysym); extern void dummyinput_RenderCallback(void); +extern void dummy_SendVRUWord(uint16_t length, uint16_t *word, uint8_t lang); +extern void dummy_SetMicState(int state); +extern void dummy_ReadVRUResults(uint16_t *error_flags, uint16_t *num_results, uint16_t *mic_level, uint16_t *voice_level, uint16_t *voice_length, uint16_t *matches); +extern void dummy_ClearVRUWords(uint8_t length); +extern void dummy_SetVRUWordMask(uint8_t length, uint8_t *mask); #endif /* DUMMY_INPUT_H */ diff --git a/mupen64plus-core/src/plugin/plugin.c b/mupen64plus-core/src/plugin/plugin.c index f7d55192..e472a418 100644 --- a/mupen64plus-core/src/plugin/plugin.c +++ b/mupen64plus-core/src/plugin/plugin.c @@ -147,7 +147,12 @@ const input_plugin_functions dummy_input = { inputRomOpen, dummyinput_SDL_KeyDown, dummyinput_SDL_KeyUp, - dummyinput_RenderCallback + dummyinput_RenderCallback, + dummy_SendVRUWord, + dummy_SetMicState, + dummy_ReadVRUResults, + dummy_ClearVRUWords, + dummy_SetVRUWordMask }; static AUDIO_INFO audio_info; From d3e262917e4d954efb85aff882e2874233c05a3a Mon Sep 17 00:00:00 2001 From: M4xw Date: Wed, 12 Jul 2023 13:43:38 +0200 Subject: [PATCH 11/58] Yeet pre-generated headers --- .../r4300/new_dynarec/arm/asm_defines_gas.h | 35 ------------------- .../r4300/new_dynarec/arm/asm_defines_nasm.h | 35 ------------------- .../r4300/new_dynarec/arm64/asm_defines_gas.h | 35 ------------------- .../new_dynarec/arm64/asm_defines_nasm.h | 35 ------------------- .../r4300/new_dynarec/x64/asm_defines_gas.h | 35 ------------------- .../r4300/new_dynarec/x64/asm_defines_nasm.h | 35 ------------------- .../r4300/new_dynarec/x86/asm_defines_gas.h | 35 ------------------- .../r4300/new_dynarec/x86/asm_defines_nasm.h | 35 ------------------- 8 files changed, 280 deletions(-) delete mode 100644 mupen64plus-core/src/device/r4300/new_dynarec/arm/asm_defines_gas.h delete mode 100644 mupen64plus-core/src/device/r4300/new_dynarec/arm/asm_defines_nasm.h delete mode 100644 mupen64plus-core/src/device/r4300/new_dynarec/arm64/asm_defines_gas.h delete mode 100644 mupen64plus-core/src/device/r4300/new_dynarec/arm64/asm_defines_nasm.h delete mode 100644 mupen64plus-core/src/device/r4300/new_dynarec/x64/asm_defines_gas.h delete mode 100644 mupen64plus-core/src/device/r4300/new_dynarec/x64/asm_defines_nasm.h delete mode 100644 mupen64plus-core/src/device/r4300/new_dynarec/x86/asm_defines_gas.h delete mode 100644 mupen64plus-core/src/device/r4300/new_dynarec/x86/asm_defines_nasm.h diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/arm/asm_defines_gas.h b/mupen64plus-core/src/device/r4300/new_dynarec/arm/asm_defines_gas.h deleted file mode 100644 index b137e36c..00000000 --- a/mupen64plus-core/src/device/r4300/new_dynarec/arm/asm_defines_gas.h +++ /dev/null @@ -1,35 +0,0 @@ -#define offsetof_struct_device_r4300 (0x00000000) -#define offsetof_struct_r4300_core_cp0 (0x029014cc) -#define offsetof_struct_cp0_last_addr (0x0000017c) -#define offsetof_struct_cp0_count_per_op (0x00000180) -#define offsetof_struct_cp0_tlb (0x00000188) -#define offsetof_struct_tlb_entries (0x00000000) -#define offsetof_struct_tlb_LUT_r (0x00000680) -#define offsetof_struct_tlb_LUT_w (0x00400680) -#define offsetof_struct_r4300_core_cached_interp (0x00000098) -#define offsetof_struct_cached_interp_invalid_code (0x00000000) -#define offsetof_struct_r4300_core_new_dynarec_hot_state (0x02501000) -#define offsetof_struct_r4300_core_extra_memory (0x00501000) -#define offsetof_struct_new_dynarec_hot_state_dynarec_local (0x00000000) -#define offsetof_struct_new_dynarec_hot_state_cycle_count (0x00000040) -#define offsetof_struct_new_dynarec_hot_state_pending_exception (0x00000044) -#define offsetof_struct_new_dynarec_hot_state_pcaddr (0x00000048) -#define offsetof_struct_new_dynarec_hot_state_stop (0x0000004c) -#define offsetof_struct_new_dynarec_hot_state_invc_ptr (0x00000050) -#define offsetof_struct_new_dynarec_hot_state_fcr0 (0x0000006c) -#define offsetof_struct_new_dynarec_hot_state_fcr31 (0x00000070) -#define offsetof_struct_new_dynarec_hot_state_regs (0x00000078) -#define offsetof_struct_new_dynarec_hot_state_hi (0x00000178) -#define offsetof_struct_new_dynarec_hot_state_lo (0x00000180) -#define offsetof_struct_new_dynarec_hot_state_cp0_regs (0x00000188) -#define offsetof_struct_new_dynarec_hot_state_cp1_regs_simple (0x00000208) -#define offsetof_struct_new_dynarec_hot_state_cp1_regs_double (0x00000288) -#define offsetof_struct_new_dynarec_hot_state_rounding_modes (0x00000308) -#define offsetof_struct_new_dynarec_hot_state_branch_target (0x00000318) -#define offsetof_struct_new_dynarec_hot_state_pc (0x0000031c) -#define offsetof_struct_new_dynarec_hot_state_fake_pc (0x00000320) -#define offsetof_struct_new_dynarec_hot_state_rs (0x000003a8) -#define offsetof_struct_new_dynarec_hot_state_rt (0x000003b0) -#define offsetof_struct_new_dynarec_hot_state_rd (0x000003b8) -#define offsetof_struct_new_dynarec_hot_state_mini_ht (0x000003c4) -#define offsetof_struct_new_dynarec_hot_state_memory_map (0x000004c4) diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/arm/asm_defines_nasm.h b/mupen64plus-core/src/device/r4300/new_dynarec/arm/asm_defines_nasm.h deleted file mode 100644 index 508dc99b..00000000 --- a/mupen64plus-core/src/device/r4300/new_dynarec/arm/asm_defines_nasm.h +++ /dev/null @@ -1,35 +0,0 @@ -%define offsetof_struct_device_r4300 (0x00000000) -%define offsetof_struct_r4300_core_cp0 (0x029014cc) -%define offsetof_struct_cp0_last_addr (0x0000017c) -%define offsetof_struct_cp0_count_per_op (0x00000180) -%define offsetof_struct_cp0_tlb (0x00000188) -%define offsetof_struct_tlb_entries (0x00000000) -%define offsetof_struct_tlb_LUT_r (0x00000680) -%define offsetof_struct_tlb_LUT_w (0x00400680) -%define offsetof_struct_r4300_core_cached_interp (0x00000098) -%define offsetof_struct_cached_interp_invalid_code (0x00000000) -%define offsetof_struct_r4300_core_new_dynarec_hot_state (0x02501000) -%define offsetof_struct_r4300_core_extra_memory (0x00501000) -%define offsetof_struct_new_dynarec_hot_state_dynarec_local (0x00000000) -%define offsetof_struct_new_dynarec_hot_state_cycle_count (0x00000040) -%define offsetof_struct_new_dynarec_hot_state_pending_exception (0x00000044) -%define offsetof_struct_new_dynarec_hot_state_pcaddr (0x00000048) -%define offsetof_struct_new_dynarec_hot_state_stop (0x0000004c) -%define offsetof_struct_new_dynarec_hot_state_invc_ptr (0x00000050) -%define offsetof_struct_new_dynarec_hot_state_fcr0 (0x0000006c) -%define offsetof_struct_new_dynarec_hot_state_fcr31 (0x00000070) -%define offsetof_struct_new_dynarec_hot_state_regs (0x00000078) -%define offsetof_struct_new_dynarec_hot_state_hi (0x00000178) -%define offsetof_struct_new_dynarec_hot_state_lo (0x00000180) -%define offsetof_struct_new_dynarec_hot_state_cp0_regs (0x00000188) -%define offsetof_struct_new_dynarec_hot_state_cp1_regs_simple (0x00000208) -%define offsetof_struct_new_dynarec_hot_state_cp1_regs_double (0x00000288) -%define offsetof_struct_new_dynarec_hot_state_rounding_modes (0x00000308) -%define offsetof_struct_new_dynarec_hot_state_branch_target (0x00000318) -%define offsetof_struct_new_dynarec_hot_state_pc (0x0000031c) -%define offsetof_struct_new_dynarec_hot_state_fake_pc (0x00000320) -%define offsetof_struct_new_dynarec_hot_state_rs (0x000003a8) -%define offsetof_struct_new_dynarec_hot_state_rt (0x000003b0) -%define offsetof_struct_new_dynarec_hot_state_rd (0x000003b8) -%define offsetof_struct_new_dynarec_hot_state_mini_ht (0x000003c4) -%define offsetof_struct_new_dynarec_hot_state_memory_map (0x000004c4) diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/arm64/asm_defines_gas.h b/mupen64plus-core/src/device/r4300/new_dynarec/arm64/asm_defines_gas.h deleted file mode 100644 index 15fc3f6b..00000000 --- a/mupen64plus-core/src/device/r4300/new_dynarec/arm64/asm_defines_gas.h +++ /dev/null @@ -1,35 +0,0 @@ -#define offsetof_struct_device_r4300 (0x00000000) -#define offsetof_struct_r4300_core_cp0 (0x031017d8) -#define offsetof_struct_cp0_last_addr (0x00000278) -#define offsetof_struct_cp0_count_per_op (0x0000027c) -#define offsetof_struct_cp0_tlb (0x00000284) -#define offsetof_struct_tlb_entries (0x00000000) -#define offsetof_struct_tlb_LUT_r (0x00000680) -#define offsetof_struct_tlb_LUT_w (0x00400680) -#define offsetof_struct_r4300_core_cached_interp (0x000000e0) -#define offsetof_struct_cached_interp_invalid_code (0x00000000) -#define offsetof_struct_r4300_core_new_dynarec_hot_state (0x02901000) -#define offsetof_struct_r4300_core_extra_memory (0x00901000) -#define offsetof_struct_new_dynarec_hot_state_dynarec_local (0x00000000) -#define offsetof_struct_new_dynarec_hot_state_cycle_count (0x00000100) -#define offsetof_struct_new_dynarec_hot_state_pending_exception (0x00000104) -#define offsetof_struct_new_dynarec_hot_state_pcaddr (0x00000108) -#define offsetof_struct_new_dynarec_hot_state_stop (0x0000010c) -#define offsetof_struct_new_dynarec_hot_state_invc_ptr (0x00000110) -#define offsetof_struct_new_dynarec_hot_state_fcr0 (0x00000134) -#define offsetof_struct_new_dynarec_hot_state_fcr31 (0x00000138) -#define offsetof_struct_new_dynarec_hot_state_regs (0x00000140) -#define offsetof_struct_new_dynarec_hot_state_hi (0x00000240) -#define offsetof_struct_new_dynarec_hot_state_lo (0x00000248) -#define offsetof_struct_new_dynarec_hot_state_cp0_regs (0x00000250) -#define offsetof_struct_new_dynarec_hot_state_cp1_regs_simple (0x000002d0) -#define offsetof_struct_new_dynarec_hot_state_cp1_regs_double (0x000003d0) -#define offsetof_struct_new_dynarec_hot_state_rounding_modes (0x000004d0) -#define offsetof_struct_new_dynarec_hot_state_branch_target (0x000004e0) -#define offsetof_struct_new_dynarec_hot_state_pc (0x000004e8) -#define offsetof_struct_new_dynarec_hot_state_fake_pc (0x000004f0) -#define offsetof_struct_new_dynarec_hot_state_rs (0x000005b0) -#define offsetof_struct_new_dynarec_hot_state_rt (0x000005b8) -#define offsetof_struct_new_dynarec_hot_state_rd (0x000005c0) -#define offsetof_struct_new_dynarec_hot_state_mini_ht (0x000005d0) -#define offsetof_struct_new_dynarec_hot_state_memory_map (0x000007d0) diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/arm64/asm_defines_nasm.h b/mupen64plus-core/src/device/r4300/new_dynarec/arm64/asm_defines_nasm.h deleted file mode 100644 index a1a12ebd..00000000 --- a/mupen64plus-core/src/device/r4300/new_dynarec/arm64/asm_defines_nasm.h +++ /dev/null @@ -1,35 +0,0 @@ -%define offsetof_struct_device_r4300 (0x00000000) -%define offsetof_struct_r4300_core_cp0 (0x031017d8) -%define offsetof_struct_cp0_last_addr (0x00000278) -%define offsetof_struct_cp0_count_per_op (0x0000027c) -%define offsetof_struct_cp0_tlb (0x00000284) -%define offsetof_struct_tlb_entries (0x00000000) -%define offsetof_struct_tlb_LUT_r (0x00000680) -%define offsetof_struct_tlb_LUT_w (0x00400680) -%define offsetof_struct_r4300_core_cached_interp (0x000000e0) -%define offsetof_struct_cached_interp_invalid_code (0x00000000) -%define offsetof_struct_r4300_core_new_dynarec_hot_state (0x02901000) -%define offsetof_struct_r4300_core_extra_memory (0x00901000) -%define offsetof_struct_new_dynarec_hot_state_dynarec_local (0x00000000) -%define offsetof_struct_new_dynarec_hot_state_cycle_count (0x00000100) -%define offsetof_struct_new_dynarec_hot_state_pending_exception (0x00000104) -%define offsetof_struct_new_dynarec_hot_state_pcaddr (0x00000108) -%define offsetof_struct_new_dynarec_hot_state_stop (0x0000010c) -%define offsetof_struct_new_dynarec_hot_state_invc_ptr (0x00000110) -%define offsetof_struct_new_dynarec_hot_state_fcr0 (0x00000134) -%define offsetof_struct_new_dynarec_hot_state_fcr31 (0x00000138) -%define offsetof_struct_new_dynarec_hot_state_regs (0x00000140) -%define offsetof_struct_new_dynarec_hot_state_hi (0x00000240) -%define offsetof_struct_new_dynarec_hot_state_lo (0x00000248) -%define offsetof_struct_new_dynarec_hot_state_cp0_regs (0x00000250) -%define offsetof_struct_new_dynarec_hot_state_cp1_regs_simple (0x000002d0) -%define offsetof_struct_new_dynarec_hot_state_cp1_regs_double (0x000003d0) -%define offsetof_struct_new_dynarec_hot_state_rounding_modes (0x000004d0) -%define offsetof_struct_new_dynarec_hot_state_branch_target (0x000004e0) -%define offsetof_struct_new_dynarec_hot_state_pc (0x000004e8) -%define offsetof_struct_new_dynarec_hot_state_fake_pc (0x000004f0) -%define offsetof_struct_new_dynarec_hot_state_rs (0x000005b0) -%define offsetof_struct_new_dynarec_hot_state_rt (0x000005b8) -%define offsetof_struct_new_dynarec_hot_state_rd (0x000005c0) -%define offsetof_struct_new_dynarec_hot_state_mini_ht (0x000005d0) -%define offsetof_struct_new_dynarec_hot_state_memory_map (0x000007d0) diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/x64/asm_defines_gas.h b/mupen64plus-core/src/device/r4300/new_dynarec/x64/asm_defines_gas.h deleted file mode 100644 index 66d7f850..00000000 --- a/mupen64plus-core/src/device/r4300/new_dynarec/x64/asm_defines_gas.h +++ /dev/null @@ -1,35 +0,0 @@ -#define offsetof_struct_device_r4300 (0x00000000) -#define offsetof_struct_r4300_core_cp0 (0x031017e8) -#define offsetof_struct_cp0_last_addr (0x00000278) -#define offsetof_struct_cp0_count_per_op (0x0000027c) -#define offsetof_struct_cp0_tlb (0x00000284) -#define offsetof_struct_tlb_entries (0x00000000) -#define offsetof_struct_tlb_LUT_r (0x00000680) -#define offsetof_struct_tlb_LUT_w (0x00400680) -#define offsetof_struct_r4300_core_cached_interp (0x000000f0) -#define offsetof_struct_cached_interp_invalid_code (0x00000000) -#define offsetof_struct_r4300_core_new_dynarec_hot_state (0x02901000) -#define offsetof_struct_r4300_core_extra_memory (0x00901000) -#define offsetof_struct_new_dynarec_hot_state_dynarec_local (0x00000000) -#define offsetof_struct_new_dynarec_hot_state_cycle_count (0x00000100) -#define offsetof_struct_new_dynarec_hot_state_pending_exception (0x00000104) -#define offsetof_struct_new_dynarec_hot_state_pcaddr (0x00000108) -#define offsetof_struct_new_dynarec_hot_state_stop (0x0000010c) -#define offsetof_struct_new_dynarec_hot_state_invc_ptr (0x00000110) -#define offsetof_struct_new_dynarec_hot_state_fcr0 (0x00000134) -#define offsetof_struct_new_dynarec_hot_state_fcr31 (0x00000138) -#define offsetof_struct_new_dynarec_hot_state_regs (0x00000140) -#define offsetof_struct_new_dynarec_hot_state_hi (0x00000240) -#define offsetof_struct_new_dynarec_hot_state_lo (0x00000248) -#define offsetof_struct_new_dynarec_hot_state_cp0_regs (0x00000250) -#define offsetof_struct_new_dynarec_hot_state_cp1_regs_simple (0x000002d0) -#define offsetof_struct_new_dynarec_hot_state_cp1_regs_double (0x000003d0) -#define offsetof_struct_new_dynarec_hot_state_rounding_modes (0x000004d0) -#define offsetof_struct_new_dynarec_hot_state_branch_target (0x000004e0) -#define offsetof_struct_new_dynarec_hot_state_pc (0x000004e8) -#define offsetof_struct_new_dynarec_hot_state_fake_pc (0x000004f0) -#define offsetof_struct_new_dynarec_hot_state_rs (0x000005c0) -#define offsetof_struct_new_dynarec_hot_state_rt (0x000005c8) -#define offsetof_struct_new_dynarec_hot_state_rd (0x000005d0) -#define offsetof_struct_new_dynarec_hot_state_mini_ht (0x000005e0) -#define offsetof_struct_new_dynarec_hot_state_memory_map (0x000007e0) diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/x64/asm_defines_nasm.h b/mupen64plus-core/src/device/r4300/new_dynarec/x64/asm_defines_nasm.h deleted file mode 100644 index 4fb00663..00000000 --- a/mupen64plus-core/src/device/r4300/new_dynarec/x64/asm_defines_nasm.h +++ /dev/null @@ -1,35 +0,0 @@ -%define offsetof_struct_device_r4300 (0x00000000) -%define offsetof_struct_r4300_core_cp0 (0x031017e8) -%define offsetof_struct_cp0_last_addr (0x00000278) -%define offsetof_struct_cp0_count_per_op (0x0000027c) -%define offsetof_struct_cp0_tlb (0x00000284) -%define offsetof_struct_tlb_entries (0x00000000) -%define offsetof_struct_tlb_LUT_r (0x00000680) -%define offsetof_struct_tlb_LUT_w (0x00400680) -%define offsetof_struct_r4300_core_cached_interp (0x000000f0) -%define offsetof_struct_cached_interp_invalid_code (0x00000000) -%define offsetof_struct_r4300_core_new_dynarec_hot_state (0x02901000) -%define offsetof_struct_r4300_core_extra_memory (0x00901000) -%define offsetof_struct_new_dynarec_hot_state_dynarec_local (0x00000000) -%define offsetof_struct_new_dynarec_hot_state_cycle_count (0x00000100) -%define offsetof_struct_new_dynarec_hot_state_pending_exception (0x00000104) -%define offsetof_struct_new_dynarec_hot_state_pcaddr (0x00000108) -%define offsetof_struct_new_dynarec_hot_state_stop (0x0000010c) -%define offsetof_struct_new_dynarec_hot_state_invc_ptr (0x00000110) -%define offsetof_struct_new_dynarec_hot_state_fcr0 (0x00000134) -%define offsetof_struct_new_dynarec_hot_state_fcr31 (0x00000138) -%define offsetof_struct_new_dynarec_hot_state_regs (0x00000140) -%define offsetof_struct_new_dynarec_hot_state_hi (0x00000240) -%define offsetof_struct_new_dynarec_hot_state_lo (0x00000248) -%define offsetof_struct_new_dynarec_hot_state_cp0_regs (0x00000250) -%define offsetof_struct_new_dynarec_hot_state_cp1_regs_simple (0x000002d0) -%define offsetof_struct_new_dynarec_hot_state_cp1_regs_double (0x000003d0) -%define offsetof_struct_new_dynarec_hot_state_rounding_modes (0x000004d0) -%define offsetof_struct_new_dynarec_hot_state_branch_target (0x000004e0) -%define offsetof_struct_new_dynarec_hot_state_pc (0x000004e8) -%define offsetof_struct_new_dynarec_hot_state_fake_pc (0x000004f0) -%define offsetof_struct_new_dynarec_hot_state_rs (0x000005c0) -%define offsetof_struct_new_dynarec_hot_state_rt (0x000005c8) -%define offsetof_struct_new_dynarec_hot_state_rd (0x000005d0) -%define offsetof_struct_new_dynarec_hot_state_mini_ht (0x000005e0) -%define offsetof_struct_new_dynarec_hot_state_memory_map (0x000007e0) diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/x86/asm_defines_gas.h b/mupen64plus-core/src/device/r4300/new_dynarec/x86/asm_defines_gas.h deleted file mode 100644 index bf682339..00000000 --- a/mupen64plus-core/src/device/r4300/new_dynarec/x86/asm_defines_gas.h +++ /dev/null @@ -1,35 +0,0 @@ -#define offsetof_struct_device_r4300 (0x00000000) -#define offsetof_struct_r4300_core_cp0 (0x029014c0) -#define offsetof_struct_cp0_last_addr (0x0000017c) -#define offsetof_struct_cp0_count_per_op (0x00000180) -#define offsetof_struct_cp0_tlb (0x00000188) -#define offsetof_struct_tlb_entries (0x00000000) -#define offsetof_struct_tlb_LUT_r (0x00000680) -#define offsetof_struct_tlb_LUT_w (0x00400680) -#define offsetof_struct_r4300_core_cached_interp (0x00000098) -#define offsetof_struct_cached_interp_invalid_code (0x00000000) -#define offsetof_struct_r4300_core_new_dynarec_hot_state (0x02501000) -#define offsetof_struct_r4300_core_extra_memory (0x00501000) -#define offsetof_struct_new_dynarec_hot_state_dynarec_local (0x00000000) -#define offsetof_struct_new_dynarec_hot_state_cycle_count (0x00000040) -#define offsetof_struct_new_dynarec_hot_state_pending_exception (0x00000044) -#define offsetof_struct_new_dynarec_hot_state_pcaddr (0x00000048) -#define offsetof_struct_new_dynarec_hot_state_stop (0x0000004c) -#define offsetof_struct_new_dynarec_hot_state_invc_ptr (0x00000050) -#define offsetof_struct_new_dynarec_hot_state_fcr0 (0x0000006c) -#define offsetof_struct_new_dynarec_hot_state_fcr31 (0x00000070) -#define offsetof_struct_new_dynarec_hot_state_regs (0x00000074) -#define offsetof_struct_new_dynarec_hot_state_hi (0x00000174) -#define offsetof_struct_new_dynarec_hot_state_lo (0x0000017c) -#define offsetof_struct_new_dynarec_hot_state_cp0_regs (0x00000184) -#define offsetof_struct_new_dynarec_hot_state_cp1_regs_simple (0x00000204) -#define offsetof_struct_new_dynarec_hot_state_cp1_regs_double (0x00000284) -#define offsetof_struct_new_dynarec_hot_state_rounding_modes (0x00000304) -#define offsetof_struct_new_dynarec_hot_state_branch_target (0x00000314) -#define offsetof_struct_new_dynarec_hot_state_pc (0x00000318) -#define offsetof_struct_new_dynarec_hot_state_fake_pc (0x0000031c) -#define offsetof_struct_new_dynarec_hot_state_rs (0x000003a0) -#define offsetof_struct_new_dynarec_hot_state_rt (0x000003a8) -#define offsetof_struct_new_dynarec_hot_state_rd (0x000003b0) -#define offsetof_struct_new_dynarec_hot_state_mini_ht (0x000003bc) -#define offsetof_struct_new_dynarec_hot_state_memory_map (0x000004bc) diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/x86/asm_defines_nasm.h b/mupen64plus-core/src/device/r4300/new_dynarec/x86/asm_defines_nasm.h deleted file mode 100644 index af446575..00000000 --- a/mupen64plus-core/src/device/r4300/new_dynarec/x86/asm_defines_nasm.h +++ /dev/null @@ -1,35 +0,0 @@ -%define offsetof_struct_device_r4300 (0x00000000) -%define offsetof_struct_r4300_core_cp0 (0x029014c0) -%define offsetof_struct_cp0_last_addr (0x0000017c) -%define offsetof_struct_cp0_count_per_op (0x00000180) -%define offsetof_struct_cp0_tlb (0x00000188) -%define offsetof_struct_tlb_entries (0x00000000) -%define offsetof_struct_tlb_LUT_r (0x00000680) -%define offsetof_struct_tlb_LUT_w (0x00400680) -%define offsetof_struct_r4300_core_cached_interp (0x00000098) -%define offsetof_struct_cached_interp_invalid_code (0x00000000) -%define offsetof_struct_r4300_core_new_dynarec_hot_state (0x02501000) -%define offsetof_struct_r4300_core_extra_memory (0x00501000) -%define offsetof_struct_new_dynarec_hot_state_dynarec_local (0x00000000) -%define offsetof_struct_new_dynarec_hot_state_cycle_count (0x00000040) -%define offsetof_struct_new_dynarec_hot_state_pending_exception (0x00000044) -%define offsetof_struct_new_dynarec_hot_state_pcaddr (0x00000048) -%define offsetof_struct_new_dynarec_hot_state_stop (0x0000004c) -%define offsetof_struct_new_dynarec_hot_state_invc_ptr (0x00000050) -%define offsetof_struct_new_dynarec_hot_state_fcr0 (0x0000006c) -%define offsetof_struct_new_dynarec_hot_state_fcr31 (0x00000070) -%define offsetof_struct_new_dynarec_hot_state_regs (0x00000074) -%define offsetof_struct_new_dynarec_hot_state_hi (0x00000174) -%define offsetof_struct_new_dynarec_hot_state_lo (0x0000017c) -%define offsetof_struct_new_dynarec_hot_state_cp0_regs (0x00000184) -%define offsetof_struct_new_dynarec_hot_state_cp1_regs_simple (0x00000204) -%define offsetof_struct_new_dynarec_hot_state_cp1_regs_double (0x00000284) -%define offsetof_struct_new_dynarec_hot_state_rounding_modes (0x00000304) -%define offsetof_struct_new_dynarec_hot_state_branch_target (0x00000314) -%define offsetof_struct_new_dynarec_hot_state_pc (0x00000318) -%define offsetof_struct_new_dynarec_hot_state_fake_pc (0x0000031c) -%define offsetof_struct_new_dynarec_hot_state_rs (0x000003a0) -%define offsetof_struct_new_dynarec_hot_state_rt (0x000003a8) -%define offsetof_struct_new_dynarec_hot_state_rd (0x000003b0) -%define offsetof_struct_new_dynarec_hot_state_mini_ht (0x000003bc) -%define offsetof_struct_new_dynarec_hot_state_memory_map (0x000004bc) From 314e0114e288fc8c1adef27e997002b5fd7ebeff Mon Sep 17 00:00:00 2001 From: M4xw Date: Fri, 19 Jul 2024 12:31:58 +0200 Subject: [PATCH 12/58] Update Ini Headers --- .../mupen64plus-core/main/mupen64plus.ini.h | 370 +++++++++++++++++- 1 file changed, 349 insertions(+), 21 deletions(-) diff --git a/custom/mupen64plus-core/main/mupen64plus.ini.h b/custom/mupen64plus-core/main/mupen64plus.ini.h index d67d6791..4d05aa34 100644 --- a/custom/mupen64plus-core/main/mupen64plus.ini.h +++ b/custom/mupen64plus-core/main/mupen64plus.ini.h @@ -25,6 +25,11 @@ char inifile[] = "Rumble=Yes\n" "Players=4\n" "\n" +"[551324F5FD0A2B0BC2B1BFBB0FE29C08]\n" +"GoodName=007 - The World is Not Enough (E) [T+Spa1.0.1_IlDucci]\n" +"CRC=D8C74049 29CDD8C4\n" +"RefMD5=34AB1DEA3111A233A8B5C5679DE22E83\n" +"\n" "[9D58996A8AA91263B5CD45C385F45FE4]\n" "GoodName=007 - The World is Not Enough (U) [!]\n" "CRC=033F4C13 319EE7A7\n" @@ -212,9 +217,20 @@ char inifile[] = "[0A5ACFEA0C7CF68AE25202040D5AD1EB]\n" "GoodName=40 Winks (E) (M3) (Prototype)\n" "CRC=ABA51D09 C668BAD9\n" +"Players=2\n" "Mempak=Yes\n" "Rumble=Yes\n" "\n" +"[8C3E4FE5F6B2D37C66A71097E8B1C72A]\n" +"GoodName=40 Winks (U) (Prototype)\n" +"CRC=AA94493C 3736CC22\n" +"RefMD5=0A5ACFEA0C7CF68AE25202040D5AD1EB\n" +"\n" +"[B88C172ED14F4ACB43C4A352D71CC640]\n" +"GoodName=40 Winks (U) (Unl)\n" +"CRC=5EF44642 44D05A25\n" +"RefMD5=0A5ACFEA0C7CF68AE25202040D5AD1EB\n" +"\n" "[66335F4DC6AB27034398BC26F263B897]\n" "GoodName=64 Hanafuda - Tenshi no Yakusoku (J) [!]\n" "CRC=36F22FBF 318912F2\n" @@ -1275,6 +1291,11 @@ char inifile[] = "SaveType=Eeprom 4KB\n" "Rumble=Yes\n" "\n" +"[D0410D2C399CBF216BB756F35E5AC4EC]\n" +"GoodName=Banjo-Kazooie (E) [T+Spa1.1_PacoChan]\n" +"CRC=8B2FCAD3 361B412C\n" +"RefMD5=06A43BACF5C0687F596DF9B018CA6D7F\n" +"\n" "[5FC8116DAFDEC73DE9C5CCC2D8E14B4F]\n" "GoodName=Banjo-Kazooie (E) (M3) [f1]\n" "CRC=192C1422 5B8CFF48\n" @@ -1798,6 +1819,11 @@ char inifile[] = "CountPerOp=1\n" "Rumble=Yes\n" "\n" +"[8D21DFCDE6534127CBB444B7361B0D99]\n" +"GoodName=Body Harvest (U) [T+Spa0.98_jackic]\n" +"CRC=810CED2D D7B3F710\n" +"RefMD5=3B8585ED03E8DDB89D7DE456317545E7\n" +"\n" "[77554810629D4EF4D93519745FA6066B]\n" "GoodName=Body Harvest (U) [b1]\n" "CRC=5326696F FE9A99C3\n" @@ -1894,11 +1920,21 @@ char inifile[] = "CRC=E87F1ACE 809F204B\n" "RefMD5=08E491F87445C6E5C168D982FC665D5F\n" "\n" -"[08E491F87445C6E5C168D982FC665D5F]\n" +"[D54FD7067BD774E32B57F9C2C0496899]\n" "GoodName=Bomberman 64 - Arcade Edition (J) [!]\n" "CRC=DF6FF0F4 29D14238\n" "Players=4\n" "\n" +"[08E491F87445C6E5C168D982FC665D5F]\n" +"GoodName=Bomberman 64 - Arcade Edition (J) [b1]\n" +"CRC=DF6FF0F4 29D14238\n" +"RefMD5=D54FD7067BD774E32B57F9C2C0496899\n" +"\n" +"[C714806B4CC144A3EABD234C07412109]\n" +"GoodName=Bomberman 64 - Arcade Edition (J) [T+Eng1.3_Zoinkity]\n" +"CRC=9780C9FB 67CF6B4A\n" +"RefMD5=D54FD7067BD774E32B57F9C2C0496899\n" +"\n" "[AEC1FDB0F1CAAD86C9F457989A4CE482]\n" "GoodName=Bomberman 64 - The Second Attack! (U) [!]\n" "CRC=237E73B4 D63B6B37\n" @@ -2033,6 +2069,7 @@ char inifile[] = "SaveType=None\n" "Mempak=Yes\n" "Players=2\n" +"CountPerOp=1\n" "\n" "[6E207C47FAAD2A4F64AE721A3A8F6161]\n" "GoodName=Bottom of the 9th (U) [b1]\n" @@ -2775,7 +2812,7 @@ char inifile[] = "CRC=FB3C48D0 8D28F69F\n" "RefMD5=DD53E1F83E8789D23DF6AF942FFEF236\n" "\n" -"[59FA8C6D533D36C0FFC2AAFAB7166E6F]\n" +"[747E76D50DC3C06FD35E146129706A60]\n" "GoodName=Charlie Blast's Territory (U) [!]\n" "CRC=1E0E96E8 4E28826B\n" "SaveType=None\n" @@ -2783,15 +2820,20 @@ char inifile[] = "CountPerOp=1\n" "Rumble=Yes\n" "\n" +"[59FA8C6D533D36C0FFC2AAFAB7166E6F]\n" +"GoodName=Charlie Blast's Territory (U) [b1]\n" +"CRC=1E0E96E8 4E28826B\n" +"RefMD5=747E76D50DC3C06FD35E146129706A60\n" +"\n" "[7FFD84FC7D112EAB4EEF15CB10189A38]\n" "GoodName=Charlie Blast's Territory (U) [hIR]\n" "CRC=1E0E96E8 4E28826B\n" -"RefMD5=59FA8C6D533D36C0FFC2AAFAB7166E6F\n" +"RefMD5=747E76D50DC3C06FD35E146129706A60\n" "\n" "[7CBB43473597F48AA9A8E0B0D38CE685]\n" "GoodName=Charlie Blast's Territory (U) [hI]\n" "CRC=A6FD0808 6E6B3ECA\n" -"RefMD5=59FA8C6D533D36C0FFC2AAFAB7166E6F\n" +"RefMD5=747E76D50DC3C06FD35E146129706A60\n" "\n" "[8F6BED633BE214CF039DBDAC356231CE]\n" "GoodName=Chopper Attack (E) [!]\n" @@ -3135,6 +3177,16 @@ char inifile[] = "Players=4\n" "Rumble=Yes\n" "\n" +"[256564DAF29311497917BAAA14ED3F6D]\n" +"GoodName=Conker's Bad Fur Day (U) [T+Fre1.3_Corrigo]\n" +"CRC=30C7AC50 7704072D\n" +"RefMD5=00E2920665F2329B95797A7EAABC2390\n" +"\n" +"[79A8A007092D1E5B00D0ED94F83620EB]\n" +"GoodName=Conker's Bad Fur Day (U) [T+Spa1.1_Blade133bo]\n" +"CRC=34AEB154 A4253B86\n" +"RefMD5=00E2920665F2329B95797A7EAABC2390\n" +"\n" "[DB7A03B77D44DB81B8A3FCDFC4B72D8C]\n" "GoodName=Cruis'n Exotica (U) [!]\n" "CRC=46A3F7AF 0F7591D0\n" @@ -3558,6 +3610,11 @@ char inifile[] = "Mempak=Yes\n" "Rumble=Yes\n" "\n" +"[14ABD7C0FA3B5256FC62824039F06A20]\n" +"GoodName=Diddy Kong Racing (E) (M3) (V1.1) [T+Ita1.0_Rulesless]\n" +"CRC=568E02F5 E212A80C\n" +"RefMD5=6B2BAFE540E0AF052A78E85B992BE999\n" +"\n" "[35CF26D4D33717BA730D7978E2F2107D]\n" "GoodName=Diddy Kong Racing (E) (M3) (V1.1) [f1] (Z64)\n" "CRC=D84C17CA 13F8F651\n" @@ -4021,7 +4078,7 @@ char inifile[] = "CRC=916EB31B F47097A5\n" "\n" "[118E9CE360B97A6F8DAB2C9F30660BF5]\n" -"GoodName=Donkey Kong 64 (E) [!]\n" +"GoodName=Donkey Kong 64 (E) (M4) [!]\n" "CRC=11936D8C 6F2C4B43\n" "SaveType=Eeprom 16KB\n" "Players=4\n" @@ -4029,17 +4086,17 @@ char inifile[] = "CountPerOp=1\n" "\n" "[428067DC7DB42DFC977A775F0A6E55B1]\n" -"GoodName=Donkey Kong 64 (E) [b1]\n" +"GoodName=Donkey Kong 64 (E) (M4) [b1]\n" "CRC=11936D8C 6F2C4B43\n" "RefMD5=118E9CE360B97A6F8DAB2C9F30660BF5\n" "\n" "[C48C46362602766EA758FAAEED88AF50]\n" -"GoodName=Donkey Kong 64 (E) [f1] (Boot&Save)\n" +"GoodName=Donkey Kong 64 (E) (M4) [f1] (Boot&Save)\n" "CRC=1F95CAAA 047FC22A\n" "RefMD5=118E9CE360B97A6F8DAB2C9F30660BF5\n" "\n" "[7BE2AAB259C437E8BA91FF8E5903837C]\n" -"GoodName=Donkey Kong 64 (E) [f1] (Save)\n" +"GoodName=Donkey Kong 64 (E) (M4) [f1] (Save)\n" "CRC=1F95CAAA 047FC22A\n" "RefMD5=118E9CE360B97A6F8DAB2C9F30660BF5\n" "\n" @@ -4167,6 +4224,11 @@ char inifile[] = "CRC=13D0EE29 D57A41AD\n" "RefMD5=B67748B64A2CC7EFD2F3AD4504561E0E\n" "\n" +"[FE63F970BB7F1D6798878D5835E1026C]\n" +"GoodName=Doom 64: Complete Edition (Doom 64 Hack)\n" +"CRC=5318786C 994249C6\n" +"RefMD5=1B1378BB9EE819F740550F566745AF73\n" +"\n" "[1B1378BB9EE819F740550F566745AF73]\n" "GoodName=Doom 64 (U) (V1.1) [!]\n" "CRC=423E96F4 CE88F05B\n" @@ -4297,6 +4359,11 @@ char inifile[] = "Players=4\n" "SaveType=Eeprom 4KB\n" "\n" +"[36B3D908FB50E5521DEFBEB2C39F47B0]\n" +"GoodName=Dr. Mario 64 (U) [T+Jap1.0_Zoinkity]\n" +"CRC=B74388D7 7DBD6DFB\n" +"RefMD5=1A7936367413E5D6874ABDA6D623AD32\n" +"\n" "[0AD0800D351F77EC31092096D58C25F0]\n" "GoodName=Dragon King by CrowTRobo (PD) [b1]\n" "CRC=19E0E54C CB721FD2\n" @@ -5064,6 +5131,11 @@ char inifile[] = "Players=4\n" "Rumble=Yes\n" "\n" +"[20F5EEEE849DB44146AAFB1B97A857F6]\n" +"GoodName=F-ZERO X (U) [T+Por0.99_byftr]\n" +"CRC=485256FF CF6DF912\n" +"RefMD5=753437D0D8ADA1D12F3F9CF0F0A5171F\n" +"\n" "[3AE32658B839FFA3189E3CA84E0B884A]\n" "GoodName=F-ZERO X (U) [f1] (Sex V1.0 Hack)\n" "CRC=7E399849 03DAC1CD\n" @@ -5766,6 +5838,11 @@ char inifile[] = "Mempak=Yes\n" "Rumble=Yes\n" "\n" +"[0AC7A16F8AFCC03031835C89D095D7B9]\n" +"GoodName=Getter Love!! (J) [T+Eng1.01_ozidual]\n" +"CRC=CDDB4BDF 84E5EFAC\n" +"RefMD5=5270D98F9E67DC7EF354ECE109C2A18F\n" +"\n" "[3BE170E7E94A03BD4E36732EB137EE39]\n" "GoodName=Getter Love!! (J) [b1]\n" "CRC=489C84E6 4C6E49F9\n" @@ -5918,12 +5995,12 @@ char inifile[] = "RefMD5=231BAC1AFB3DE138072C2D697783059B\n" "\n" "[2DB6ED9A6A244CB209FFDF52D9006E4D]\n" -"GoodName=Golden Nugget 64 (U) [f1] (PAL)\n" +"GoodName=Golden Nugget 64 (U) [b3][f1] (PAL)\n" "CRC=438CEBDC 44C1837C\n" "RefMD5=231BAC1AFB3DE138072C2D697783059B\n" "\n" "[F68F02A69829469F208D538D7CF4A215]\n" -"GoodName=Golden Nugget 64 (U) [h1C]\n" +"GoodName=Golden Nugget 64 (U) [b3][h1C]\n" "CRC=4690FB1C 4CD56D44\n" "RefMD5=231BAC1AFB3DE138072C2D697783059B\n" "\n" @@ -5935,6 +6012,11 @@ char inifile[] = "Rumble=Yes\n" "Status=4\n" "\n" +"[6E23E6BAD28F8AC5C07344DCB0C266B8]\n" +"GoodName=GoldenEye 007 (E) [T+Spa2.0_Sogun&IlDucci]\n" +"CRC=625E0850 1F1B2327\n" +"RefMD5=CFF69B70A8AD674A0EFE5558765855C9\n" +"\n" "[ECD4F078DC77F4B2D12C1C0F5CABA323]\n" "GoodName=GoldenEye 007 (E) [b1]\n" "CRC=0414CA61 2E57B8AA\n" @@ -6007,6 +6089,11 @@ char inifile[] = "Rumble=Yes\n" "Status=4\n" "\n" +"[78F83525E0CC98641AEACE37E3FF7F16]\n" +"GoodName=GoldenEye 007 (U) [T+Spa2.0_Sogun&IlDucci]\n" +"CRC=3A03E50F E78D1CA7\n" +"RefMD5=70C525880240C1E838B8B1BE35666C3B\n" +"\n" "[0B5A50E8D477B3BC53BE997264FE84A5]\n" "GoodName=GoldenEye 007 (U) [b1]\n" "CRC=DCBC50D1 09FD1AA3\n" @@ -6046,6 +6133,25 @@ char inifile[] = "GoodName=GoldenEye 007 Intro by SonCrap (PD)\n" "CRC=9303DD17 0813B398\n" "\n" +"[67F2E18ED18A5F0EA3FE2D913E3F1786]\n" +"GoodName=GoldenEye X\n" +"; Alternate Title=GoldenEye X (5e Clouds)\n" +"CRC=FDF95D15 618546CA\n" +"RefMD5=E03B088B6AC9E0080440EFED07C1E40F\n" +"\n" +"[435D75D07878D305293EC4D591B86303]\n" +"GoodName=GoldenEye X\n" +"; Alternate Title=GoldenEye X (5e Cloudless)\n" +"CRC=E8B3F63D E5A997B1\n" +"RefMD5=E03B088B6AC9E0080440EFED07C1E40F\n" +"\n" +"[EA0E3E6AEFA58738A12906298373218B]\n" +"GoodName=GoldenEye 007 (UE) (Switch Online) [!]\n" +"CRC=DCBC50D1 9FD1AA3\n" +"Players=4\n" +"SaveType=Eeprom 4KB\n" +"Rumble=Yes\n" +"\n" "[9B8A7541D0234F4D97004ECBC216D9C2]\n" "GoodName=HIPTHRUST by MooglyGuy (PD)\n" "CRC=88A12FB3 8A583CBD\n" @@ -6157,6 +6263,11 @@ char inifile[] = "CRC=3E70E866 4438BAE8\n" "Players=1\n" "\n" +"[78E2ED1DB5F90E2EA9C04771B27685BC]\n" +"GoodName=Heiwa Pachinko World 64 (J) [T+Eng1.0_Zoinkity]\n" +"CRC=3AE679E2 1A3C42DB\n" +"RefMD5=5E8539E037EEA88C5A2746F60E431C8D\n" +"\n" "[9D0D85E3A0C94B98D6404E8FC28E7B01]\n" "GoodName=Heiwa Pachinko World 64 (J) [b1]\n" "CRC=3E70E866 4438BAE8\n" @@ -6296,6 +6407,7 @@ char inifile[] = "Players=1\n" "SaveType=Eeprom 4KB\n" "Rumble=Yes\n" +"AiDmaModifier=88\n" "\n" "[DCC316EFFC4928F5B0AE8D273D8024BF]\n" "GoodName=HiRes CFB Demo (PD)\n" @@ -7843,6 +7955,11 @@ char inifile[] = "Rumble=Yes\n" "Players=1\n" "\n" +"[A9BA7523591C975AC00309ED516FD248]\n" +"GoodName=Legend of Zelda, The - Majora's Mask (U) [T+Ita2.0_Rulesless]\n" +"CRC=5242DFBC 2FDE6D42\n" +"RefMD5=2A0A8ACB61538235BC1094D297FB6556\n" +"\n" "[9C7D1B51ECBC9AC333D00273F96758B1]\n" "GoodName=Legend of Zelda, The - Majora's Mask (U) [T+Pol1.0]\n" "CRC=5354631C 03A2DEF0\n" @@ -8037,6 +8154,11 @@ char inifile[] = "CRC=EC7011B7 7616D72B\n" "RefMD5=5BD1FE107BF8106B2AB6650ABECD54D6\n" "\n" +"[86A9550AD8CDDACA13813765045A00EE]\n" +"GoodName=Legend of Zelda, The - Ocarina of Time (U) (V1.0) [T+Spa2.2_eduardo_a2j]\n" +"CRC=EC7011B7 7616D72B\n" +"RefMD5=5BD1FE107BF8106B2AB6650ABECD54D6\n" +"\n" "[AAEB5ACA09291E8F5FEDFA1F576D9F0A]\n" "GoodName=Legend of Zelda, The - Ocarina of Time (U) (V1.0) [T-Pol1.2]\n" "CRC=EC7011B7 7616D72B\n" @@ -9251,11 +9373,21 @@ char inifile[] = "CRC=3E5055B6 2E92DA52\n" "RefMD5=3A67D9986F54EB282924FCA4CD5F6DFF\n" "\n" +"[22FACED432B0A9A4EC69A79452D16049]\n" +"GoodName=Mario Kart 64 (U) [T+Ita1.0_Rulesless]\n" +"CRC=4578A055 3F31815D\n" +"RefMD5=3A67D9986F54EB282924FCA4CD5F6DFF\n" +"\n" "[65CBD8DCEC6B7CE61E1CB233EFA4A317]\n" "GoodName=Mario Kart 64 (U) [T+Por1.0_Dr_X]\n" "CRC=3E5055B6 2E92DA52\n" "RefMD5=3A67D9986F54EB282924FCA4CD5F6DFF\n" "\n" +"[79743743904EEA727CB82725BE765EC6]\n" +"GoodName=Mario Kart 64 (U) [T+Spa1.1_Blade133bo]\n" +"CRC=40CA1EC9 8CC292D4\n" +"RefMD5=3A67D9986F54EB282924FCA4CD5F6DFF\n" +"\n" "[4A5E85A3E7118A742E3678F8F7E82B0E]\n" "GoodName=Mario Kart 64 (U) [b1]\n" "CRC=3E5055B6 2E92DA52\n" @@ -9319,6 +9451,11 @@ char inifile[] = "Rumble=Yes\n" "CountPerOp=1\n" "\n" +"[1EAC55826A435FD1C0E0FEEFE89FAC15]\n" +"GoodName=Mario Party (E) (M3) [T+Spa1.0_PacoChan]\n" +"CRC=45C59BC3 D4C62468\n" +"RefMD5=9773150709BD804B8E57E35F1D6B0EED\n" +"\n" "[2608A4D7A695D0B1A1BBC47695EACE0E]\n" "GoodName=Mario Party (E) (M3) [b1]\n" "CRC=9C663069 80F24A80\n" @@ -11889,12 +12026,18 @@ char inifile[] = "Rumble=Yes\n" "Transferpak=Yes\n" "\n" +"[5D72155E00Bf2CAB41B0F4A2f2E09C61]\n" +"GoodName=Perfect Dark Plus\n" +"CRC=FB453D58 AD565422\n" +"RefMD5=E03B088B6AC9E0080440EFED07C1E40F\n" +"\n" "[E0BCB2758EDF0AC6AB7DB36D98E1E57C]\n" "GoodName=Pikachu Genki Dechu (J) [!]\n" "CRC=3F245305 FC0B74AA\n" "Players=1\n" "SaveType=Eeprom 4KB\n" "Rumble=Yes\n" +"AiDmaModifier=88\n" "\n" "[D0AE6C07AC0481EBA5FF9CE798A69574]\n" "GoodName=Pikachu Genki Dechu (J) [b1]\n" @@ -12228,13 +12371,18 @@ char inifile[] = "SaveType=Flash RAM\n" "CountPerOp=1\n" "\n" -"[D0453459095F69BE36D675D8F743069B]\n" +"[8E1968191DD27655C03BE812CF041A95]\n" "GoodName=Pokemon Snap (I) [!]\n" "CRC=C0C85046 61051B05\n" "Players=1\n" "SaveType=Flash RAM\n" "CountPerOp=1\n" "\n" +"[D0453459095F69BE36D675D8F743069B]\n" +"GoodName=Pokemon Snap (I) [b1]\n" +"CRC=C0C85046 61051B05\n" +"RefMD5=8E1968191DD27655C03BE812CF041A95\n" +"\n" "[A45D7115BE5A06FD1567F9F913C3BDF8]\n" "GoodName=Pokemon Snap (S) [!]\n" "CRC=817D286A EF417416\n" @@ -12600,6 +12748,16 @@ char inifile[] = "Players=2\n" "Rumble=Yes\n" "\n" +"[B8267C225583A02ACC3B74B3178B87E9]\n" +"GoodName=Puyo Puyo Sun 64 (J) [T+Eng1.1_Zoinkity]\n" +"CRC=1C348AFE 5B96ECFA\n" +"RefMD5=FAAA2094B04DCA4C287AF9334D22529D\n" +"\n" +"[9803F144AB702A7C3E16092AA929AB45]\n" +"GoodName=Puyo Puyo Sun 64 (J) [T+Kor1.0_Zoinkity&UltimatePenguin]\n" +"CRC=7FCBF1D2 4546EB4\n" +"RefMD5=FAAA2094B04DCA4C287AF9334D22529D\n" +"\n" "[20C7ABF5A4DB8DC1C70725FD213E29A2]\n" "GoodName=Puyo Puyo Sun 64 (J) [b1]\n" "CRC=94807E6B 60CC62E4\n" @@ -13078,6 +13236,11 @@ char inifile[] = "Mempak=Yes\n" "Rumble=Yes\n" "\n" +"[73165CBE4E2421475C5FE9A227DCAFEC]\n" +"GoodName=Ready 2 Rumble Boxing (E) [T+Spa2.0_IlDucci]\n" +"CRC=56E06922 10F665F6\n" +"RefMD5=ADC95AE01855FA305B13F8B22427E597\n" +"\n" "[70F73D01FEAF768758F31B23DE2C2620]\n" "GoodName=Ready 2 Rumble Boxing (E) (M3) [t1] (P1 Untouchable)\n" "CRC=8638A334 8EBF1508\n" @@ -13231,47 +13394,47 @@ char inifile[] = "CRC=9FF69D4F 195F0059\n" "RefMD5=2ABCD1AD41B3356FBC1018ECB121283E\n" "\n" -"[5698757883A6F46FE5B4C9B6E780B480]\n" +"[C4A9BBF989DDFAF5F389E4ADC6195DBC]\n" "GoodName=Robotron 64 (U) [!]\n" "CRC=AC8E4B32 E7B47326\n" "Players=2\n" "SaveType=None\n" "Mempak=Yes\n" "\n" -"[C4A9BBF989DDFAF5F389E4ADC6195DBC]\n" +"[5698757883A6F46FE5B4C9B6E780B480]\n" "GoodName=Robotron 64 (U) [b1]\n" "CRC=AC8E4B32 E7B47326\n" -"RefMD5=5698757883A6F46FE5B4C9B6E780B480\n" +"RefMD5=C4A9BBF989DDFAF5F389E4ADC6195DBC\n" "\n" "[8555DCF12FD35206451FB415F3FD2EEF]\n" "GoodName=Robotron 64 (U) [b2]\n" "CRC=8F4C4B32 6D881FCA\n" -"RefMD5=5698757883A6F46FE5B4C9B6E780B480\n" +"RefMD5=C4A9BBF989DDFAF5F389E4ADC6195DBC\n" "\n" "[AB50026DAEA629F554408E5BA3EFA182]\n" "GoodName=Robotron 64 (U) [b3]\n" "CRC=8F4C4B32 6D881FCA\n" -"RefMD5=5698757883A6F46FE5B4C9B6E780B480\n" +"RefMD5=C4A9BBF989DDFAF5F389E4ADC6195DBC\n" "\n" "[1AA79B5DEFE1F5852D5ADD799383CCCD]\n" "GoodName=Robotron 64 (U) [b4]\n" "CRC=575CCC2E A8B5ACE0\n" -"RefMD5=5698757883A6F46FE5B4C9B6E780B480\n" +"RefMD5=C4A9BBF989DDFAF5F389E4ADC6195DBC\n" "\n" "[377B9C40EBE4F4CF845E577A8DC9ACCB]\n" "GoodName=Robotron 64 (U) [f1] (PAL)\n" "CRC=8F4C4B32 6D881FCA\n" -"RefMD5=5698757883A6F46FE5B4C9B6E780B480\n" +"RefMD5=C4A9BBF989DDFAF5F389E4ADC6195DBC\n" "\n" "[27E4C31540E18E3E8CEA97BFF744CC11]\n" "GoodName=Robotron 64 (U) [o1]\n" "CRC=AC8E4B32 E7B47326\n" -"RefMD5=5698757883A6F46FE5B4C9B6E780B480\n" +"RefMD5=C4A9BBF989DDFAF5F389E4ADC6195DBC\n" "\n" "[E151CCE259636C5AE15CA1C8B380EE76]\n" "GoodName=Robotron 64 (U) [t1]\n" "CRC=575CCC2E A8B5ACE0\n" -"RefMD5=5698757883A6F46FE5B4C9B6E780B480\n" +"RefMD5=C4A9BBF989DDFAF5F389E4ADC6195DBC\n" "\n" "[427C5457D4A29A222811F0CAA9CCA7B9]\n" "GoodName=Rocket - Robot on Wheels (E) (M3) [!]\n" @@ -13932,6 +14095,21 @@ char inifile[] = "CRC=FDFC3328 A0B2BA23\n" "RefMD5=5AAC6E652C5CF1E37A466AC0073E88CA\n" "\n" +"[D8AB932AFD1CED97ACD33FA984B309ED]\n" +"GoodName=SmashRemix1.1.0\n" +"CRC=FB816989 6F442541\n" +"RefMD5=5AAC6E652C5CF1E37A466AC0073E88CA\n" +"\n" +"[70A61772B2123879367011A5E2900569]\n" +"GoodName=SmashRemix1.1.1\n" +"CRC=FB816D35 3FC67AFF\n" +"RefMD5=5AAC6E652C5CF1E37A466AC0073E88CA\n" +"\n" +"[44DE1F9ED1FA35B72D31A1F809285B01]\n" +"GoodName=SmashRemix1.2.0\n" +"CRC=A1BFF9C9 8FB192A2\n" +"RefMD5=5AAC6E652C5CF1E37A466AC0073E88CA\n" +"\n" "[B8D4B92E66A312708626B3216DE07A3A]\n" "GoodName=Snobow Kids (J) [!]\n" "CRC=84FC04FF B1253CE9\n" @@ -14633,6 +14811,11 @@ char inifile[] = "CRC=4DD7ED54 74F9287D\n" "RefMD5=C7B40352AAD8D863D88D51672F9A0087\n" "\n" +"[4E12B97690897465A27BDF0DDA722826]\n" +"GoodName=Star Wars - Shadows of the Empire (U) (Unl)\n" +"CRC=1C5EE088 D32A963D\n" +"RefMD5=C7B40352AAD8D863D88D51672F9A0087\n" +"\n" "[8603B180E70B2A72EF77D46C2BEC2234]\n" "GoodName=Star Wars - Shutsugeki! Rogue Chuutai (J) [!]\n" "CRC=827E4890 958468DC\n" @@ -14689,6 +14872,11 @@ char inifile[] = "SaveType=Eeprom 16KB\n" "Rumble=Yes\n" "\n" +"[73113AE7AAF0911BA6A1DF1353980D14]\n" +"GoodName=Star Wars Episode I - Racer (E) [T+Spa1.0.2_IlDucci]\n" +"CRC=E857ED95 6028F9AF\n" +"RefMD5=6EF9FED309F28BD59B605F128869AA00\n" +"\n" "[8CC7E31925FBFA13A584F529E8912207]\n" "GoodName=Star Wars Episode I - Racer (E) (M3) [f1] (Save)\n" "CRC=166312CB C30220D2\n" @@ -14738,6 +14926,11 @@ char inifile[] = "CRC=B722133E 5EECEC4D\n" "RefMD5=1EE8800A4003E7F9688C5A35F929D01B\n" "\n" +"[0B2B4D3C38426763059D68AA53B86FDF]\n" +"GoodName=Star Wars Episode I - Racer (U) (Unl)\n" +"CRC=21CD632F 8BCA2AD4\n" +"RefMD5=1EE8800A4003E7F9688C5A35F929D01B\n" +"\n" "[3EB732A8D004263AD8EB0DA59A29582A]\n" "GoodName=StarCraft 64 (Beta)\n" "CRC=BC9B2CC3 4ED04DA5\n" @@ -14937,6 +15130,11 @@ char inifile[] = "Status=4\n" "SaveType=Eeprom 4KB\n" "\n" +"[6614298A943234FD30885BD0B1383CBB]\n" +"GoodName=Super Mario 64 (J) [T+Kor1.0_Minio]\n" +"CRC=B36CF0BE A35F5C7B\n" +"RefMD5=85D61F5525AF708C9F1E84DCE6DC10E9\n" +"\n" "[DBF7F4D881E1DF604C41367B04233E74]\n" "GoodName=Super Mario 64 (J) [h1C]\n" "CRC=4EAA3D0E 74757C24\n" @@ -14979,6 +15177,12 @@ char inifile[] = "CRC=635A42C5 BDC58EDC\n" "RefMD5=20B854B239203BAF6C961B850A4A51A2\n" "\n" +"[E2FCF7782B622495FABE12FC7244B35B]\n" +"GoodName=Super Mario 64 (U) [T+Spa3.9_Blade133bo]\n" +"CRC=88EC3675 59CE4583\n" +"RefMD5=20B854B239203BAF6C961B850A4A51A2\n" +"Rumble=Yes\n" +"\n" "[35BBAF4869D0B0D2147D35F1EC3CF2CA]\n" "GoodName=Super Mario 64 (U) [T-Ita1.0final_beta1_Rulesless]\n" "CRC=819D5B33 A5096295\n" @@ -14989,6 +15193,12 @@ char inifile[] = "CRC=490A59F2 22BC515E\n" "RefMD5=20B854B239203BAF6C961B850A4A51A2\n" "\n" +"[E5544B156833120E7B97A05AAB743F98]\n" +"GoodName=Super Mario 64 (U) [T+Ita4.2_Rulesless]\n" +"CRC=5EDE011C E166686\n" +"RefMD5=20B854B239203BAF6C961B850A4A51A2\n" +"Rumble=Yes\n" +"\n" "[933C474FC970A0E7D659F32334DB6475]\n" "GoodName=Super Mario 64 (U) [b1]\n" "CRC=635A2BFF 8B022326\n" @@ -15034,6 +15244,16 @@ char inifile[] = "CRC=635A42C5 BDC58EDC\n" "RefMD5=20B854B239203BAF6C961B850A4A51A2\n" "\n" +"[338FAA6E250F72F1B1D7B873BFAEE918]\n" +"GoodName=SM64 Splitscreen Multiplayer Beta\n" +"CRC=5C277FBA E8A351C9\n" +"RefMD5=20B854B239203BAF6C961B850A4A51A2\n" +"\n" +"[F0595A36EAB6109C5FF2420EB452B6E2]\n" +"GoodName=SM64 Splitscreen Multiplayer\n" +"CRC=13CD0830 3CCF65E9\n" +"RefMD5=20B854B239203BAF6C961B850A4A51A2\n" +"\n" "[2D727C3278AA232D94F2FB45AEC4D303]\n" "GoodName=Super Mario 64 - Shindou Edition (J) [!]\n" "CRC=D6FBA4A8 6326AA2C\n" @@ -15042,6 +15262,11 @@ char inifile[] = "SaveType=Eeprom 4KB\n" "Rumble=Yes\n" "\n" +"[A00D0D095592113A279A27ACD785CE50]\n" +"GoodName=Super Mario 64 - Shindou Edition (J) [T+Kor1.0_Minio]\n" +"CRC=C1373DC8 ED88D197\n" +"RefMD5=2D727C3278AA232D94F2FB45AEC4D303\n" +"\n" "[23033085561CD331CC81F0026FCB2CE2]\n" "GoodName=Super Mario 64 - Shindou Edition (J) [b1]\n" "CRC=D6FBA4A8 6326AA2C\n" @@ -16016,6 +16241,16 @@ char inifile[] = "CRC=B73AB6F6 296267DD\n" "RefMD5=A0657BC99E169153FD46AECCFDE748F3\n" "\n" +"[79BD05A97E50AC63A638E4E90AB9588B]\n" +"GoodName=Tsumi to Batsu - Hoshi no Keishousha (J) [T+Spa1.0b_TheFireRed&IlDucci]\n" +"CRC=B1C7794B D93DC5F3\n" +"RefMD5=A0657BC99E169153FD46AECCFDE748F3\n" +"\n" +"[103E16BF7FCFB4D713D2EBE19DCB5311]\n" +"GoodName=Tsumi to Batsu - Hoshi no Keishousha (J) [T+Spa1.0b_TheFireRed&IlDucci] (Fandub)\n" +"CRC=B1C7794B D93DC5F3\n" +"RefMD5=A0657BC99E169153FD46AECCFDE748F3\n" +"\n" "[13FAA58604597E4EDC608070F8E0AE24]\n" "GoodName=Turok - Dinosaur Hunter (E) (V1.0) [!]\n" "CRC=2F7009DD FC3BAC53\n" @@ -16068,6 +16303,11 @@ char inifile[] = "SaveType=None\n" "Mempak=Yes\n" "\n" +"[85F616E54160FFE14E4E817065EA61CE]\n" +"GoodName=Turok - Dinosaur Hunter (U) (V1.0) (Analog Movement Controls_1.01 - No Invert)\n" +"CRC=2F70F10D 5C4187FF\n" +"RefMD5=AE5107EFDD3C210E1EDD4ACD9B3CAC31\n" +"\n" "[AE5107EFDD3C210E1EDD4ACD9B3CAC31]\n" "GoodName=Turok - Dinosaur Hunter (U) (V1.0) [!]\n" "CRC=2F70F10D 5C4187FF\n" @@ -17166,6 +17406,16 @@ char inifile[] = "Mempak=Yes\n" "Rumble=Yes\n" "\n" +"[ECC1692D12FDCB0C3F605E44DD54CE8C]\n" +"GoodName=Wave Race 64 - Shindou Edition (J) (V1.2) [T+Eng1.0_Zoinkity]\n" +"CRC=57AF893E 884A377C\n" +"RefMD5=FF67DF97476C210D158779AE6142F239\n" +"\n" +"[D0472173524D6EC4E78CBCA30EFD98FC]\n" +"GoodName=Wave Race 64 - Shindou Edition (J) (V1.2) [T+Eng1.1_Zoinkity]\n" +"CRC=57AF893E 8EEF209A\n" +"RefMD5=FF67DF97476C210D158779AE6142F239\n" +"\n" "[AFBC694A3BA5AE83D3CEEF906BF01839]\n" "GoodName=Wave Race 64 - Shindou Edition (J) (V1.2) [b1]\n" "CRC=535DF3E2 609789F1\n" @@ -17616,6 +17866,14 @@ char inifile[] = "CRC=BEADC61C 8622D9F6\n" "RefMD5=1AC234649D28F09E82C0D11ABB17F03B\n" "\n" +"[BC4BCD45FA712ECDD8A85918A9CA5F4B]\n" +"GoodName=Xeno Crisis (A)\n" +"CRC=470112E9 53C66CB4\n" +"Players=2\n" +"SaveType=None\n" +"Mempak=Yes\n" +"Rumble=Yes\n" +"\n" "[9137129A586E1BCAB6AE81BAC6B01275]\n" "GoodName=Xplorer64 BIOS V1.067 (G)\n" "CRC=656C6561 73652031\n" @@ -18061,7 +18319,77 @@ char inifile[] = "GoodName=Zelda no Densetsu - Mujura no Kamen (J) (V1.1) [!]\n" "CRC=69AE0438 2C63F3F3\n" "RefMD5=15D1A2217CAD61C39CFECBFFA0703E25\n" -"\n"; +"\n" +"; 64DD Games\n" +";\n" +"[8D3D9F294B6E174BC7B1D2FD1C727530]\n" +"GoodName=64DD IPL (JPN)\n" +"\n" +"[37C36E4286D36892A9FC70EAFE4104BE]\n" +"GoodName=64DD IPL (USA)\n" +"\n" +"[8485643E5830CD67ED4C0A5FD49E2491]\n" +"GoodName=Mario Artist Paint Studio (J)\n" +"\n" +"[88228E990B58A94E9B3460BEFF632304]\n" +"GoodName=Mario Artist Talent Studio (J)\n" +"\n" +"[114AF722029D6386C3BFEA4CC8FA603C]\n" +"GoodName=Mario Artist Communication Kit (J)\n" +"\n" +"[DA23EE561578B7DAD77ED72728B46D30]\n" +"GoodName=Mario Artist Polygon Studio (J)\n" +"\n" +"[EBBA03F20096FC2BC178FC3A1F4EC2B6]\n" +"GoodName=Sim City 64 (J)\n" +"\n" +"[9F797A9C704B5EBD04D6A2B036309AF2]\n" +"GoodName=Nihon Pro Golf Tour 64 (J)\n" +"\n" +"[E8EB810D996E12CD6C47445F87A94C72]\n" +"GoodName=Kyojin no Doshin 1 (J)\n" +"\n" +"[AFE059AE1210751B6C849CFB482C99DD]\n" +"GoodName=Kyojin no Doshin 1 (J) (Store Demo)\n" +"\n" +"[26087E0750FC6DA0F3DE2A431E34BDDF]\n" +"GoodName=Randnet Disk (J) [Rev. 00]\n" +"\n" +"[33CBC59786063285DC3C090E0ED312E3]\n" +"GoodName=Randnet Disk (J) [Rev. 01]\n" +"\n" +"[FCCA9AF8C1174C40B478EA0E02673B16]\n" +"GoodName=Kyojin no Doshin - Kaihou Sensen Chibikkochikko Dai Shuugou (J)\n" +"\n" +"[CB2FB00C3921245AE04BB38BA01ABE92]\n" +"GoodName=F-ZERO X Expansion Kit (J)\n" +"\n" +"[D9EA905727B44F2774926CB03C2300A7]\n" +"GoodName=Super Mario 64 Disk Version (J) (Spaceworld 1996 Demo)\n" +"\n" +"[84706E10026BD2EE9654DA5E9821598D]\n" +"GoodName=Dezaemon 3D Expansion Disk (J) (Proto)\n" +"SaveType=SRAM\n" +"\n" +"[7BB3974754A54BA27A93F96691DC4695]\n" +"GoodName=Dezaemon 3D Expansion Disk (J) (Proto)\n" +"SaveType=SRAM\n" +"\n" +"[40AA8EC52C5E025D6C6AD94CEBC2218F]\n" +"GoodName=Dezaemon 3D Expansion Disk (J) (Proto)\n" +"SaveType=SRAM\n" +"\n" +"[C108A9B1EB024F0D2317C05976BA49F2]\n" +"GoodName=Dezaemon 3D Expansion Disk (J) (Proto) [a1]\n" +"SaveType=SRAM\n" +"\n" +"[4D02F5A719BD455F67721C64B16AE0CB]\n" +"GoodName=Mario Artist Paint Studio (J) (1999-02-11 Proto)\n" +"CountPerOp=1\n" +"\n" +"[A76B619EC832A7E2ABCFBDFEB5375E39]\n" +"GoodName=Mario Artist Paint Studio (J) (1999-02-11 Proto)\n" +"CountPerOp=1\n"; #endif From 81b7783fe22d406a1a8b57e5638ac4f226f3cd21 Mon Sep 17 00:00:00 2001 From: M4xw Date: Fri, 19 Jul 2024 12:53:58 +0200 Subject: [PATCH 13/58] Update precompiled headers --- .../r4300/new_dynarec/arm/asm_defines_gas.h | 35 +++++++++++++++++++ .../r4300/new_dynarec/arm/asm_defines_nasm.h | 35 +++++++++++++++++++ .../r4300/new_dynarec/arm64/asm_defines_gas.h | 35 +++++++++++++++++++ .../new_dynarec/arm64/asm_defines_nasm.h | 35 +++++++++++++++++++ .../r4300/new_dynarec/x64/asm_defines_gas.h | 35 +++++++++++++++++++ .../r4300/new_dynarec/x64/asm_defines_nasm.h | 35 +++++++++++++++++++ .../r4300/new_dynarec/x86/asm_defines_gas.h | 35 +++++++++++++++++++ .../r4300/new_dynarec/x86/asm_defines_nasm.h | 35 +++++++++++++++++++ 8 files changed, 280 insertions(+) create mode 100644 mupen64plus-core/src/device/r4300/new_dynarec/arm/asm_defines_gas.h create mode 100644 mupen64plus-core/src/device/r4300/new_dynarec/arm/asm_defines_nasm.h create mode 100644 mupen64plus-core/src/device/r4300/new_dynarec/arm64/asm_defines_gas.h create mode 100644 mupen64plus-core/src/device/r4300/new_dynarec/arm64/asm_defines_nasm.h create mode 100644 mupen64plus-core/src/device/r4300/new_dynarec/x64/asm_defines_gas.h create mode 100644 mupen64plus-core/src/device/r4300/new_dynarec/x64/asm_defines_nasm.h create mode 100644 mupen64plus-core/src/device/r4300/new_dynarec/x86/asm_defines_gas.h create mode 100644 mupen64plus-core/src/device/r4300/new_dynarec/x86/asm_defines_nasm.h diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/arm/asm_defines_gas.h b/mupen64plus-core/src/device/r4300/new_dynarec/arm/asm_defines_gas.h new file mode 100644 index 00000000..7cbf7dd0 --- /dev/null +++ b/mupen64plus-core/src/device/r4300/new_dynarec/arm/asm_defines_gas.h @@ -0,0 +1,35 @@ +#define offsetof_struct_device_r4300 (0x00000000) +#define offsetof_struct_r4300_core_cp0 (0x029014dc) +#define offsetof_struct_cp0_last_addr (0x00000194) +#define offsetof_struct_cp0_count_per_op (0x00000198) +#define offsetof_struct_cp0_tlb (0x000001a0) +#define offsetof_struct_tlb_entries (0x00000000) +#define offsetof_struct_tlb_LUT_r (0x00000680) +#define offsetof_struct_tlb_LUT_w (0x00400680) +#define offsetof_struct_r4300_core_cached_interp (0x00000098) +#define offsetof_struct_cached_interp_invalid_code (0x00000000) +#define offsetof_struct_r4300_core_new_dynarec_hot_state (0x02501000) +#define offsetof_struct_r4300_core_extra_memory (0x00501000) +#define offsetof_struct_new_dynarec_hot_state_dynarec_local (0x00000000) +#define offsetof_struct_new_dynarec_hot_state_cycle_count (0x00000040) +#define offsetof_struct_new_dynarec_hot_state_pending_exception (0x00000044) +#define offsetof_struct_new_dynarec_hot_state_pcaddr (0x00000048) +#define offsetof_struct_new_dynarec_hot_state_stop (0x0000004c) +#define offsetof_struct_new_dynarec_hot_state_invc_ptr (0x00000050) +#define offsetof_struct_new_dynarec_hot_state_cp1_fcr0 (0x0000006c) +#define offsetof_struct_new_dynarec_hot_state_cp1_fcr31 (0x00000070) +#define offsetof_struct_new_dynarec_hot_state_regs (0x00000078) +#define offsetof_struct_new_dynarec_hot_state_hi (0x00000178) +#define offsetof_struct_new_dynarec_hot_state_lo (0x00000180) +#define offsetof_struct_new_dynarec_hot_state_cp0_regs (0x00000188) +#define offsetof_struct_new_dynarec_hot_state_cp1_regs_simple (0x00000210) +#define offsetof_struct_new_dynarec_hot_state_cp1_regs_double (0x00000290) +#define offsetof_struct_new_dynarec_hot_state_rounding_modes (0x00000318) +#define offsetof_struct_new_dynarec_hot_state_branch_target (0x00000328) +#define offsetof_struct_new_dynarec_hot_state_pc (0x0000032c) +#define offsetof_struct_new_dynarec_hot_state_fake_pc (0x00000330) +#define offsetof_struct_new_dynarec_hot_state_rs (0x000003b8) +#define offsetof_struct_new_dynarec_hot_state_rt (0x000003c0) +#define offsetof_struct_new_dynarec_hot_state_rd (0x000003c8) +#define offsetof_struct_new_dynarec_hot_state_mini_ht (0x000003d4) +#define offsetof_struct_new_dynarec_hot_state_memory_map (0x000004d4) diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/arm/asm_defines_nasm.h b/mupen64plus-core/src/device/r4300/new_dynarec/arm/asm_defines_nasm.h new file mode 100644 index 00000000..7b8581fd --- /dev/null +++ b/mupen64plus-core/src/device/r4300/new_dynarec/arm/asm_defines_nasm.h @@ -0,0 +1,35 @@ +%define offsetof_struct_device_r4300 (0x00000000) +%define offsetof_struct_r4300_core_cp0 (0x029014dc) +%define offsetof_struct_cp0_last_addr (0x00000194) +%define offsetof_struct_cp0_count_per_op (0x00000198) +%define offsetof_struct_cp0_tlb (0x000001a0) +%define offsetof_struct_tlb_entries (0x00000000) +%define offsetof_struct_tlb_LUT_r (0x00000680) +%define offsetof_struct_tlb_LUT_w (0x00400680) +%define offsetof_struct_r4300_core_cached_interp (0x00000098) +%define offsetof_struct_cached_interp_invalid_code (0x00000000) +%define offsetof_struct_r4300_core_new_dynarec_hot_state (0x02501000) +%define offsetof_struct_r4300_core_extra_memory (0x00501000) +%define offsetof_struct_new_dynarec_hot_state_dynarec_local (0x00000000) +%define offsetof_struct_new_dynarec_hot_state_cycle_count (0x00000040) +%define offsetof_struct_new_dynarec_hot_state_pending_exception (0x00000044) +%define offsetof_struct_new_dynarec_hot_state_pcaddr (0x00000048) +%define offsetof_struct_new_dynarec_hot_state_stop (0x0000004c) +%define offsetof_struct_new_dynarec_hot_state_invc_ptr (0x00000050) +%define offsetof_struct_new_dynarec_hot_state_cp1_fcr0 (0x0000006c) +%define offsetof_struct_new_dynarec_hot_state_cp1_fcr31 (0x00000070) +%define offsetof_struct_new_dynarec_hot_state_regs (0x00000078) +%define offsetof_struct_new_dynarec_hot_state_hi (0x00000178) +%define offsetof_struct_new_dynarec_hot_state_lo (0x00000180) +%define offsetof_struct_new_dynarec_hot_state_cp0_regs (0x00000188) +%define offsetof_struct_new_dynarec_hot_state_cp1_regs_simple (0x00000210) +%define offsetof_struct_new_dynarec_hot_state_cp1_regs_double (0x00000290) +%define offsetof_struct_new_dynarec_hot_state_rounding_modes (0x00000318) +%define offsetof_struct_new_dynarec_hot_state_branch_target (0x00000328) +%define offsetof_struct_new_dynarec_hot_state_pc (0x0000032c) +%define offsetof_struct_new_dynarec_hot_state_fake_pc (0x00000330) +%define offsetof_struct_new_dynarec_hot_state_rs (0x000003b8) +%define offsetof_struct_new_dynarec_hot_state_rt (0x000003c0) +%define offsetof_struct_new_dynarec_hot_state_rd (0x000003c8) +%define offsetof_struct_new_dynarec_hot_state_mini_ht (0x000003d4) +%define offsetof_struct_new_dynarec_hot_state_memory_map (0x000004d4) diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/arm64/asm_defines_gas.h b/mupen64plus-core/src/device/r4300/new_dynarec/arm64/asm_defines_gas.h new file mode 100644 index 00000000..a827dc16 --- /dev/null +++ b/mupen64plus-core/src/device/r4300/new_dynarec/arm64/asm_defines_gas.h @@ -0,0 +1,35 @@ +#define offsetof_struct_device_r4300 (0x00000000) +#define offsetof_struct_r4300_core_cp0 (0x031017e8) +#define offsetof_struct_cp0_last_addr (0x000002a8) +#define offsetof_struct_cp0_count_per_op (0x000002ac) +#define offsetof_struct_cp0_tlb (0x000002b4) +#define offsetof_struct_tlb_entries (0x00000000) +#define offsetof_struct_tlb_LUT_r (0x00000680) +#define offsetof_struct_tlb_LUT_w (0x00400680) +#define offsetof_struct_r4300_core_cached_interp (0x000000e0) +#define offsetof_struct_cached_interp_invalid_code (0x00000000) +#define offsetof_struct_r4300_core_new_dynarec_hot_state (0x02901000) +#define offsetof_struct_r4300_core_extra_memory (0x00901000) +#define offsetof_struct_new_dynarec_hot_state_dynarec_local (0x00000000) +#define offsetof_struct_new_dynarec_hot_state_cycle_count (0x00000100) +#define offsetof_struct_new_dynarec_hot_state_pending_exception (0x00000104) +#define offsetof_struct_new_dynarec_hot_state_pcaddr (0x00000108) +#define offsetof_struct_new_dynarec_hot_state_stop (0x0000010c) +#define offsetof_struct_new_dynarec_hot_state_invc_ptr (0x00000110) +#define offsetof_struct_new_dynarec_hot_state_cp1_fcr0 (0x00000134) +#define offsetof_struct_new_dynarec_hot_state_cp1_fcr31 (0x00000138) +#define offsetof_struct_new_dynarec_hot_state_regs (0x00000140) +#define offsetof_struct_new_dynarec_hot_state_hi (0x00000240) +#define offsetof_struct_new_dynarec_hot_state_lo (0x00000248) +#define offsetof_struct_new_dynarec_hot_state_cp0_regs (0x00000250) +#define offsetof_struct_new_dynarec_hot_state_cp1_regs_simple (0x000002d8) +#define offsetof_struct_new_dynarec_hot_state_cp1_regs_double (0x000003d8) +#define offsetof_struct_new_dynarec_hot_state_rounding_modes (0x000004e0) +#define offsetof_struct_new_dynarec_hot_state_branch_target (0x000004f0) +#define offsetof_struct_new_dynarec_hot_state_pc (0x000004f8) +#define offsetof_struct_new_dynarec_hot_state_fake_pc (0x00000500) +#define offsetof_struct_new_dynarec_hot_state_rs (0x000005c0) +#define offsetof_struct_new_dynarec_hot_state_rt (0x000005c8) +#define offsetof_struct_new_dynarec_hot_state_rd (0x000005d0) +#define offsetof_struct_new_dynarec_hot_state_mini_ht (0x000005e0) +#define offsetof_struct_new_dynarec_hot_state_memory_map (0x000007e0) diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/arm64/asm_defines_nasm.h b/mupen64plus-core/src/device/r4300/new_dynarec/arm64/asm_defines_nasm.h new file mode 100644 index 00000000..4cb61e1e --- /dev/null +++ b/mupen64plus-core/src/device/r4300/new_dynarec/arm64/asm_defines_nasm.h @@ -0,0 +1,35 @@ +%define offsetof_struct_device_r4300 (0x00000000) +%define offsetof_struct_r4300_core_cp0 (0x031017e8) +%define offsetof_struct_cp0_last_addr (0x000002a8) +%define offsetof_struct_cp0_count_per_op (0x000002ac) +%define offsetof_struct_cp0_tlb (0x000002b4) +%define offsetof_struct_tlb_entries (0x00000000) +%define offsetof_struct_tlb_LUT_r (0x00000680) +%define offsetof_struct_tlb_LUT_w (0x00400680) +%define offsetof_struct_r4300_core_cached_interp (0x000000e0) +%define offsetof_struct_cached_interp_invalid_code (0x00000000) +%define offsetof_struct_r4300_core_new_dynarec_hot_state (0x02901000) +%define offsetof_struct_r4300_core_extra_memory (0x00901000) +%define offsetof_struct_new_dynarec_hot_state_dynarec_local (0x00000000) +%define offsetof_struct_new_dynarec_hot_state_cycle_count (0x00000100) +%define offsetof_struct_new_dynarec_hot_state_pending_exception (0x00000104) +%define offsetof_struct_new_dynarec_hot_state_pcaddr (0x00000108) +%define offsetof_struct_new_dynarec_hot_state_stop (0x0000010c) +%define offsetof_struct_new_dynarec_hot_state_invc_ptr (0x00000110) +%define offsetof_struct_new_dynarec_hot_state_cp1_fcr0 (0x00000134) +%define offsetof_struct_new_dynarec_hot_state_cp1_fcr31 (0x00000138) +%define offsetof_struct_new_dynarec_hot_state_regs (0x00000140) +%define offsetof_struct_new_dynarec_hot_state_hi (0x00000240) +%define offsetof_struct_new_dynarec_hot_state_lo (0x00000248) +%define offsetof_struct_new_dynarec_hot_state_cp0_regs (0x00000250) +%define offsetof_struct_new_dynarec_hot_state_cp1_regs_simple (0x000002d8) +%define offsetof_struct_new_dynarec_hot_state_cp1_regs_double (0x000003d8) +%define offsetof_struct_new_dynarec_hot_state_rounding_modes (0x000004e0) +%define offsetof_struct_new_dynarec_hot_state_branch_target (0x000004f0) +%define offsetof_struct_new_dynarec_hot_state_pc (0x000004f8) +%define offsetof_struct_new_dynarec_hot_state_fake_pc (0x00000500) +%define offsetof_struct_new_dynarec_hot_state_rs (0x000005c0) +%define offsetof_struct_new_dynarec_hot_state_rt (0x000005c8) +%define offsetof_struct_new_dynarec_hot_state_rd (0x000005d0) +%define offsetof_struct_new_dynarec_hot_state_mini_ht (0x000005e0) +%define offsetof_struct_new_dynarec_hot_state_memory_map (0x000007e0) diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/x64/asm_defines_gas.h b/mupen64plus-core/src/device/r4300/new_dynarec/x64/asm_defines_gas.h new file mode 100644 index 00000000..7f5a81fd --- /dev/null +++ b/mupen64plus-core/src/device/r4300/new_dynarec/x64/asm_defines_gas.h @@ -0,0 +1,35 @@ +#define offsetof_struct_device_r4300 (0x00000000) +#define offsetof_struct_r4300_core_cp0 (0x031017f8) +#define offsetof_struct_cp0_last_addr (0x000002a8) +#define offsetof_struct_cp0_count_per_op (0x000002ac) +#define offsetof_struct_cp0_tlb (0x000002b4) +#define offsetof_struct_tlb_entries (0x00000000) +#define offsetof_struct_tlb_LUT_r (0x00000680) +#define offsetof_struct_tlb_LUT_w (0x00400680) +#define offsetof_struct_r4300_core_cached_interp (0x000000f0) +#define offsetof_struct_cached_interp_invalid_code (0x00000000) +#define offsetof_struct_r4300_core_new_dynarec_hot_state (0x02901000) +#define offsetof_struct_r4300_core_extra_memory (0x00901000) +#define offsetof_struct_new_dynarec_hot_state_dynarec_local (0x00000000) +#define offsetof_struct_new_dynarec_hot_state_cycle_count (0x00000100) +#define offsetof_struct_new_dynarec_hot_state_pending_exception (0x00000104) +#define offsetof_struct_new_dynarec_hot_state_pcaddr (0x00000108) +#define offsetof_struct_new_dynarec_hot_state_stop (0x0000010c) +#define offsetof_struct_new_dynarec_hot_state_invc_ptr (0x00000110) +#define offsetof_struct_new_dynarec_hot_state_cp1_fcr0 (0x00000134) +#define offsetof_struct_new_dynarec_hot_state_cp1_fcr31 (0x00000138) +#define offsetof_struct_new_dynarec_hot_state_regs (0x00000140) +#define offsetof_struct_new_dynarec_hot_state_hi (0x00000240) +#define offsetof_struct_new_dynarec_hot_state_lo (0x00000248) +#define offsetof_struct_new_dynarec_hot_state_cp0_regs (0x00000250) +#define offsetof_struct_new_dynarec_hot_state_cp1_regs_simple (0x000002d8) +#define offsetof_struct_new_dynarec_hot_state_cp1_regs_double (0x000003d8) +#define offsetof_struct_new_dynarec_hot_state_rounding_modes (0x000004e0) +#define offsetof_struct_new_dynarec_hot_state_branch_target (0x000004f0) +#define offsetof_struct_new_dynarec_hot_state_pc (0x000004f8) +#define offsetof_struct_new_dynarec_hot_state_fake_pc (0x00000500) +#define offsetof_struct_new_dynarec_hot_state_rs (0x000005d0) +#define offsetof_struct_new_dynarec_hot_state_rt (0x000005d8) +#define offsetof_struct_new_dynarec_hot_state_rd (0x000005e0) +#define offsetof_struct_new_dynarec_hot_state_mini_ht (0x000005f0) +#define offsetof_struct_new_dynarec_hot_state_memory_map (0x000007f0) diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/x64/asm_defines_nasm.h b/mupen64plus-core/src/device/r4300/new_dynarec/x64/asm_defines_nasm.h new file mode 100644 index 00000000..a6c0525a --- /dev/null +++ b/mupen64plus-core/src/device/r4300/new_dynarec/x64/asm_defines_nasm.h @@ -0,0 +1,35 @@ +%define offsetof_struct_device_r4300 (0x00000000) +%define offsetof_struct_r4300_core_cp0 (0x031017f8) +%define offsetof_struct_cp0_last_addr (0x000002a8) +%define offsetof_struct_cp0_count_per_op (0x000002ac) +%define offsetof_struct_cp0_tlb (0x000002b4) +%define offsetof_struct_tlb_entries (0x00000000) +%define offsetof_struct_tlb_LUT_r (0x00000680) +%define offsetof_struct_tlb_LUT_w (0x00400680) +%define offsetof_struct_r4300_core_cached_interp (0x000000f0) +%define offsetof_struct_cached_interp_invalid_code (0x00000000) +%define offsetof_struct_r4300_core_new_dynarec_hot_state (0x02901000) +%define offsetof_struct_r4300_core_extra_memory (0x00901000) +%define offsetof_struct_new_dynarec_hot_state_dynarec_local (0x00000000) +%define offsetof_struct_new_dynarec_hot_state_cycle_count (0x00000100) +%define offsetof_struct_new_dynarec_hot_state_pending_exception (0x00000104) +%define offsetof_struct_new_dynarec_hot_state_pcaddr (0x00000108) +%define offsetof_struct_new_dynarec_hot_state_stop (0x0000010c) +%define offsetof_struct_new_dynarec_hot_state_invc_ptr (0x00000110) +%define offsetof_struct_new_dynarec_hot_state_cp1_fcr0 (0x00000134) +%define offsetof_struct_new_dynarec_hot_state_cp1_fcr31 (0x00000138) +%define offsetof_struct_new_dynarec_hot_state_regs (0x00000140) +%define offsetof_struct_new_dynarec_hot_state_hi (0x00000240) +%define offsetof_struct_new_dynarec_hot_state_lo (0x00000248) +%define offsetof_struct_new_dynarec_hot_state_cp0_regs (0x00000250) +%define offsetof_struct_new_dynarec_hot_state_cp1_regs_simple (0x000002d8) +%define offsetof_struct_new_dynarec_hot_state_cp1_regs_double (0x000003d8) +%define offsetof_struct_new_dynarec_hot_state_rounding_modes (0x000004e0) +%define offsetof_struct_new_dynarec_hot_state_branch_target (0x000004f0) +%define offsetof_struct_new_dynarec_hot_state_pc (0x000004f8) +%define offsetof_struct_new_dynarec_hot_state_fake_pc (0x00000500) +%define offsetof_struct_new_dynarec_hot_state_rs (0x000005d0) +%define offsetof_struct_new_dynarec_hot_state_rt (0x000005d8) +%define offsetof_struct_new_dynarec_hot_state_rd (0x000005e0) +%define offsetof_struct_new_dynarec_hot_state_mini_ht (0x000005f0) +%define offsetof_struct_new_dynarec_hot_state_memory_map (0x000007f0) diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/x86/asm_defines_gas.h b/mupen64plus-core/src/device/r4300/new_dynarec/x86/asm_defines_gas.h new file mode 100644 index 00000000..54378d5f --- /dev/null +++ b/mupen64plus-core/src/device/r4300/new_dynarec/x86/asm_defines_gas.h @@ -0,0 +1,35 @@ +#define offsetof_struct_device_r4300 (0x00000000) +#define offsetof_struct_r4300_core_cp0 (0x029014d0) +#define offsetof_struct_cp0_last_addr (0x00000194) +#define offsetof_struct_cp0_count_per_op (0x00000198) +#define offsetof_struct_cp0_tlb (0x000001a0) +#define offsetof_struct_tlb_entries (0x00000000) +#define offsetof_struct_tlb_LUT_r (0x00000680) +#define offsetof_struct_tlb_LUT_w (0x00400680) +#define offsetof_struct_r4300_core_cached_interp (0x00000098) +#define offsetof_struct_cached_interp_invalid_code (0x00000000) +#define offsetof_struct_r4300_core_new_dynarec_hot_state (0x02501000) +#define offsetof_struct_r4300_core_extra_memory (0x00501000) +#define offsetof_struct_new_dynarec_hot_state_dynarec_local (0x00000000) +#define offsetof_struct_new_dynarec_hot_state_cycle_count (0x00000040) +#define offsetof_struct_new_dynarec_hot_state_pending_exception (0x00000044) +#define offsetof_struct_new_dynarec_hot_state_pcaddr (0x00000048) +#define offsetof_struct_new_dynarec_hot_state_stop (0x0000004c) +#define offsetof_struct_new_dynarec_hot_state_invc_ptr (0x00000050) +#define offsetof_struct_new_dynarec_hot_state_cp1_fcr0 (0x0000006c) +#define offsetof_struct_new_dynarec_hot_state_cp1_fcr31 (0x00000070) +#define offsetof_struct_new_dynarec_hot_state_regs (0x00000074) +#define offsetof_struct_new_dynarec_hot_state_hi (0x00000174) +#define offsetof_struct_new_dynarec_hot_state_lo (0x0000017c) +#define offsetof_struct_new_dynarec_hot_state_cp0_regs (0x00000184) +#define offsetof_struct_new_dynarec_hot_state_cp1_regs_simple (0x0000020c) +#define offsetof_struct_new_dynarec_hot_state_cp1_regs_double (0x0000028c) +#define offsetof_struct_new_dynarec_hot_state_rounding_modes (0x00000314) +#define offsetof_struct_new_dynarec_hot_state_branch_target (0x00000324) +#define offsetof_struct_new_dynarec_hot_state_pc (0x00000328) +#define offsetof_struct_new_dynarec_hot_state_fake_pc (0x0000032c) +#define offsetof_struct_new_dynarec_hot_state_rs (0x000003b0) +#define offsetof_struct_new_dynarec_hot_state_rt (0x000003b8) +#define offsetof_struct_new_dynarec_hot_state_rd (0x000003c0) +#define offsetof_struct_new_dynarec_hot_state_mini_ht (0x000003cc) +#define offsetof_struct_new_dynarec_hot_state_memory_map (0x000004cc) diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/x86/asm_defines_nasm.h b/mupen64plus-core/src/device/r4300/new_dynarec/x86/asm_defines_nasm.h new file mode 100644 index 00000000..208aa2f8 --- /dev/null +++ b/mupen64plus-core/src/device/r4300/new_dynarec/x86/asm_defines_nasm.h @@ -0,0 +1,35 @@ +%define offsetof_struct_device_r4300 (0x00000000) +%define offsetof_struct_r4300_core_cp0 (0x029014d0) +%define offsetof_struct_cp0_last_addr (0x00000194) +%define offsetof_struct_cp0_count_per_op (0x00000198) +%define offsetof_struct_cp0_tlb (0x000001a0) +%define offsetof_struct_tlb_entries (0x00000000) +%define offsetof_struct_tlb_LUT_r (0x00000680) +%define offsetof_struct_tlb_LUT_w (0x00400680) +%define offsetof_struct_r4300_core_cached_interp (0x00000098) +%define offsetof_struct_cached_interp_invalid_code (0x00000000) +%define offsetof_struct_r4300_core_new_dynarec_hot_state (0x02501000) +%define offsetof_struct_r4300_core_extra_memory (0x00501000) +%define offsetof_struct_new_dynarec_hot_state_dynarec_local (0x00000000) +%define offsetof_struct_new_dynarec_hot_state_cycle_count (0x00000040) +%define offsetof_struct_new_dynarec_hot_state_pending_exception (0x00000044) +%define offsetof_struct_new_dynarec_hot_state_pcaddr (0x00000048) +%define offsetof_struct_new_dynarec_hot_state_stop (0x0000004c) +%define offsetof_struct_new_dynarec_hot_state_invc_ptr (0x00000050) +%define offsetof_struct_new_dynarec_hot_state_cp1_fcr0 (0x0000006c) +%define offsetof_struct_new_dynarec_hot_state_cp1_fcr31 (0x00000070) +%define offsetof_struct_new_dynarec_hot_state_regs (0x00000074) +%define offsetof_struct_new_dynarec_hot_state_hi (0x00000174) +%define offsetof_struct_new_dynarec_hot_state_lo (0x0000017c) +%define offsetof_struct_new_dynarec_hot_state_cp0_regs (0x00000184) +%define offsetof_struct_new_dynarec_hot_state_cp1_regs_simple (0x0000020c) +%define offsetof_struct_new_dynarec_hot_state_cp1_regs_double (0x0000028c) +%define offsetof_struct_new_dynarec_hot_state_rounding_modes (0x00000314) +%define offsetof_struct_new_dynarec_hot_state_branch_target (0x00000324) +%define offsetof_struct_new_dynarec_hot_state_pc (0x00000328) +%define offsetof_struct_new_dynarec_hot_state_fake_pc (0x0000032c) +%define offsetof_struct_new_dynarec_hot_state_rs (0x000003b0) +%define offsetof_struct_new_dynarec_hot_state_rt (0x000003b8) +%define offsetof_struct_new_dynarec_hot_state_rd (0x000003c0) +%define offsetof_struct_new_dynarec_hot_state_mini_ht (0x000003cc) +%define offsetof_struct_new_dynarec_hot_state_memory_map (0x000004cc) From ddbdbb5056f36029bf1c165d97c027d318b996d9 Mon Sep 17 00:00:00 2001 From: M4xw Date: Fri, 19 Jul 2024 12:57:48 +0200 Subject: [PATCH 14/58] Fix branch after rebase --- mupen64plus-core/.gitrepo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mupen64plus-core/.gitrepo b/mupen64plus-core/.gitrepo index 485d903b..21b528d2 100644 --- a/mupen64plus-core/.gitrepo +++ b/mupen64plus-core/.gitrepo @@ -5,7 +5,7 @@ ; [subrepo] remote = https://github.com/libretro/mupen64plus-core.git - branch = rebase + branch = master commit = efb46b8a1cc7f7160d7bf7a0f1e4bf2ec3727fa3 parent = 9d940bacb95c4d86733f42b67b57fc83046a6d39 method = rebase From f36d0510815cd4e73e98dc8862a2d216e183355c Mon Sep 17 00:00:00 2001 From: M4xw Date: Fri, 19 Jul 2024 12:58:20 +0200 Subject: [PATCH 15/58] git subrepo push mupen64plus-core subrepo: subdir: "mupen64plus-core" merged: "a362165c0" upstream: origin: "https://github.com/libretro/mupen64plus-core.git" branch: "master" commit: "a362165c0" git-subrepo: version: "0.4.6" origin: "???" commit: "???" --- mupen64plus-core/.gitrepo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mupen64plus-core/.gitrepo b/mupen64plus-core/.gitrepo index 21b528d2..8901cdf7 100644 --- a/mupen64plus-core/.gitrepo +++ b/mupen64plus-core/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/libretro/mupen64plus-core.git branch = master - commit = efb46b8a1cc7f7160d7bf7a0f1e4bf2ec3727fa3 - parent = 9d940bacb95c4d86733f42b67b57fc83046a6d39 + commit = a362165c0af96cce7780e3d277dd85a18a3eae8e + parent = ddbdbb5056f36029bf1c165d97c027d318b996d9 method = rebase cmdver = 0.4.6 From c7cd8edcd015ddcbd4a2e984573c9c1d1ddd0b6e Mon Sep 17 00:00:00 2001 From: M4xw Date: Fri, 19 Jul 2024 15:32:38 +0200 Subject: [PATCH 16/58] Bump ver --- libretro/libretro.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libretro/libretro.c b/libretro/libretro.c index 33e31269..1938b75a 100644 --- a/libretro/libretro.c +++ b/libretro/libretro.c @@ -648,7 +648,7 @@ void retro_set_environment(retro_environment_t cb) void retro_get_system_info(struct retro_system_info *info) { info->library_name = "Mupen64Plus-Next"; - info->library_version = "2.6" FLAVOUR_VERSION GIT_VERSION; + info->library_version = "2.7" FLAVOUR_VERSION GIT_VERSION; info->valid_extensions = "n64|v64|z64|bin|u1"; info->need_fullpath = false; info->block_extract = false; From 2b23e132684d1c22be8604502d2e7b653d12ec9b Mon Sep 17 00:00:00 2001 From: Eric Warmenhoven Date: Tue, 13 Aug 2024 10:26:00 -0400 Subject: [PATCH 17/58] iOS/tvOS: move build flags inside makefile --- .gitlab-ci.yml | 14 -------------- Makefile | 4 ++++ 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c26c3a71..6d61150a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -149,26 +149,12 @@ libretro-build-libnx-aarch64: # iOS libretro-build-ios-arm64-gles3: - variables: - WITH_DYNAREC: "" - EGL: 0 - GLES3: 1 - HAVE_NEON: 0 - HAVE_THR_AL: 1 - LLE: 1 extends: - .libretro-ios-arm64-make-default - .core-defs # tvOS libretro-build-tvos-arm64-gles3: - variables: - WITH_DYNAREC: "" - EGL: 0 - GLES3: 1 - HAVE_NEON: 0 - HAVE_THR_AL: 1 - LLE: 1 extends: - .libretro-tvos-arm64-make-default - .core-defs diff --git a/Makefile b/Makefile index 9aa30195..ada59773 100644 --- a/Makefile +++ b/Makefile @@ -437,6 +437,8 @@ else ifneq (,$(findstring ios,$(platform))) ifeq ($(platform),ios-arm64) HAVE_PARALLEL_RSP = 1 HAVE_PARALLEL_RDP = 1 + HAVE_THR_AL = 1 + LLE = 1 WITH_DYNAREC= GLES=1 GLES3=1 @@ -489,6 +491,8 @@ else ifneq (,$(findstring tvos,$(platform))) EGL := 0 HAVE_PARALLEL_RSP = 1 HAVE_PARALLEL_RDP = 1 + HAVE_THR_AL = 1 + LLE = 1 PLATCFLAGS += -DHAVE_POSIX_MEMALIGN -DIOS -DOS_IOS PLATCFLAGS += -Ofast -ffast-math -funsafe-math-optimizations -DNO_ASM COREFLAGS += -Ofast -ffast-math -funsafe-math-optimizations -DNO_ASM From 89ab3b43250ec7725dc4668d8dcbe29de02158ef Mon Sep 17 00:00:00 2001 From: Eric Warmenhoven Date: Tue, 13 Aug 2024 10:39:37 -0400 Subject: [PATCH 18/58] osx: also move these flags into the makefile instead of gitlab-ci --- .gitlab-ci.yml | 6 ------ Makefile | 6 +++--- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6d61150a..a32f833c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -162,9 +162,6 @@ libretro-build-tvos-arm64-gles3: # MacOS 64-bit libretro-build-osx-x64: variables: - WITH_DYNAREC: "" - HAVE_THR_AL: 1 - LLE: 1 extends: - .libretro-osx-x64-make-default - .core-defs @@ -172,9 +169,6 @@ libretro-build-osx-x64: # MacOS ARM 64-bit libretro-build-osx-arm64: variables: - WITH_DYNAREC: "" - HAVE_THR_AL: 1 - LLE: 1 extends: - .libretro-osx-arm64-make-default - .core-defs diff --git a/Makefile b/Makefile index ada59773..a75c1431 100644 --- a/Makefile +++ b/Makefile @@ -408,12 +408,12 @@ else ifneq (,$(findstring osx,$(platform))) GL_LIB := -framework OpenGL # Target Dynarec - ifeq ($(ARCH), $(filter $(ARCH), ppc)) - WITH_DYNAREC = - endif + WITH_DYNAREC = HAVE_PARALLEL_RSP = 1 HAVE_PARALLEL_RDP = 1 + HAVE_THR_AL = 1 + LLE = 1 COREFLAGS += -DOS_LINUX ASFLAGS = -f elf -d ELF_TYPE From af797557b0e6d339d047b43f73d0ade021da1637 Mon Sep 17 00:00:00 2001 From: Eric Warmenhoven Date: Tue, 13 Aug 2024 11:26:54 -0400 Subject: [PATCH 19/58] fix leftover line --- .gitlab-ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a32f833c..b19f66f5 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -161,14 +161,12 @@ libretro-build-tvos-arm64-gles3: # MacOS 64-bit libretro-build-osx-x64: - variables: extends: - .libretro-osx-x64-make-default - .core-defs # MacOS ARM 64-bit libretro-build-osx-arm64: - variables: extends: - .libretro-osx-arm64-make-default - .core-defs From 544ef0f955a735ef0c784b098301119a98358916 Mon Sep 17 00:00:00 2001 From: Eric Warmenhoven Date: Thu, 15 Aug 2024 14:03:25 -0400 Subject: [PATCH 20/58] Additional neon optimization flag --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a75c1431..481e8b92 100644 --- a/Makefile +++ b/Makefile @@ -615,7 +615,7 @@ endif include Makefile.common ifeq ($(HAVE_NEON), 1) - COREFLAGS += -DHAVE_NEON -D__ARM_NEON__ -D__NEON_OPT -ftree-vectorize -funsafe-math-optimizations -fno-finite-math-only + COREFLAGS += -DHAVE_NEON -D__ARM_NEON__ -D__NEON_OPT -ftree-vectorize -funsafe-math-optimizations -fno-finite-math-only -DUSE_SSE2NEON ifeq (,$(filter $(platform),ios-arm64 tvos-arm64)) COREFLAGS += -mvectorize-with-neon-quad -ftree-vectorizer-verbose=2 endif From c2f6acfe3b7b07ab86c3e4cd89f61a9911191793 Mon Sep 17 00:00:00 2001 From: M4xw Date: Wed, 21 Aug 2024 15:47:25 +0200 Subject: [PATCH 21/58] Stub ConfigGetParamBool use --- mupen64plus-core/src/main/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mupen64plus-core/src/main/main.c b/mupen64plus-core/src/main/main.c index a03494d3..3ef61a27 100644 --- a/mupen64plus-core/src/main/main.c +++ b/mupen64plus-core/src/main/main.c @@ -1414,7 +1414,7 @@ m64p_error main_run(void) uint64_t mpk_seed = !netplay_is_init() ? (uint64_t)time(NULL) : 0; l_mpk_idgen = xoshiro256pp_seed(mpk_seed); - no_compiled_jump = ConfigGetParamBool(g_CoreConfig, "NoCompiledJump"); + no_compiled_jump = 0; //ConfigGetParamBool(g_CoreConfig, "NoCompiledJump"); //We disable any randomness for netplay //randomize_interrupt = !netplay_is_init() ? ConfigGetParamBool(g_CoreConfig, "RandomizeInterrupt") : 0; From 4ee3d259b844fb3865459fdf5f2c9bee2ad9f322 Mon Sep 17 00:00:00 2001 From: M4xw Date: Tue, 15 Oct 2024 14:25:52 +0200 Subject: [PATCH 22/58] git subrepo clone --force ../GLideN64 GLideN64/ subrepo: subdir: "GLideN64" merged: "ff0793c9c" upstream: origin: "../GLideN64" branch: "develop" commit: "ff0793c9c" git-subrepo: version: "0.4.6" origin: "???" commit: "???" --- GLideN64/.github/workflows/build.yml | 30 +- GLideN64/.gitrepo | 4 +- GLideN64/ini/GLideN64.custom.ini | 1 + GLideN64/projects/msvc/GLideN64.vcxproj | 6 +- .../projects/msvc/GLideN64.vcxproj.filters | 18 +- .../src/BufferCopy/ColorBufferToRDRAM.cpp | 5 +- GLideN64/src/CMakeLists.txt | 8 +- GLideN64/src/Config.cpp | 9 + GLideN64/src/Config.h | 7 +- GLideN64/src/DisplayWindow.cpp | 11 +- GLideN64/src/FrameBuffer.cpp | 4 + GLideN64/src/GBI.cpp | 72 ++++- GLideN64/src/GBI.h | 17 +- GLideN64/src/GLideNHQ/TxFilter.cpp | 16 +- GLideN64/src/GLideNHQ/TxFilter.h | 4 +- GLideN64/src/GLideNHQ/TxFilterExport.cpp | 20 +- GLideN64/src/GLideNHQ/TxFilterExport.h | 8 +- GLideN64/src/GLideNHQ/TxUtil.cpp | 147 +++++++-- GLideN64/src/GLideNHQ/TxUtil.h | 10 +- GLideN64/src/GLideNUI-wtl/Language.cpp | 5 +- GLideN64/src/GLideNUI-wtl/config-video.cpp | 6 +- GLideN64/src/GLideNUI/ConfigDialog.cpp | 25 +- GLideN64/src/GLideNUI/GLideNUI.cpp | 14 +- GLideN64/src/GLideNUI/Settings.cpp | 3 + GLideN64/src/GLideNUI/configDialog.ui | 19 +- .../src/Graphics/FramebufferTextureFormats.h | 9 +- .../glsl_CombinerProgramBuilderCommon.cpp | 61 ++-- .../glsl_CombinerProgramUniformFactory.cpp | 4 +- .../GLSL/glsl_CombinerProgramUniformFactory.h | 4 +- ..._CombinerProgramUniformFactoryAccurate.cpp | 1 - ...sl_CombinerProgramUniformFactoryCommon.cpp | 29 +- ...glsl_CombinerProgramUniformFactoryCommon.h | 4 +- .../mupen64plus/mupen64plus_DisplayWindow.cpp | 13 +- ...opengl_BufferManipulationObjectFactory.cpp | 24 +- .../OpenGLContext/opengl_Parameters.cpp | 9 +- .../OpenGLContext/windows/WindowsWGL.cpp | 3 +- .../windows/windows_DisplayWindow.cpp | 4 +- GLideN64/src/Graphics/Parameters.h | 1 - GLideN64/src/GraphicsDrawer.cpp | 86 ++--- GLideN64/src/GraphicsDrawer.h | 4 +- GLideN64/src/Log.cpp | 50 +++ GLideN64/src/MupenPlusPluginAPI.cpp | 2 +- GLideN64/src/Neon/3DMathNeon.cpp | 4 +- GLideN64/src/Neon/gSPNeon.cpp | 4 +- GLideN64/src/NoiseTexture.cpp | 148 --------- GLideN64/src/NoiseTexture.h | 30 -- GLideN64/src/PluginAPI.h | 2 +- GLideN64/src/TextDrawer.cpp | 8 +- GLideN64/src/TextureFilterHandler.cpp | 2 + GLideN64/src/Textures.cpp | 57 +++- GLideN64/src/Textures.h | 2 +- GLideN64/src/TxFilterStub.cpp | 12 + GLideN64/src/VI.cpp | 9 + GLideN64/src/ZilmarGFX_1_3.h | 8 + GLideN64/src/ZilmarPluginAPI.cpp | 5 + GLideN64/src/gDP.cpp | 2 +- GLideN64/src/gDP.h | 1 + GLideN64/src/gSP.cpp | 170 ++++++---- GLideN64/src/gSP.h | 5 +- GLideN64/src/mupenplus/Config_mupenplus.cpp | 11 +- GLideN64/src/mupenplus/GLideN64_mupenplus.h | 1 + GLideN64/src/mupenplus/MupenPlusAPIImpl.cpp | 2 +- GLideN64/src/uCodes/F3DDKR.cpp | 3 +- GLideN64/src/uCodes/F3DEX095.cpp | 12 + GLideN64/src/uCodes/F3DEX095.h | 7 + GLideN64/src/uCodes/F3DEX3.cpp | 294 ++++++++++++++++++ GLideN64/src/uCodes/F3DEX3.h | 6 + GLideN64/src/uCodes/L3D.cpp | 34 +- GLideN64/src/uCodes/L3D.h | 1 + GLideN64/src/uCodes/L3DEX.cpp | 53 +++- GLideN64/src/uCodes/L3DEX.h | 4 + GLideN64/src/uCodes/L3DEX2.cpp | 52 +++- GLideN64/src/uCodes/L3DEX2.h | 5 +- GLideN64/src/windows/ScreenShot.cpp | 3 +- 74 files changed, 1212 insertions(+), 522 deletions(-) delete mode 100644 GLideN64/src/NoiseTexture.cpp delete mode 100644 GLideN64/src/NoiseTexture.h create mode 100644 GLideN64/src/uCodes/F3DEX095.cpp create mode 100644 GLideN64/src/uCodes/F3DEX095.h create mode 100644 GLideN64/src/uCodes/F3DEX3.cpp create mode 100644 GLideN64/src/uCodes/F3DEX3.h diff --git a/GLideN64/.github/workflows/build.yml b/GLideN64/.github/workflows/build.yml index 6e2d2efa..18cc0f74 100644 --- a/GLideN64/.github/workflows/build.yml +++ b/GLideN64/.github/workflows/build.yml @@ -9,7 +9,7 @@ jobs: matrix: image: ["GLideN64 (x64 Mupen64Plus-CLI)", "GLideN64 (x64 Mupen64Plus-Qt)"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Packages run: | # fix for "error processing package grub-efi-amd64-signed" @@ -43,7 +43,7 @@ jobs: cp translations/release/*.qm build/linux-mupen64plus-qt/ - name: Upload GLideN64 (x64 Mupen64Plus-CLI) if: ${{ matrix.image == 'GLideN64 (x64 Mupen64Plus-CLI)' }} - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: GLideN64-${{ env.GIT_REVISION }}-Linux-Mupen64Plus-CLI-x64 path: | @@ -51,7 +51,7 @@ jobs: build/linux-mupen64plus-cli/GLideN64.custom.ini - name: Upload GLideN64 (x64 Mupen64Plus-Qt) if: ${{ matrix.image == 'GLideN64 (x64 Mupen64Plus-Qt)' }} - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: GLideN64-${{ env.GIT_REVISION }}-Linux-Mupen64Plus-Qt-x64 path: | @@ -70,8 +70,8 @@ jobs: QT_BUILD_x86: qt-5_15-x86-msvc2017-static QT_BUILD_x64: qt-5_15-x64-msvc2017-static steps: - - uses: actions/checkout@v3 - - uses: microsoft/setup-msbuild@v1.1 + - uses: actions/checkout@v4 + - uses: microsoft/setup-msbuild@v2 - uses: msys2/setup-msys2@v2 with: update: true @@ -162,7 +162,7 @@ jobs: cp translations/release/*.qm build/windows-mupen64plus-qt/ shell: msys2 {0} - name: Upload GLideN64 (x64 Project64-Qt) - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: ${{ matrix.image == 'GLideN64 (x64 Project64-Qt)' }} with: name: GLideN64-${{ env.GIT_REVISION }}-Windows-Project64-Qt-x64 @@ -171,7 +171,7 @@ jobs: build\windows-project64-qt-x64\GLideN64.custom.ini build\windows-project64-qt-x64\*.qm - name: Upload GLideN64 (x86 Project64-Qt) - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: ${{ matrix.image == 'GLideN64 (x86 Project64-Qt)' }} with: name: GLideN64-${{ env.GIT_REVISION }}-Windows-Project64-Qt-x86 @@ -180,7 +180,7 @@ jobs: build\windows-project64-qt\GLideN64.custom.ini build\windows-project64-qt\*.qm - name: Upload GLideN64 (x64 Project64-WTL) - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: ${{ matrix.image == 'GLideN64 (x64 Project64-WTL)' }} with: name: GLideN64-${{ env.GIT_REVISION }}-Windows-Project64-WTL-x64 @@ -189,7 +189,7 @@ jobs: build\windows-project64-wtl-x64\GLideN64.custom.ini build\windows-project64-wtl-x64\translations\*.Lang - name: Upload GLideN64 (x86 Project64-WTL) - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: ${{ matrix.image == 'GLideN64 (x86 Project64-WTL)' }} with: name: GLideN64-${{ env.GIT_REVISION }}-Windows-Project64-WTL-x86 @@ -198,7 +198,7 @@ jobs: build\windows-project64-wtl\GLideN64.custom.ini build\windows-project64-wtl\translations\*.Lang - name: Upload GLideN64 (x64 Mupen64Plus-CLI) - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: ${{ matrix.image == 'GLideN64 (x64 Mupen64Plus-CLI)' }} with: name: GLideN64-${{ env.GIT_REVISION }}-Windows-Mupen64Plus-CLI-x64 @@ -206,7 +206,7 @@ jobs: build\windows-mupen64plus-cli-x64\*.dll build\windows-mupen64plus-cli-x64\GLideN64.custom.ini - name: Upload GLideN64 (x86 Mupen64Plus-CLI) - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: ${{ matrix.image == 'GLideN64 (x86 Mupen64Plus-CLI)' }} with: name: GLideN64-${{ env.GIT_REVISION }}-Windows-Mupen64Plus-CLI-x86 @@ -214,7 +214,7 @@ jobs: build\windows-mupen64plus-cli\*.dll build\windows-mupen64plus-cli\GLideN64.custom.ini - name: Upload GLideN64 (x64 Mupen64Plus-Qt) - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: ${{ matrix.image == 'GLideN64 (x64 Mupen64Plus-Qt)' }} with: name: GLideN64-${{ env.GIT_REVISION }}-Windows-Mupen64Plus-Qt-x64 @@ -227,9 +227,9 @@ jobs: needs: [Windows, Linux] if: github.ref == 'refs/heads/master' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Download Artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: path: artifacts # sadly we can't download the artifacts without extracting it @@ -238,7 +238,7 @@ jobs: run: | cd artifacts for artifact in * - do + do echo "-> Creating ${artifact}.zip" pushd "$artifact" zip -r "../${artifact}.zip" * diff --git a/GLideN64/.gitrepo b/GLideN64/.gitrepo index d6976a67..69bb6f54 100644 --- a/GLideN64/.gitrepo +++ b/GLideN64/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = git@github.com:libretro/GLideN64.git branch = develop - commit = bce50d1953cd70218496bc0359d6152e0fa2e195 - parent = 7838a7824892afc9bf0598788370e0bb27589876 + commit = ff0793c9c4bec49a1ae40e727c69f9d2a33e61ed + parent = c2f6acfe3b7b07ab86c3e4cd89f61a9911191793 method = rebase cmdver = 0.4.6 diff --git a/GLideN64/ini/GLideN64.custom.ini b/GLideN64/ini/GLideN64.custom.ini index 2c903728..f5e5cc9b 100644 --- a/GLideN64/ini/GLideN64.custom.ini +++ b/GLideN64/ini/GLideN64.custom.ini @@ -181,6 +181,7 @@ frameBufferEmulation\copyDepthToRDRAM=0 Good_Name=Mario Kart 64 (E)(J)(U) graphics2D\enableNativeResTexrects=1 graphics2D\enableTexCoordBounds=1 +frameBufferEmulation\copyToRDRAM=2 [MARIO%20STORY] Good_Name=Mario Story (J) diff --git a/GLideN64/projects/msvc/GLideN64.vcxproj b/GLideN64/projects/msvc/GLideN64.vcxproj index 0be3fd30..73d37083 100644 --- a/GLideN64/projects/msvc/GLideN64.vcxproj +++ b/GLideN64/projects/msvc/GLideN64.vcxproj @@ -369,7 +369,6 @@ copy /Y "$(OutDir)$(TargetName).*" "$(Mupen64PluginsDir_x64)") true - @@ -392,9 +391,11 @@ copy /Y "$(OutDir)$(TargetName).*" "$(Mupen64PluginsDir_x64)") + + @@ -520,7 +521,6 @@ copy /Y "$(OutDir)$(TargetName).*" "$(Mupen64PluginsDir_x64)") true - @@ -541,9 +541,11 @@ copy /Y "$(OutDir)$(TargetName).*" "$(Mupen64PluginsDir_x64)") + + diff --git a/GLideN64/projects/msvc/GLideN64.vcxproj.filters b/GLideN64/projects/msvc/GLideN64.vcxproj.filters index aec78dbd..46a4fe58 100644 --- a/GLideN64/projects/msvc/GLideN64.vcxproj.filters +++ b/GLideN64/projects/msvc/GLideN64.vcxproj.filters @@ -245,9 +245,6 @@ Source Files\Graphics\OpenGL\GLSL - - Source Files - Source Files @@ -443,6 +440,12 @@ Source Files\Graphics\OpenGL\GLSL + + Source Files\uCodes + + + Source Files\uCodes + @@ -610,9 +613,6 @@ Header Files\Graphics\OpenGL\GLSL - - Header Files - Header Files\Graphics @@ -703,9 +703,15 @@ Header Files\uCodes + + Header Files\uCodes + Header Files\uCodes + + Header Files\uCodes + Header Files\uCodes diff --git a/GLideN64/src/BufferCopy/ColorBufferToRDRAM.cpp b/GLideN64/src/BufferCopy/ColorBufferToRDRAM.cpp index e79785e3..66c8f62e 100644 --- a/GLideN64/src/BufferCopy/ColorBufferToRDRAM.cpp +++ b/GLideN64/src/BufferCopy/ColorBufferToRDRAM.cpp @@ -214,9 +214,10 @@ void ColorBufferToRDRAM::_copy(u32 _startAddress, u32 _endAddress, bool _sync) u32 *ptr_src = (u32*)pPixels; u16 *ptr_dst = (u16*)(RDRAM + _startAddress); m_blueNoiseIdx++; - if ((config.generalEmulation.hacks & hack_subscreen) != 0u && height == 1u) + if (gDP.m_subscreen) { copyWhiteToRDRAM(m_pCurFrameBuffer); - else + gDP.m_subscreen = false; + } else writeToRdram(ptr_src, ptr_dst, &ColorBufferToRDRAM::_RGBAtoRGBA16, dummyTester, 1, width, height, numPixels, _startAddress, m_pCurFrameBuffer->m_startAddress, m_pCurFrameBuffer->m_size); } else if (m_pCurFrameBuffer->m_size == G_IM_SIZ_8b) { u8 *ptr_src = (u8*)pPixels; diff --git a/GLideN64/src/CMakeLists.txt b/GLideN64/src/CMakeLists.txt index fca4e3a3..a49783cc 100644 --- a/GLideN64/src/CMakeLists.txt +++ b/GLideN64/src/CMakeLists.txt @@ -59,7 +59,6 @@ set(GLideN64_SOURCES gSP.cpp Log.cpp N64.cpp - NoiseTexture.cpp PaletteTexture.cpp Performance.cpp PostProcessor.cpp @@ -121,8 +120,10 @@ set(GLideN64_SOURCES uCodes/F3DBETA.cpp uCodes/F3DDKR.cpp uCodes/F3DEX.cpp + uCodes/F3DEX095.cpp uCodes/F3DAM.cpp uCodes/F3DEX2.cpp + uCodes/F3DEX3.cpp uCodes/F3DEX2ACCLAIM.cpp uCodes/F3DEX2CBFD.cpp uCodes/F3DZEX2.cpp @@ -192,9 +193,10 @@ if( NOT CMAKE_BUILD_TYPE) set( CMAKE_BUILD_TYPE Release) endif( NOT CMAKE_BUILD_TYPE) -if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "MinSizeRel") +if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "MinSizeRel" OR + CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") set(GLIDEN64_BUILD_TYPE Release) -elseif(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") +elseif(CMAKE_BUILD_TYPE STREQUAL "Debug") set(GLIDEN64_BUILD_TYPE Debug) endif() diff --git a/GLideN64/src/Config.cpp b/GLideN64/src/Config.cpp index 91871514..819743d1 100644 --- a/GLideN64/src/Config.cpp +++ b/GLideN64/src/Config.cpp @@ -69,7 +69,11 @@ void Config::resetToDefaults() frameBufferEmulation.copyDepthToRDRAM = cdSoftwareRender; frameBufferEmulation.copyFromRDRAM = 0; frameBufferEmulation.copyAuxToRDRAM = 0; +#ifdef M64P_GLIDENUI + frameBufferEmulation.copyToRDRAM = ctSync; +#else frameBufferEmulation.copyToRDRAM = ctDoubleBuffer; +#endif frameBufferEmulation.N64DepthCompare = dcDisable; frameBufferEmulation.forceDepthBufferClear = 0; frameBufferEmulation.aspect = a43; @@ -99,6 +103,7 @@ void Config::resetToDefaults() textureFilter.txCacheCompression = 1; textureFilter.txSaveCache = 1; textureFilter.txDump = 0; + textureFilter.txStrongCRC = 0; textureFilter.txEnhancedTextureFileStorage = 0; textureFilter.txHiresTextureFileStorage = 0; @@ -203,6 +208,8 @@ const char* Config::hotkeyIniName(u32 _idx) return "hkForceGammaCorrection"; case Config::HotKey::hkInaccurateTexCords: return "hkInaccurateTexCords"; + case Config::HotKey::hkStrongCRC: + return "hkStrongCRC"; } return nullptr; } @@ -241,6 +248,8 @@ const char* Config::enabledHotkeyIniName(u32 _idx) return "hkForceGammaCorrectionEnabled"; case Config::HotKey::hkInaccurateTexCords: return "hkInaccurateTexCordsEnabled"; + case Config::HotKey::hkStrongCRC: + return "hkStrongCRCEnabled"; } return nullptr; } diff --git a/GLideN64/src/Config.h b/GLideN64/src/Config.h index 934ef68b..81508d73 100644 --- a/GLideN64/src/Config.h +++ b/GLideN64/src/Config.h @@ -99,8 +99,9 @@ struct Config aStretch = 0, a43 = 1, a169 = 2, - aAdjust = 3, - aTotal = 4 + aAdjust43 = 3, + aAdjust169 = 4, + aTotal = 5 }; enum CopyToRDRAM { @@ -176,6 +177,7 @@ struct Config u32 txCacheCompression; // Zip textures cache u32 txSaveCache; // Save texture cache to hard disk u32 txDump; // Dump textures + u32 txStrongCRC; // Dump textures with alternative (strong) CRC u32 txEnhancedTextureFileStorage; // Use file storage instead of memory cache for enhanced textures. u32 txHiresTextureFileStorage; // Use file storage instead of memory cache for hires textures. @@ -238,6 +240,7 @@ struct Config hkOsdRenderingResolution, hkForceGammaCorrection, hkInaccurateTexCords, + hkStrongCRC, hkTotal }; diff --git a/GLideN64/src/DisplayWindow.cpp b/GLideN64/src/DisplayWindow.cpp index 8c6ad8a3..59725f62 100644 --- a/GLideN64/src/DisplayWindow.cpp +++ b/GLideN64/src/DisplayWindow.cpp @@ -174,7 +174,7 @@ void DisplayWindow::_setBufferSize() m_height = m_screenHeight; } break; - case Config::aAdjust: // adjust + case Config::aAdjust43: // adjust m_width = m_screenWidth; m_height = m_screenHeight; if (m_screenWidth * 3 / 4 > m_screenHeight) { @@ -183,6 +183,15 @@ void DisplayWindow::_setBufferSize() m_bAdjustScreen = true; } break; + case Config::aAdjust169: // adjust + m_width = m_screenWidth; + m_height = m_screenHeight; + if (m_screenWidth * 9 / 16 > m_screenHeight) { + f32 width169 = m_screenHeight * 16.0f / 9.0f; + m_adjustScale = width169 / m_screenWidth; + m_bAdjustScreen = true; + } + break; default: assert(false && "Unknown aspect ratio"); m_width = m_screenWidth; diff --git a/GLideN64/src/FrameBuffer.cpp b/GLideN64/src/FrameBuffer.cpp index b5c2fa63..68cf2bf4 100644 --- a/GLideN64/src/FrameBuffer.cpp +++ b/GLideN64/src/FrameBuffer.cpp @@ -918,6 +918,10 @@ void FrameBufferList::saveBuffer(u32 _address, u16 _format, u16 _size, u16 _widt wnd.getDrawer().clearDepthBuffer(); } + if ((config.generalEmulation.hacks & hack_subscreen) != 0u && + _format == G_IM_FMT_I && _size == G_IM_SIZ_8b) + gDP.m_subscreen = gDP.otherMode._u64 == 0x00000cf00f0a0004; + m_pCurrent->m_isDepthBuffer = _address == gDP.depthImageAddress; m_pCurrent->m_isPauseScreen = m_pCurrent->m_isOBScreen = false; m_pCurrent->m_copied = false; diff --git a/GLideN64/src/GBI.cpp b/GLideN64/src/GBI.cpp index a6fb2381..3313627f 100644 --- a/GLideN64/src/GBI.cpp +++ b/GLideN64/src/GBI.cpp @@ -12,7 +12,9 @@ #include "RSP.h" #include "uCodes/F3D.h" #include "uCodes/F3DEX.h" +#include "uCodes/F3DEX095.h" #include "uCodes/F3DEX2.h" +#include "uCodes/F3DEX3.h" #include "uCodes/L3D.h" #include "uCodes/L3DEX.h" #include "uCodes/L3DEX2.h" @@ -39,6 +41,9 @@ #include "Graphics/Context.h" #include "Graphics/Parameters.h" +#include +#include + u32 last_good_ucode = (u32) -1; struct SpecialMicrocodeInfo @@ -54,7 +59,7 @@ static const std::vector specialMicrocodes = { { S2DEX2, false, true, false, 0x02c399dd }, // Animal Forest - { F3DEX, false, false, true, 0x0ace4c3f }, // Mario Kart 64 + { F3DEX095, false, false, true, 0x0ace4c3f }, // Mario Kart 64 { F3D, true, false, false, 0x16c3a775 }, // AeroFighters { F3DEX2CBFD, true, true, false, 0x1b4ace88 }, // Conker's Bad Fur Day { F3DPD, true, true, false, 0x1c4f7869 }, // Perfect Dark @@ -114,6 +119,7 @@ u32 G_OBJ_LOADTXTR, G_OBJ_LDTX_SPRITE, G_OBJ_LDTX_RECT, G_OBJ_LDTX_RECT_R; u32 G_RDPHALF_0; u32 G_PERSPNORM; u32 G_ZOBJ, G_ZRDPCMD, G_ZWAITSIGNAL, G_ZMTXCAT, G_ZMULT_MPMTX, G_ZLIGHTING; +u32 G_TRISTRIP, G_TRIFAN, G_LIGHTTORDP, G_RELSEGMENT; u32 G_MTX_STACKSIZE; @@ -146,7 +152,7 @@ GBIInfo GBI; void GBI_Unknown( u32 w0, u32 w1 ) { - DebugMsg(DEBUG_NORMAL, "UNKNOWN GBI COMMAND 0x%02X", _SHIFTR(w0, 24, 8)); + DebugMsg(DEBUG_NORMAL, "UNKNOWN GBI COMMAND 0x%02X\n", _SHIFTR(w0, 24, 8)); LOG(LOG_ERROR, "UNKNOWN GBI COMMAND 0x%02X", _SHIFTR(w0, 24, 8)); } @@ -204,6 +210,11 @@ void GBIInfo::_makeCurrent(MicrocodeInfo * _pCurrent) F3D_Init(); m_hwlSupported = true; break; + case F3DEX095: + F3DEX095_Init(); + m_hwlSupported = true; + gSP.clipRatio = m_pCurrent->Rej ? 2U : 1U; + break; case F3DEX: F3DEX_Init(); m_hwlSupported = true; @@ -290,6 +301,10 @@ void GBIInfo::_makeCurrent(MicrocodeInfo * _pCurrent) m_hwlSupported = false; gSP.clipRatio = 2U; break; + case F3DEX3: + F3DEX3_Init(); + m_hwlSupported = false; + break; case F3DTEXA: F3DTEXA_Init(); m_hwlSupported = true; @@ -352,6 +367,18 @@ bool GBIInfo::_makeExistingMicrocodeCurrent(u32 uc_start, u32 uc_dstart, u32 uc_ return true; } +// based on musl libc +static inline int ascii_isupper(int c) +{ + return (unsigned)c - 'A' < 26; +} + +static inline int ascii_tolower(int c) +{ + if (isupper(c)) return c | 32; + return c; +} + void GBIInfo::loadMicrocode(u32 uc_start, u32 uc_dstart, u16 uc_dsize) { if (_makeExistingMicrocodeCurrent(uc_start, uc_dstart, uc_dsize)) @@ -387,6 +414,47 @@ void GBIInfo::loadMicrocode(u32 uc_start, u32 uc_dstart, u16 uc_dsize) char uc_str[256]; strcpy(uc_str, "Not Found"); + // Check for F3DEX3 microcode + { + static const char F3DEX3_NAME[] = "f3dex3"; + const char* probe = &uc_data[0x138]; + char name_buffer[sizeof(F3DEX3_NAME) - 1]; + memcpy(name_buffer, probe, sizeof(F3DEX3_NAME) - 1); + std::transform(name_buffer, name_buffer + sizeof(F3DEX3_NAME) - 1, name_buffer, ascii_tolower); + if (0 == memcmp(name_buffer, F3DEX3_NAME, sizeof(F3DEX3_NAME) - 1)) + { + current.type = F3DEX3; + current.NoN = true; + current.negativeY = false; + current.fast3DPersp = false; + current.combineMatrices = false; + + std::set features; + { + // 0x180 is absolutely an overkill but it is ok for now + const char* name_end = (const char*)memchr(probe, ' ', 0x180); + size_t name_len = name_end - probe; + // It will look like F3DEX3_LVP_BrZ_NOC + std::string feature; + std::string name = std::string(probe, name_len); + std::transform(name.begin(), name.end(), name.begin(), ascii_tolower); + std::stringstream name_stream(name); + while (std::getline(name_stream, feature, '_')) + { + features.emplace(std::move(feature)); + } + } + + current.f3dex3.legacyVertexPipeline = features.find("lvp") != features.end(); + current.f3dex3.noOcclusionPlane = features.find("noc") != features.end(); + current.f3dex3.branchOnZ = features.find("brz") != features.end(); + + LOG(LOG_VERBOSE, "Load microcode (%s) type: %d crc: 0x%08x romname: %s\n", uc_str, current.type, uc_crc, RSP.romname); + _makeCurrent(¤t); + return; + } + } + for (u32 i = 0; i < 2046; ++i) { if ((uc_data[i] == 'R') && (uc_data[i+1] == 'S') && (uc_data[i+2] == 'P')) { u32 j = 0; diff --git a/GLideN64/src/GBI.h b/GLideN64/src/GBI.h index ee77ab1f..3f009e46 100644 --- a/GLideN64/src/GBI.h +++ b/GLideN64/src/GBI.h @@ -35,7 +35,9 @@ #define F5Indi_Naboo 26 #define S2DEX_1_03 27 #define S2DEX_1_05 28 -#define NONE 29 +#define F3DEX3 29 +#define F3DEX095 30 +#define NONE 31 // Fixed point conversion factors #define FIXED2FLOATRECIP1 0.5f @@ -401,6 +403,7 @@ extern u32 G_OBJ_LOADTXTR, G_OBJ_LDTX_SPRITE, G_OBJ_LDTX_RECT, G_OBJ_LDTX_RECT_R extern u32 G_RDPHALF_0; extern u32 G_PERSPNORM; extern u32 G_ZOBJ, G_ZRDPCMD, G_ZWAITSIGNAL, G_ZMTXCAT, G_ZMULT_MPMTX, G_ZLIGHTING; +extern u32 G_TRISTRIP, G_TRIFAN, G_LIGHTTORDP, G_RELSEGMENT; #define LIGHT_1 1 #define LIGHT_2 2 @@ -468,7 +471,7 @@ typedef struct struct Light { - u8 pad0, b, g, r; + u8 type, b, g, r; u8 pad1, b2, g2, r2; s8 pad2, z, y, x; }; @@ -489,6 +492,13 @@ struct MicrocodeInfo bool fast3DPersp = false; bool texturePersp = true; bool combineMatrices = false; + struct + { + // LVP is how microcodes other than F3DEX3 function + bool legacyVertexPipeline = true; + bool noOcclusionPlane = false; + bool branchOnZ = false; + } f3dex3; }; struct GBIInfo @@ -509,6 +519,9 @@ struct GBIInfo bool isNegativeY() const { return m_pCurrent != nullptr ? m_pCurrent->negativeY : true; } bool isTexturePersp() const { return m_pCurrent != nullptr ? m_pCurrent->texturePersp: true; } bool isCombineMatrices() const { return m_pCurrent != nullptr ? m_pCurrent->combineMatrices: false; } + bool isLegacyVertexPipeline() const { return m_pCurrent != nullptr ? m_pCurrent->f3dex3.legacyVertexPipeline : true; } + bool isNoOcclusionPlane() const { return m_pCurrent != nullptr ? m_pCurrent->f3dex3.noOcclusionPlane : false; } + bool isBranchOnZ() const { return m_pCurrent != nullptr ? m_pCurrent->f3dex3.branchOnZ : false; } private: void _flushCommands(); diff --git a/GLideN64/src/GLideNHQ/TxFilter.cpp b/GLideN64/src/GLideNHQ/TxFilter.cpp index d3c3ae1f..de70b174 100644 --- a/GLideN64/src/GLideNHQ/TxFilter.cpp +++ b/GLideN64/src/GLideNHQ/TxFilter.cpp @@ -149,7 +149,7 @@ TxFilter::TxFilter(int maxwidth, wchar_t fullTexPackPath[MAX_PATH]; wcscpy(fullTexPackPath, texPackPath); wcscat(fullTexPackPath, OSAL_DIR_SEPARATOR_STR); - wcscat(fullTexPackPath, ident); + wcscat(fullTexPackPath, _ident.c_str()); _txHiResLoader = new TxHiResNoCache(_maxwidth, _maxheight, _maxbpp, _options, texCachePath, texPackPath, fullTexPackPath, _ident.c_str(), callback); } else { _txHiResLoader = new TxHiResCache(_maxwidth, _maxheight, _maxbpp, _options, texCachePath, texPackPath, _ident.c_str(), callback); @@ -580,8 +580,18 @@ TxFilter::checksum64(uint8 *src, int width, int height, int size, int rowStride, return 0; } +uint64 +TxFilter::checksum64strong(uint8 *src, int width, int height, int size, int rowStride, uint8 *palette) +{ + if (_options & (HIRESTEXTURES_MASK | DUMP_TEX)) + return TxUtil::checksum64strong(src, width, height, size, rowStride, palette); + + return 0; +} + boolean -TxFilter::dmptx(uint8 *src, int width, int height, int rowStridePixel, ColorFormat gfmt, N64FormatSize n64FmtSz, Checksum r_crc64) +TxFilter::dmptx(uint8 *src, int width, int height, int rowStridePixel, + ColorFormat gfmt, N64FormatSize n64FmtSz, Checksum r_crc64, boolean isStrongCrc) { assert(gfmt != graphics::colorFormat::RGBA); if (!_initialized) @@ -609,7 +619,7 @@ TxFilter::dmptx(uint8 *src, int width, int height, int rowStridePixel, ColorForm tmpbuf.assign(_dumpPath); tmpbuf.append(wst("/")); tmpbuf.append(_ident); - tmpbuf.append(wst("/GLideNHQ")); + isStrongCrc ? tmpbuf.append(wst("/GLideNHQ_strong_crc")) : tmpbuf.append(wst("/GLideNHQ")); if (!osal_path_existsW(tmpbuf.c_str()) && osal_mkdirp(tmpbuf.c_str()) != 0) return 0; diff --git a/GLideN64/src/GLideNHQ/TxFilter.h b/GLideN64/src/GLideNHQ/TxFilter.h index 2c4af8e4..75d0aa4b 100644 --- a/GLideN64/src/GLideNHQ/TxFilter.h +++ b/GLideN64/src/GLideNHQ/TxFilter.h @@ -78,7 +78,9 @@ class TxFilter N64FormatSize n64FmtSz, GHQTexInfo *info); uint64 checksum64(uint8 *src, int width, int height, int size, int rowStride, uint8 *palette); - boolean dmptx(uint8 *src, int width, int height, int rowStridePixel, ColorFormat gfmt, N64FormatSize n64FmtSz, Checksum r_crc64); + uint64 checksum64strong(uint8 *src, int width, int height, int size, int rowStride, uint8 *palette); + boolean dmptx(uint8 *src, int width, int height, int rowStridePixel, + ColorFormat gfmt, N64FormatSize n64FmtSz, Checksum r_crc64, boolean isStrongCrc); boolean reloadhirestex(); void dumpcache(); }; diff --git a/GLideN64/src/GLideNHQ/TxFilterExport.cpp b/GLideN64/src/GLideNHQ/TxFilterExport.cpp index 335276b0..dc892e85 100644 --- a/GLideN64/src/GLideNHQ/TxFilterExport.cpp +++ b/GLideN64/src/GLideNHQ/TxFilterExport.cpp @@ -84,15 +84,33 @@ txfilter_checksum(uint8 *src, int width, int height, int size, int rowStride, ui return 0; } +TAPI uint64 TAPIENTRY +txfilter_checksum_strong(uint8 *src, int width, int height, int size, int rowStride, uint8 *palette) +{ + if (txFilter) + return txFilter->checksum64strong(src, width, height, size, rowStride, palette); + + return 0; +} + TAPI boolean TAPIENTRY txfilter_dmptx(uint8 *src, int width, int height, int rowStridePixel, uint16 gfmt, N64FormatSize n64FmtSz, Checksum r_crc64) { if (txFilter) - return txFilter->dmptx(src, width, height, rowStridePixel, ColorFormat(u32(gfmt)), n64FmtSz, r_crc64); + return txFilter->dmptx(src, width, height, rowStridePixel, ColorFormat(u32(gfmt)), n64FmtSz, r_crc64, FALSE); return 0; } +TAPI boolean TAPIENTRY +txfilter_dmptx_strong(uint8 *src, int width, int height, int rowStridePixel, uint16 gfmt, N64FormatSize n64FmtSz, Checksum r_crc64) +{ + if (txFilter) + return txFilter->dmptx(src, width, height, rowStridePixel, ColorFormat(u32(gfmt)), n64FmtSz, r_crc64, TRUE); + + return 0; +} + TAPI boolean TAPIENTRY txfilter_reloadhirestex() { diff --git a/GLideN64/src/GLideNHQ/TxFilterExport.h b/GLideN64/src/GLideNHQ/TxFilterExport.h index eaf655a8..9633d04e 100644 --- a/GLideN64/src/GLideNHQ/TxFilterExport.h +++ b/GLideN64/src/GLideNHQ/TxFilterExport.h @@ -92,7 +92,7 @@ typedef unsigned char boolean; #define GZ_HIRESTEXCACHE 0x00800000 #define DUMP_TEXCACHE 0x01000000 #define DUMP_HIRESTEXCACHE 0x02000000 -#define UNDEFINED_0 0x04000000 +#define DUMP_STRONG_CRC 0x04000000 #define UNDEFINED_1 0x08000000 #define FORCE16BPP_HIRESTEX 0x10000000 #define FORCE16BPP_TEX 0x20000000 @@ -225,9 +225,15 @@ txfilter_hirestex(uint64 g64crc, Checksum r_crc64, uint16 *palette, N64FormatSiz TAPI uint64 TAPIENTRY txfilter_checksum(uint8 *src, int width, int height, int size, int rowStride, uint8 *palette); +TAPI uint64 TAPIENTRY +txfilter_checksum_strong(uint8 *src, int width, int height, int size, int rowStride, uint8 *palette); + TAPI boolean TAPIENTRY txfilter_dmptx(uint8 *src, int width, int height, int rowStridePixel, uint16 gfmt, N64FormatSize n64FmtSz, Checksum r_crc64); +TAPI boolean TAPIENTRY +txfilter_dmptx_strong(uint8 *src, int width, int height, int rowStridePixel, uint16 gfmt, N64FormatSize n64FmtSz, Checksum r_crc64); + TAPI boolean TAPIENTRY txfilter_reloadhirestex(); diff --git a/GLideN64/src/GLideNHQ/TxUtil.cpp b/GLideN64/src/GLideNHQ/TxUtil.cpp index 36f66417..4e8cfb17 100644 --- a/GLideN64/src/GLideNHQ/TxUtil.cpp +++ b/GLideN64/src/GLideNHQ/TxUtil.cpp @@ -38,21 +38,22 @@ #include #endif +#define XXH_INLINE_ALL +#include "xxHash/xxhash.h" + /* * Utilities ******************************************************************************/ +static uint32 Uint64ToUint32(uint64_t t) +{ + return static_cast((t & 0xFFFFFFFF) ^ (t >> 32)); +} + uint32 TxUtil::checksumTx(uint8 *src, int width, int height, ColorFormat format) { int dataSize = sizeofTx(width, height, format); - - /* for now we use adler32 if something else is better - * we can simply swtich later - */ - /* return (dataSize ? Adler32(src, dataSize, 1) : 0); */ - - /* zlib crc32 */ - return (dataSize ? crc32(crc32(0L, Z_NULL, 0), src, dataSize) : 0); + return Uint64ToUint32(XXH3_64bits(src, dataSize)); } int @@ -77,18 +78,6 @@ TxUtil::sizeofTx(int width, int height, ColorFormat format) return dataSize; } -uint32 -TxUtil::checksum(uint8 *src, int width, int height, int size, int rowStride) -{ - /* Rice CRC32 for now. We can switch this to Jabo MD5 or - * any other custom checksum. - * TODO: use *_HIRESTEXTURE option. */ - - if (!src) return 0; - - return RiceCRC32(src, width, height, size, rowStride); -} - uint64 TxUtil::checksum64(uint8 *src, int width, int height, int size, int rowStride, uint8 *palette) { @@ -126,6 +115,43 @@ TxUtil::checksum64(uint8 *src, int width, int height, int size, int rowStride, u return crc64Ret; } +uint64 +TxUtil::checksum64strong(uint8 *src, int width, int height, int size, int rowStride, uint8 *palette) +{ + /* XXH3_64bits for strong 32bit texture hash. */ + /* Returned value is 64bits: hi=palette crc32 low=texture crc32 */ + + if (!src) + return 0; + + uint64 crc64Ret = 0; + + if (palette) { + uint32 crc32 = 0, cimax = 0; + switch (size & 0xff) { + case 1: + if (StrongCRC32_CI8(src, width, height, rowStride, &crc32, &cimax)) { + crc64Ret = StrongCRC32(palette, cimax + 1, 1, 2, 512); + crc64Ret <<= 32; + crc64Ret |= crc32; + } + break; + case 0: + if (StrongCRC32_CI4(src, width, height, rowStride, &crc32, &cimax)) { + crc64Ret = StrongCRC32(palette, cimax + 1, 1, 2, 32); + crc64Ret <<= 32; + crc64Ret |= crc32; + } + } + } + + if (!crc64Ret) { + crc64Ret = StrongCRC32(src, width, height, size, rowStride); + } + + return crc64Ret; +} + /* Rice CRC32 for hires texture packs */ /* NOTE: The following is used in Glide64 to calculate the CRC32 * for Rice hires texture packs. @@ -186,24 +212,22 @@ TxUtil::RiceCRC32(const uint8* src, int width, int height, int size, int rowStri } #else int y = height - 1; - while (y >= 0) - { + do { uint32 esi = 0; int x = bytesPerLine - 4; - while (x >= 0) - { + do { esi = *(uint32*)(src + x); esi ^= x; crc32Ret = (crc32Ret << 4) + ((crc32Ret >> 28) & 15); crc32Ret += esi; x -= 4; - } + } while (x >= 0); esi ^= y; crc32Ret += esi; src += rowStride; --y; - } + } while (y >= 0); #endif } catch(...) { DBG_INFO(80, wst("Error: RiceCRC32 exception!\n")); @@ -486,6 +510,77 @@ TxUtil::RiceCRC32_CI8(const uint8* src, int width, int height, int rowStride, return 1; } +uint32 +TxUtil::StrongCRC32(const uint8* src, int width, int height, int size, int rowStride) +{ + /* NOTE: bytesPerLine must be equal or larger than 4 */ + const uint32 bytesPerLine = width << size >> 1; + + u64 crc = UINT64_MAX; + std::vector buf(static_cast(height) * std::max(bytesPerLine, static_cast(rowStride))); + uint8* pData = buf.data(); + try { + for (int y = 0; y < height; ++y) { + if (bytesPerLine < 4) { + // bytesPerLine must be >= 4, but if it less than 4, reproduce RiceCRC behavior, + // that is read bytes before provided source address. + memcpy(pData, src - 4 + bytesPerLine, 4); + pData += 4; + } + else { + memcpy(pData, src, bytesPerLine); + pData += bytesPerLine; + } + src += rowStride; + } + crc = XXH3_64bits(buf.data(), static_cast(pData - buf.data())); + } + catch (...) { + DBG_INFO(80, wst("Error: StrongCRC32 exception!\n")); + } + + return Uint64ToUint32(crc); +} + +boolean +TxUtil::StrongCRC32_CI4(const uint8* src, int width, int height, int rowStride, + uint32* crc32, uint32* cimax) +{ + /* NOTE: bytes_per_width must be equal or larger than 4 */ + + /* 4bit CI */ + try { + uint32 crc32Ret = StrongCRC32(src, width, height, 0, rowStride); + uint32 cimaxRet = CalculateMaxCI4b(src, width, height, rowStride); + *crc32 = crc32Ret; + *cimax = cimaxRet; + return 1; + } catch(...) { + DBG_INFO(80, wst("Error: RiceCRC32 exception!\n")); + } + return 0; +} + +boolean +TxUtil::StrongCRC32_CI8(const uint8* src, int width, int height, int rowStride, + uint32* crc32, uint32* cimax) +{ + /* NOTE: bytes_per_width must be equal or larger than 4 */ + + /* 8bit CI */ + try { + uint32 crc32Ret = StrongCRC32(src, width, height, 1, rowStride); + uint32 cimaxRet = CalculateMaxCI8b(src, width, height, rowStride); + *crc32 = crc32Ret; + *cimax = cimaxRet; + return 1; + } + catch (...) { + DBG_INFO(80, wst("Error: RiceCRC32 exception!\n")); + } + return 0; +} + uint32 TxUtil::getNumberofProcessors() { uint32 numcore = 1; //std::thread::hardware_concurrency(); diff --git a/GLideN64/src/GLideNHQ/TxUtil.h b/GLideN64/src/GLideNHQ/TxUtil.h index 26b6a058..1c0b2e61 100644 --- a/GLideN64/src/GLideNHQ/TxUtil.h +++ b/GLideN64/src/GLideNHQ/TxUtil.h @@ -43,14 +43,16 @@ class TxUtil uint32* crc32, uint32* cimax); static boolean RiceCRC32_CI8(const uint8* src, int width, int height, int rowStride, uint32* crc32, uint32* cimax); + static uint32 StrongCRC32(const uint8* src, int width, int height, int size, int rowStride); + static boolean StrongCRC32_CI4(const uint8* src, int width, int height, int rowStride, + uint32* crc32, uint32* cimax); + static boolean StrongCRC32_CI8(const uint8* src, int width, int height, int rowStride, + uint32* crc32, uint32* cimax); public: static int sizeofTx(int width, int height, ColorFormat format); static uint32 checksumTx(uint8 *data, int width, int height, ColorFormat format); -#if 0 /* unused */ - static uint32 chkAlpha(uint32* src, int width, int height); -#endif - static uint32 checksum(uint8 *src, int width, int height, int size, int rowStride); static uint64 checksum64(uint8 *src, int width, int height, int size, int rowStride, uint8 *palette); + static uint64 checksum64strong(uint8 *src, int width, int height, int size, int rowStride, uint8 *palette); static uint32 getNumberofProcessors(); }; diff --git a/GLideN64/src/GLideNUI-wtl/Language.cpp b/GLideN64/src/GLideNUI-wtl/Language.cpp index 40322299..d629f6d3 100644 --- a/GLideN64/src/GLideNUI-wtl/Language.cpp +++ b/GLideN64/src/GLideNUI-wtl/Language.cpp @@ -33,11 +33,12 @@ void LoadDefaultStrings(void) g_defaultStrings.insert(LANG_STRINGS::value_type(VIDEO_WINDOWED_RESOLUTION, "Windowed resolution:")); g_defaultStrings.insert(LANG_STRINGS::value_type(VIDEO_WINDOWED_RESOLUTION_TOOLTIP, "This option selects the resolution for windowed mode. You can also type in a custom window size.\n\n[Recommended: 640 x 480, 800 x 600, 1024 x 768, 1280 x 960]")); g_defaultStrings.insert(LANG_STRINGS::value_type(VIDEO_ASPECT_RATIO, "Aspect ratio:")); - g_defaultStrings.insert(LANG_STRINGS::value_type(VIDEO_ASPECT_RATIO_TOOLTIP, "This setting adjusts the aspect ratio of the video output. All N64 games support 4:3. Some games support 16:9 within game settings. Use Stretch to fill the screen without pillar or letterboxing.\n\nTry to adjust game to fit tries to adjust the viewing space to fit without stretching. Many games work well adjusted, but some don't.")); + g_defaultStrings.insert(LANG_STRINGS::value_type(VIDEO_ASPECT_RATIO_TOOLTIP, "This setting adjusts the aspect ratio of the video output. All N64 games support 4:3. Some games support 16:9 within game settings. Use Stretch to fill the screen without pillar or letterboxing.\n\nTry to adjust 4:3 game to fit (also known as \"widescreen hack\") tries to adjust the viewing space to fit a game designed for 4:3 without stretching. Many games work well adjusted, but some don't.\n\nTry to adjust 16:9 game to fit</span> (also known as \"ultrawidescreen hack\") tries to adjust the viewing space to fit a game designed for 16:9 without stretching. Many games work well adjusted, but some don't. For games offering native 16:9 options, this is generally preferable for ultrawide aspect ratios as it'll lead to fewer rendering issues.")); g_defaultStrings.insert(LANG_STRINGS::value_type(VIDEO_ASPECT_4_3, "4:3 (recommended)")); g_defaultStrings.insert(LANG_STRINGS::value_type(VIDEO_ASPECT_16_19, "16:9")); g_defaultStrings.insert(LANG_STRINGS::value_type(VIDEO_ASPECT_STRETCH, "Stretch")); - g_defaultStrings.insert(LANG_STRINGS::value_type(VIDEO_ASPECT_ADJUST, "Try to adjust game to fit")); + g_defaultStrings.insert(LANG_STRINGS::value_type(VIDEO_ASPECT_ADJUST, "Try to adjust 4:3 game to fit")); + g_defaultStrings.insert(LANG_STRINGS::value_type(VIDEO_ASPECT_ADJUST, "Try to adjust 16:9 game to fit")); g_defaultStrings.insert(LANG_STRINGS::value_type(VIDEO_VSYNC, "Enable VSync")); g_defaultStrings.insert(LANG_STRINGS::value_type(VIDEO_VSYNC_TOOLTIP, "Vertical sync, or VSync, can improve the image by syncing the game's frame rate to your monitor's refresh rate. This prevents image tearing, but may cause performance problems.\n\n[Recommended: Usually off, on if you have image tearing problems]")); g_defaultStrings.insert(LANG_STRINGS::value_type(VIDEO_THREADED_VIDEO, "Enable threaded video")); diff --git a/GLideN64/src/GLideNUI-wtl/config-video.cpp b/GLideN64/src/GLideNUI-wtl/config-video.cpp index 7456f733..8c56fd05 100644 --- a/GLideN64/src/GLideNUI-wtl/config-video.cpp +++ b/GLideN64/src/GLideNUI-wtl/config-video.cpp @@ -477,7 +477,8 @@ void CVideoTab::LoadSettings(bool /*blockCustomSettings*/) { case Config::aStretch: aspectComboBox.SetCurSel(2); break; case Config::a43: aspectComboBox.SetCurSel(0); break; case Config::a169: aspectComboBox.SetCurSel(1); break; - case Config::aAdjust: aspectComboBox.SetCurSel(3); break; + case Config::aAdjust43: aspectComboBox.SetCurSel(3); break; + case Config::aAdjust169: aspectComboBox.SetCurSel(4); break; } CComboBox(GetDlgItem(IDC_CMB_PATTERN)).SetCurSel(config.generalEmulation.rdramImageDitheringMode); @@ -522,7 +523,8 @@ void CVideoTab::SaveSettings() if (AspectIndx == 2) { config.frameBufferEmulation.aspect = Config::aStretch; } else if (AspectIndx == 0) { config.frameBufferEmulation.aspect = Config::a43; } else if (AspectIndx == 1) { config.frameBufferEmulation.aspect = Config::a169; } - else if (AspectIndx == 3) { config.frameBufferEmulation.aspect = Config::aAdjust; } + else if (AspectIndx == 3) { config.frameBufferEmulation.aspect = Config::aAdjust43; } + else if (AspectIndx == 4) { config.frameBufferEmulation.aspect = Config::aAdjust169; } config.video.verticalSync = CButton(GetDlgItem(IDC_CHK_VERTICAL_SYNC)).GetCheck() == BST_CHECKED; config.video.threadedVideo = CButton(GetDlgItem(IDC_CHK_THREADED_VIDEO)).GetCheck() == BST_CHECKED; diff --git a/GLideN64/src/GLideNUI/ConfigDialog.cpp b/GLideN64/src/GLideNUI/ConfigDialog.cpp index da8fbd1f..0bb7d380 100644 --- a/GLideN64/src/GLideNUI/ConfigDialog.cpp +++ b/GLideN64/src/GLideNUI/ConfigDialog.cpp @@ -105,6 +105,8 @@ QString ConfigDialog::_hotkeyDescription(quint32 _idx) const return tr("Toggle force gamma correction"); case Config::HotKey::hkInaccurateTexCords: return tr("Toggle inaccurate texture coordinates"); + case Config::HotKey::hkStrongCRC: + return tr("Toggle strong CRC for textures dump"); } return tr("Unknown hotkey"); } @@ -330,9 +332,12 @@ void ConfigDialog::_init(bool reInit, bool blockCustomSettings) case Config::a169: ui->aspectComboBox->setCurrentIndex(1); break; - case Config::aAdjust: + case Config::aAdjust43: ui->aspectComboBox->setCurrentIndex(3); break; + case Config::aAdjust169: + ui->aspectComboBox->setCurrentIndex(4); + break; } ui->resolutionFactorSpinBox->valueChanged(2); @@ -370,6 +375,7 @@ void ConfigDialog::_init(bool reInit, bool blockCustomSettings) ui->texturePackGroupBox->setChecked(config.textureFilter.txHiresEnable != 0); ui->alphaChannelCheckBox->setChecked(config.textureFilter.txHiresFullAlphaChannel != 0); ui->alternativeCRCCheckBox->setChecked(config.textureFilter.txHresAltCRC != 0); + ui->strongCRCCheckBox->setChecked(config.textureFilter.txStrongCRC != 0); ui->force16bppCheckBox->setChecked(config.textureFilter.txForce16bpp != 0); ui->compressCacheCheckBox->setChecked(config.textureFilter.txCacheCompression != 0); ui->saveTextureCacheCheckBox->setChecked(config.textureFilter.txSaveCache != 0); @@ -377,9 +383,9 @@ void ConfigDialog::_init(bool reInit, bool blockCustomSettings) ui->hiresTexFileStorageCheckBox->setChecked(config.textureFilter.txHiresTextureFileStorage != 0); ui->noTexFileStorageCheckBox->setChecked(config.textureFilter.txNoTextureFileStorage != 0); - ui->texPackPathLineEdit->setText(QString::fromWCharArray(config.textureFilter.txPath)); - ui->texCachePathLineEdit->setText(QString::fromWCharArray(config.textureFilter.txCachePath)); - ui->texDumpPathLineEdit->setText(QString::fromWCharArray(config.textureFilter.txDumpPath)); + ui->texPackPathLineEdit->setText(QDir::toNativeSeparators(QString::fromWCharArray(config.textureFilter.txPath))); + ui->texCachePathLineEdit->setText(QDir::toNativeSeparators(QString::fromWCharArray(config.textureFilter.txCachePath))); + ui->texDumpPathLineEdit->setText(QDir::toNativeSeparators(QString::fromWCharArray(config.textureFilter.txDumpPath))); ui->textureFilterLimitSpinBox->setValue(config.textureFilter.txHiresVramLimit); @@ -649,7 +655,9 @@ void ConfigDialog::accept(bool justSave) { else if (ui->aspectComboBox->currentIndex() == 1) config.frameBufferEmulation.aspect = Config::a169; else if (ui->aspectComboBox->currentIndex() == 3) - config.frameBufferEmulation.aspect = Config::aAdjust; + config.frameBufferEmulation.aspect = Config::aAdjust43; + else if (ui->aspectComboBox->currentIndex() == 4) + config.frameBufferEmulation.aspect = Config::aAdjust169; if (ui->factor0xRadioButton->isChecked()) config.frameBufferEmulation.nativeResFactor = 0; @@ -684,6 +692,7 @@ void ConfigDialog::accept(bool justSave) { config.textureFilter.txHiresEnable = ui->texturePackGroupBox->isChecked() ? 1 : 0; config.textureFilter.txHiresFullAlphaChannel = ui->alphaChannelCheckBox->isChecked() ? 1 : 0; config.textureFilter.txHresAltCRC = ui->alternativeCRCCheckBox->isChecked() ? 1 : 0; + config.textureFilter.txStrongCRC = ui->strongCRCCheckBox->isChecked() ? 1 : 0; config.textureFilter.txCacheCompression = ui->compressCacheCheckBox->isChecked() ? 1 : 0; config.textureFilter.txForce16bpp = ui->force16bppCheckBox->isChecked() ? 1 : 0; @@ -877,7 +886,7 @@ void ConfigDialog::on_texPackPathButton_clicked() ui->texPackPathLineEdit->text(), options); if (!directory.isEmpty()) - ui->texPackPathLineEdit->setText(directory); + ui->texPackPathLineEdit->setText(QDir::toNativeSeparators(directory)); } void ConfigDialog::on_texCachePathButton_clicked() @@ -888,7 +897,7 @@ void ConfigDialog::on_texCachePathButton_clicked() ui->texCachePathLineEdit->text(), options); if (!directory.isEmpty()) - ui->texCachePathLineEdit->setText(directory); + ui->texCachePathLineEdit->setText(QDir::toNativeSeparators(directory)); } void ConfigDialog::on_texDumpPathButton_clicked() @@ -899,7 +908,7 @@ void ConfigDialog::on_texDumpPathButton_clicked() ui->texDumpPathLineEdit->text(), options); if (!directory.isEmpty()) - ui->texDumpPathLineEdit->setText(directory); + ui->texDumpPathLineEdit->setText(QDir::toNativeSeparators(directory)); } void ConfigDialog::on_noTexFileStorageCheckBox_toggled(bool checked) diff --git a/GLideN64/src/GLideNUI/GLideNUI.cpp b/GLideN64/src/GLideNUI/GLideNUI.cpp index 1f324520..02fd7d53 100644 --- a/GLideN64/src/GLideNUI/GLideNUI.cpp +++ b/GLideN64/src/GLideNUI/GLideNUI.cpp @@ -31,13 +31,14 @@ int openConfigDialog(const wchar_t * _strFileName, const wchar_t * _strSharedFil if (config.generalEmulation.enableCustomSettings != 0 && _romName != nullptr && strlen(_romName) != 0) loadCustomRomSettings(strIniFileName, strSharedIniFileName, _romName); + int argc = 1; + char argv0[] = "GLideN64"; + char * argv[] = { argv0 }; std::unique_ptr pQApp; QCoreApplication* pApp = QCoreApplication::instance(); if (pApp == nullptr) { - int argc = 0; - char * argv = 0; - pQApp.reset(new QApplication(argc, &argv)); + pQApp.reset(new QApplication(argc, argv)); pApp = pQApp.get(); } @@ -63,9 +64,10 @@ int openAboutDialog(const wchar_t * _strFileName) cleanMyResource(); initMyResource(); - int argc = 0; - char * argv = 0; - QApplication a(argc, &argv); + int argc = 1; + char argv0[] = "GLideN64"; + char * argv[] = { argv0 }; + QApplication a(argc, argv); QTranslator translator; if (translator.load(getTranslationFile(), QString::fromWCharArray(_strFileName))) diff --git a/GLideN64/src/GLideNUI/Settings.cpp b/GLideN64/src/GLideNUI/Settings.cpp index c32e0472..be7a9a1b 100644 --- a/GLideN64/src/GLideNUI/Settings.cpp +++ b/GLideN64/src/GLideNUI/Settings.cpp @@ -103,6 +103,7 @@ void _loadSettings(QSettings & settings) config.textureFilter.txHiresEnable = settings.value("txHiresEnable", config.textureFilter.txHiresEnable).toInt(); config.textureFilter.txHiresFullAlphaChannel = settings.value("txHiresFullAlphaChannel", config.textureFilter.txHiresFullAlphaChannel).toInt(); config.textureFilter.txHresAltCRC = settings.value("txHresAltCRC", config.textureFilter.txHresAltCRC).toInt(); + config.textureFilter.txStrongCRC = settings.value("txStrongCRC", config.textureFilter.txStrongCRC).toInt(); config.textureFilter.txForce16bpp = settings.value("txForce16bpp", config.textureFilter.txForce16bpp).toInt(); config.textureFilter.txCacheCompression = settings.value("txCacheCompression", config.textureFilter.txCacheCompression).toInt(); config.textureFilter.txSaveCache = settings.value("txSaveCache", config.textureFilter.txSaveCache).toInt(); @@ -252,6 +253,7 @@ void _writeSettingsToFile(const QString & filename) settings.setValue("txHiresEnable", config.textureFilter.txHiresEnable); settings.setValue("txHiresFullAlphaChannel", config.textureFilter.txHiresFullAlphaChannel); settings.setValue("txHresAltCRC", config.textureFilter.txHresAltCRC); + settings.setValue("txStrongCRC", config.textureFilter.txStrongCRC); settings.setValue("txForce16bpp", config.textureFilter.txForce16bpp); settings.setValue("txCacheCompression", config.textureFilter.txCacheCompression); settings.setValue("txSaveCache", config.textureFilter.txSaveCache); @@ -550,6 +552,7 @@ void saveCustomRomSettings(const QString & _strIniFolder, const QString & _strSh WriteCustomSetting(textureFilter, txHiresEnable); WriteCustomSetting(textureFilter, txHiresFullAlphaChannel); WriteCustomSetting(textureFilter, txHresAltCRC); + WriteCustomSetting(textureFilter, txStrongCRC); WriteCustomSetting(textureFilter, txForce16bpp); WriteCustomSetting(textureFilter, txCacheCompression); WriteCustomSetting(textureFilter, txSaveCache); diff --git a/GLideN64/src/GLideNUI/configDialog.ui b/GLideN64/src/GLideNUI/configDialog.ui index 70b4bf58..c6674504 100644 --- a/GLideN64/src/GLideNUI/configDialog.ui +++ b/GLideN64/src/GLideNUI/configDialog.ui @@ -168,7 +168,7 @@ - <html><head/><body><p>This setting adjusts the aspect ratio of the video output. All N64 games support <span style=" font-weight:600;">4:3</span>. Some games support <span style=" font-weight:600;">16:9</span> within game settings. Use <span style=" font-weight:600;">Stretch</span> to fill the screen without pillar or letterboxing.</p><p><span style=" font-weight:600;">Try to adjust game to fit</span> tries to adjust the viewing space to fit without stretching. Many games work well adjusted, but some don't.</p></body></html> + <html><head/><body><p>This setting adjusts the aspect ratio of the video output. All N64 games support <span style=" font-weight:600;">4:3</span>. Some games support <span style=" font-weight:600;">16:9</span> within game settings. Use <span style=" font-weight:600;">Stretch</span> to fill the screen without pillar or letterboxing.</p><p><span style=" font-weight:600;">Try to adjust 4:3 game to fit</span> (also known as "widescreen hack") tries to adjust the viewing space to fit a game designed for 4:3 without stretching. Many games work well adjusted, but some don't.</p><p><span style=" font-weight:600;">Try to adjust 16:9 game to fit</span> (also known as "ultrawidescreen hack") tries to adjust the viewing space to fit a game designed for 16:9 without stretching. Many games work well adjusted, but some don't. For games offering native 16:9 options, this is generally preferable for ultrawide aspect ratios as it'll lead to fewer rendering issues.</p></body></html> @@ -212,7 +212,12 @@ - Try to adjust game to fit + Try to adjust 4:3 game to fit + + + + + Try to adjust 16:9 game to fit @@ -2673,6 +2678,16 @@ + + + + <html><head/><body><p>This option enables alternative, strong CRC method instead of RiceCRC for texture dump. If you find some textures not dumped, try checking this option.</p><p>[Recommended: <span style=" font-style:italic;">Mostly unchecked, unless RiceCRC fails</span>]</p></body></html> + + + Strong CRC calculation (when RiceCRC fails) + + + diff --git a/GLideN64/src/Graphics/FramebufferTextureFormats.h b/GLideN64/src/Graphics/FramebufferTextureFormats.h index c15b6295..ec14150e 100644 --- a/GLideN64/src/Graphics/FramebufferTextureFormats.h +++ b/GLideN64/src/Graphics/FramebufferTextureFormats.h @@ -31,10 +31,11 @@ namespace graphics { DatatypeParam lutType; u32 lutFormatBytes; - InternalColorFormatParam noiseInternalFormat; - ColorFormatParam noiseFormat; - DatatypeParam noiseType; - u32 noiseFormatBytes; + // Used for font atlas + InternalColorFormatParam fontInternalFormat; + ColorFormatParam fontFormat; + DatatypeParam fontType; + u32 fontFormatBytes; virtual ~FramebufferTextureFormats() {} }; diff --git a/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderCommon.cpp b/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderCommon.cpp index 92fbdfc2..35161d33 100644 --- a/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderCommon.cpp +++ b/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderCommon.cpp @@ -1003,26 +1003,18 @@ class ShaderNoise : public ShaderPart public: ShaderNoise(const opengl::GLInfo & _glinfo) { - if (_glinfo.isGLES2) { - m_part = - "uniform sampler2D uTexNoise; \n" - "lowp float snoise() \n" - "{ \n" - " mediump vec2 texSize = vec2(640.0, 580.0); \n" - " mediump vec2 coord = gl_FragCoord.xy/uScreenScale/texSize; \n" - " return texture2D(uTexNoise, coord).r; \n" - "} \n" - ; - } else { - m_part = - "uniform sampler2D uTexNoise; \n" - "lowp float snoise() \n" - "{ \n" - " ivec2 coord = ivec2(gl_FragCoord.xy/uScreenScale); \n" - " return texelFetch(uTexNoise, coord, 0).r; \n" - "} \n" - ; - } + m_part = + "uniform mediump float uNoiseSeed; \n" + "lowp float snoise() \n" + "{ \n" + " highp vec2 coord = floor(gl_FragCoord.xy/uScreenScale);\n" + " highp vec3 p3 = vec3(uNoiseSeed, coord); \n" + // hash13 from https://www.shadertoy.com/view/4djSRW + " p3 = fract(p3 * .1031); \n" + " p3 += dot(p3, p3.zyx + 31.32); \n" + " return fract((p3.x + p3.y) * p3.z); \n" + "} \n" + ; } }; @@ -1067,7 +1059,6 @@ class ShaderDither : public ShaderPart "} \n" "lowp vec3 snoiseRGB() \n" "{ \n" - " mediump vec2 texSize = vec2(640.0, 580.0); \n" ; if (config.generalEmulation.enableHiresNoiseDithering != 0) // multiplier for higher res noise effect @@ -1077,19 +1068,15 @@ class ShaderDither : public ShaderPart m_part += " lowp float mult = 1.0; \n"; m_part += - " mediump vec2 coordR = mult * ((gl_FragCoord.xy)/uScreenScale/texSize);\n" - " mediump vec2 coordG = mult * ((gl_FragCoord.xy + vec2( 0.0, texSize.y / 2.0 ))/uScreenScale/texSize);\n" - " mediump vec2 coordB = mult * ((gl_FragCoord.xy + vec2( texSize.x / 2.0, 0.0))/uScreenScale/texSize);\n" - // Only red channel of noise texture contains noise. - " lowp float r = texture(uTexNoise,coordR).r; \n" - " lowp float g = texture(uTexNoise,coordG).r; \n" - " lowp float b = texture(uTexNoise,coordB).r; \n" - " \n" - " return vec3(r,g,b); \n" - "} \n" + " highp vec2 coord = floor(mult * (gl_FragCoord.xy/uScreenScale));\n" + " highp vec3 p3 = vec3(uNoiseSeed, coord); \n" + // hash33 from https://www.shadertoy.com/view/4djSRW + " p3 = fract(p3 * vec3(.1031, .1030, .0973)); \n" + " p3 += dot(p3, p3.yxz+33.33); \n" + " return fract((p3.xxy + p3.yxx)*p3.zyx); \n" + "} \n" "lowp float snoiseA() \n" "{ \n" - " mediump vec2 texSize = vec2(640.0, 580.0); \n" ; if (config.generalEmulation.enableHiresNoiseDithering != 0) // multiplier for higher res noise effect @@ -1100,10 +1087,12 @@ class ShaderDither : public ShaderPart " lowp float mult = 1.0; \n"; m_part += " \n" - " mediump vec2 coord = mult * ((gl_FragCoord.xy)/uScreenScale/texSize);\n" - " \n" - // Only red channel of noise texture contains noise. - " return texture(uTexNoise,coord).r; \n" + " highp vec2 coord = floor(mult * (gl_FragCoord.xy/uScreenScale));\n" + " highp vec3 p3 = vec3(uNoiseSeed, coord); \n" + // hash13 from https://www.shadertoy.com/view/4djSRW + " p3 = fract(p3 * .1031); \n" + " p3 += dot(p3, p3.zyx + 31.32); \n" + " return fract((p3.x + p3.y) * p3.z); \n" "} \n" ; } diff --git a/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactory.cpp b/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactory.cpp index 798dab25..29614472 100644 --- a/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactory.cpp +++ b/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactory.cpp @@ -19,7 +19,7 @@ void CombinerProgramUniformFactory::buildUniforms(GLuint _program, const CombinerKey &_key, UniformGroups &_uniforms) { - _addNoiseTex(_program, _uniforms); + _addNoiseSeed(_program, _uniforms); _addScreenSpaceTriangleInfo(_program, _uniforms); _addRasterInfo(_program, _uniforms); _addViewportInfo(_program, _uniforms); @@ -71,7 +71,7 @@ void CombinerProgramUniformFactory::buildUniforms(GLuint _program, _addBlendCvg(_program, _uniforms); - _addDitherMode(_program, _uniforms, _inputs.usesNoise()); + _addDitherMode(_program, _uniforms); _addScreenScale(_program, _uniforms); diff --git a/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactory.h b/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactory.h index 3f1b2a33..92574514 100644 --- a/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactory.h +++ b/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactory.h @@ -15,7 +15,7 @@ class CombinerProgramUniformFactory { UniformGroups &_uniforms); private: - virtual void _addNoiseTex(GLuint _program, UniformGroups &_uniforms) const = 0; + virtual void _addNoiseSeed(GLuint _program, UniformGroups &_uniforms) const = 0; virtual void _addScreenSpaceTriangleInfo(GLuint _program, UniformGroups &_uniforms) const = 0; @@ -55,7 +55,7 @@ class CombinerProgramUniformFactory { virtual void _addBlendCvg(GLuint _program, UniformGroups &_uniforms) const = 0; - virtual void _addDitherMode(GLuint _program, UniformGroups &_uniforms, bool _usesNoise) const = 0; + virtual void _addDitherMode(GLuint _program, UniformGroups &_uniforms) const = 0; virtual void _addScreenScale(GLuint _program, UniformGroups &_uniforms) const = 0; diff --git a/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactoryAccurate.cpp b/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactoryAccurate.cpp index f5bf4a49..462e703b 100644 --- a/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactoryAccurate.cpp +++ b/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactoryAccurate.cpp @@ -317,7 +317,6 @@ class UTextureEngine : public UniformGroup uTexWrap[t].set(aTexWrap[t][0], aTexWrap[t][1], _force); uTexClamp[t].set(aTexClamp[t][0], aTexClamp[t][1], _force); uTexWrapEn[t].set(aTexWrapEn[t][0], aTexWrapEn[t][1], _force); - uTexWrapEn[t].set(aTexWrapEn[t][0], aTexWrapEn[t][1], _force); uTexClampEn[t].set(aTexClampEn[t][0], aTexClampEn[t][1], _force); uTexMirrorEn[t].set(aTexMirrorEn[t][0], aTexMirrorEn[t][1], _force); uTexSize[t].set(aTexSize[t][0], aTexSize[t][1], _force); diff --git a/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactoryCommon.cpp b/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactoryCommon.cpp index fc35779d..d9cb8f39 100644 --- a/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactoryCommon.cpp +++ b/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactoryCommon.cpp @@ -5,7 +5,6 @@ #include #include -#include #include #include #include @@ -15,20 +14,21 @@ namespace { using namespace glsl; /*---------------UniformGroup-------------*/ -class UNoiseTex : public UniformGroup +class UNoiseSeed : public UniformGroup { public: - UNoiseTex(GLuint _program) { - LocateUniform(uTexNoise); + UNoiseSeed(GLuint _program) { + LocateUniform(uNoiseSeed); } void update(bool _force) override { - uTexNoise.set(int(graphics::textureIndices::NoiseTex), _force); + u32 counter = dwnd().getBuffersSwapCount(); + uNoiseSeed.set(static_cast(counter & 0xff), _force); } private: - iUniform uTexNoise; + fUniform uNoiseSeed; }; class UDepthTex : public UniformGroup @@ -323,8 +323,7 @@ class UBlendCvg : public UniformGroup class UDitherMode : public UniformGroup { public: - UDitherMode(GLuint _program, bool _usesNoise) - : m_usesNoise(_usesNoise) + UDitherMode(GLuint _program) { LocateUniform(uAlphaCompareMode); LocateUniform(uAlphaDitherMode); @@ -343,18 +342,12 @@ class UDitherMode : public UniformGroup uAlphaDitherMode.set(0, _force); uColorDitherMode.set(0, _force); } - - bool updateNoiseTex = m_usesNoise; - updateNoiseTex |= (gDP.otherMode.cycleType < G_CYC_COPY) && (gDP.otherMode.colorDither == G_CD_NOISE || gDP.otherMode.alphaDither == G_AD_NOISE || gDP.otherMode.alphaCompare == G_AC_DITHER); - if (updateNoiseTex) - g_noiseTexture.update(); } private: iUniform uAlphaCompareMode; iUniform uAlphaDitherMode; iUniform uColorDitherMode; - bool m_usesNoise; }; class UScreenScale : public UniformGroup @@ -770,9 +763,9 @@ class ULights : public UniformGroup /*---------------CombinerProgramUniformFactoryCommon-------------*/ namespace glsl { -void CombinerProgramUniformFactoryCommon::_addNoiseTex(GLuint _program, UniformGroups &_uniforms) const +void CombinerProgramUniformFactoryCommon::_addNoiseSeed(GLuint _program, UniformGroups &_uniforms) const { - _uniforms.emplace_back(new UNoiseTex(_program)); + _uniforms.emplace_back(new UNoiseSeed(_program)); } void CombinerProgramUniformFactoryCommon::_addScreenSpaceTriangleInfo(GLuint _program, UniformGroups &_uniforms) const @@ -845,9 +838,9 @@ void CombinerProgramUniformFactoryCommon::_addBlendCvg(GLuint _program, UniformG _uniforms.emplace_back(new UBlendCvg(_program)); } -void CombinerProgramUniformFactoryCommon::_addDitherMode(GLuint _program, UniformGroups &_uniforms, bool _usesNoise) const +void CombinerProgramUniformFactoryCommon::_addDitherMode(GLuint _program, UniformGroups &_uniforms) const { - _uniforms.emplace_back(new UDitherMode(_program, _usesNoise)); + _uniforms.emplace_back(new UDitherMode(_program)); } void CombinerProgramUniformFactoryCommon::_addScreenScale(GLuint _program, UniformGroups &_uniforms) const diff --git a/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactoryCommon.h b/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactoryCommon.h index a9a96d64..c9176f05 100644 --- a/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactoryCommon.h +++ b/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactoryCommon.h @@ -100,7 +100,7 @@ class CombinerProgramUniformFactoryCommon : public CombinerProgramUniformFactory CombinerProgramUniformFactoryCommon(const opengl::GLInfo & _glInfo); private: - void _addNoiseTex(GLuint _program, UniformGroups &_uniforms) const override; + void _addNoiseSeed(GLuint _program, UniformGroups &_uniforms) const override; void _addScreenSpaceTriangleInfo(GLuint _program, UniformGroups &_uniforms) const override; @@ -130,7 +130,7 @@ class CombinerProgramUniformFactoryCommon : public CombinerProgramUniformFactory void _addBlendCvg(GLuint _program, UniformGroups &_uniforms) const override; - void _addDitherMode(GLuint _program, UniformGroups &_uniforms, bool _usesNoise) const override; + void _addDitherMode(GLuint _program, UniformGroups &_uniforms) const override; void _addScreenScale(GLuint _program, UniformGroups &_uniforms) const override; diff --git a/GLideN64/src/Graphics/OpenGLContext/mupen64plus/mupen64plus_DisplayWindow.cpp b/GLideN64/src/Graphics/OpenGLContext/mupen64plus/mupen64plus_DisplayWindow.cpp index e434872f..5762a7ec 100644 --- a/GLideN64/src/Graphics/OpenGLContext/mupen64plus/mupen64plus_DisplayWindow.cpp +++ b/GLideN64/src/Graphics/OpenGLContext/mupen64plus/mupen64plus_DisplayWindow.cpp @@ -170,11 +170,6 @@ void DisplayWindowMupen64plus::_readScreen2(void * _dest, int * _width, int * _h if (_dest == nullptr) return; - u8 *pBufferData = (u8*)malloc((*_width)*(*_height) * 4); - if (pBufferData == nullptr) - return; - u8 *pDest = (u8*)_dest; - #if !defined(OS_ANDROID) && !defined(OS_IOS) GLint oldMode; glGetIntegerv(GL_READ_BUFFER, &oldMode); @@ -182,11 +177,14 @@ void DisplayWindowMupen64plus::_readScreen2(void * _dest, int * _width, int * _h glReadBuffer(GL_FRONT); else glReadBuffer(GL_BACK); - glReadPixels(0, m_heightOffset, m_screenWidth, m_screenHeight, GL_RGBA, GL_UNSIGNED_BYTE, pBufferData); + glReadPixels(0, m_heightOffset, m_screenWidth, m_screenHeight, GL_RGB, GL_UNSIGNED_BYTE, _dest); glReadBuffer(oldMode); #else + u8 *pBufferData = (u8*)malloc((*_width)*(*_height) * 4); + if (pBufferData == nullptr) + return; + u8 *pDest = (u8*)_dest; glReadPixels(0, m_heightOffset, m_screenWidth, m_screenHeight, GL_RGBA, GL_UNSIGNED_BYTE, pBufferData); -#endif //Convert RGBA to RGB for (s32 y = 0; y < *_height; ++y) { @@ -201,6 +199,7 @@ void DisplayWindowMupen64plus::_readScreen2(void * _dest, int * _width, int * _h } free(pBufferData); +#endif } graphics::ObjectHandle DisplayWindowMupen64plus::_getDefaultFramebuffer() diff --git a/GLideN64/src/Graphics/OpenGLContext/opengl_BufferManipulationObjectFactory.cpp b/GLideN64/src/Graphics/OpenGLContext/opengl_BufferManipulationObjectFactory.cpp index 61de0845..ffbcde6a 100644 --- a/GLideN64/src/Graphics/OpenGLContext/opengl_BufferManipulationObjectFactory.cpp +++ b/GLideN64/src/Graphics/OpenGLContext/opengl_BufferManipulationObjectFactory.cpp @@ -282,10 +282,10 @@ struct FramebufferTextureFormatsGLES2 : public graphics::FramebufferTextureForma colorFormatBytes = 2; } - noiseInternalFormat = graphics::internalcolorFormat::LUMINANCE; - noiseFormat = graphics::colorFormat::LUMINANCE; - noiseType = GL_UNSIGNED_BYTE; - noiseFormatBytes = 1; + fontInternalFormat = graphics::internalcolorFormat::LUMINANCE; + fontFormat = graphics::colorFormat::LUMINANCE; + fontType = GL_UNSIGNED_BYTE; + fontFormatBytes = 1; } }; @@ -339,10 +339,10 @@ struct FramebufferTextureFormatsGLES3 : public graphics::FramebufferTextureForma lutType = GL_UNSIGNED_INT; lutFormatBytes = 4; - noiseInternalFormat = GL_R8; - noiseFormat = GL_RED; - noiseType = GL_UNSIGNED_BYTE; - noiseFormatBytes = 1; + fontInternalFormat = GL_R8; + fontFormat = GL_RED; + fontType = GL_UNSIGNED_BYTE; + fontFormatBytes = 1; } }; @@ -379,10 +379,10 @@ struct FramebufferTextureFormatsOpenGL : public graphics::FramebufferTextureForm lutType = GL_UNSIGNED_INT; lutFormatBytes = 4; - noiseInternalFormat = GL_R8; - noiseFormat = GL_RED; - noiseType = GL_UNSIGNED_BYTE; - noiseFormatBytes = 1; + fontInternalFormat = GL_R8; + fontFormat = GL_RED; + fontType = GL_UNSIGNED_BYTE; + fontFormatBytes = 1; } }; diff --git a/GLideN64/src/Graphics/OpenGLContext/opengl_Parameters.cpp b/GLideN64/src/Graphics/OpenGLContext/opengl_Parameters.cpp index 55f34572..2ca2153e 100644 --- a/GLideN64/src/Graphics/OpenGLContext/opengl_Parameters.cpp +++ b/GLideN64/src/Graphics/OpenGLContext/opengl_Parameters.cpp @@ -71,11 +71,10 @@ namespace graphics { namespace textureIndices { TextureUnitParam Tex[2] = { 0U, 1U }; - TextureUnitParam NoiseTex(2U); - TextureUnitParam DepthTex(3U); - TextureUnitParam ZLUTTex(4U); - TextureUnitParam PaletteTex(5U); - TextureUnitParam MSTex[2] = { 6U, 7U }; + TextureUnitParam DepthTex(2U); + TextureUnitParam ZLUTTex(3U); + TextureUnitParam PaletteTex(4U); + TextureUnitParam MSTex[2] = { 5U, 6U }; } namespace textureImageUnits { diff --git a/GLideN64/src/Graphics/OpenGLContext/windows/WindowsWGL.cpp b/GLideN64/src/Graphics/OpenGLContext/windows/WindowsWGL.cpp index 77df7bcb..d918df18 100644 --- a/GLideN64/src/Graphics/OpenGLContext/windows/WindowsWGL.cpp +++ b/GLideN64/src/Graphics/OpenGLContext/windows/WindowsWGL.cpp @@ -15,7 +15,8 @@ bool WindowsWGL::start() 1, // version number PFD_DRAW_TO_WINDOW | // support window PFD_SUPPORT_OPENGL | // support OpenGL - PFD_DOUBLEBUFFER, // double buffered + PFD_DOUBLEBUFFER | // double buffered + PFD_SUPPORT_COMPOSITION, // composition support, see https://www.opengl.org/pipeline/article/vol003_7/ PFD_TYPE_RGBA, // RGBA type 32, // color depth 0, 0, 0, 0, 0, 0, // color bits ignored diff --git a/GLideN64/src/Graphics/OpenGLContext/windows/windows_DisplayWindow.cpp b/GLideN64/src/Graphics/OpenGLContext/windows/windows_DisplayWindow.cpp index 42107ae0..b0217892 100644 --- a/GLideN64/src/Graphics/OpenGLContext/windows/windows_DisplayWindow.cpp +++ b/GLideN64/src/Graphics/OpenGLContext/windows/windows_DisplayWindow.cpp @@ -198,7 +198,7 @@ bool DisplayWindowWindows::_borderlessDevice() m_screenHeight = static_cast(deviceMode.dmPelsHeight); m_heightOffset = 0; _setBufferSize(); - return (SetWindowPos(hWnd, NULL, 0, 0, m_screenWidth, m_screenHeight, SWP_NOACTIVATE | SWP_NOZORDER | SWP_SHOWWINDOW) == TRUE); + return (SetWindowPos(hWnd, NULL, 0, 0, m_screenWidth + 1, m_screenHeight, SWP_NOACTIVATE | SWP_NOZORDER | SWP_SHOWWINDOW) == TRUE); } } @@ -213,7 +213,7 @@ bool DisplayWindowWindows::_borderlessDevice() m_screenHeight = std::abs(monitors.rcMonitors[i].top - monitors.rcMonitors[i].bottom); m_heightOffset = 0; _setBufferSize(); - return (SetWindowPos(hWnd, NULL, X, Y, m_screenWidth, m_screenHeight, SWP_NOACTIVATE | SWP_NOZORDER | SWP_SHOWWINDOW) == TRUE); + return (SetWindowPos(hWnd, NULL, X, Y, m_screenWidth + 1, m_screenHeight, SWP_NOACTIVATE | SWP_NOZORDER | SWP_SHOWWINDOW) == TRUE); } } return false; diff --git a/GLideN64/src/Graphics/Parameters.h b/GLideN64/src/Graphics/Parameters.h index eebed5f9..a08e4f64 100644 --- a/GLideN64/src/Graphics/Parameters.h +++ b/GLideN64/src/Graphics/Parameters.h @@ -71,7 +71,6 @@ namespace graphics { namespace textureIndices { extern TextureUnitParam Tex[2]; - extern TextureUnitParam NoiseTex; extern TextureUnitParam DepthTex; extern TextureUnitParam ZLUTTex; extern TextureUnitParam PaletteTex; diff --git a/GLideN64/src/GraphicsDrawer.cpp b/GLideN64/src/GraphicsDrawer.cpp index e00690c2..a2fb0aff 100644 --- a/GLideN64/src/GraphicsDrawer.cpp +++ b/GLideN64/src/GraphicsDrawer.cpp @@ -11,7 +11,6 @@ #include "Performance.h" #include "TextureFilterHandler.h" #include "PostProcessor.h" -#include "NoiseTexture.h" #include "ZlutTexture.h" #include "PaletteTexture.h" #include "TextDrawer.h" @@ -223,10 +222,10 @@ void GraphicsDrawer::updateScissor(FrameBuffer * _pBuffer) const void GraphicsDrawer::_updateViewport(const FrameBuffer* _pBuffer, const f32 scale) const { - s32 X, Y, WIDTH, HEIGHT; + s32 X = 0, Y = 0, WIDTH, HEIGHT; f32 scaleX, scaleY; + const FrameBuffer* pCurrentBuffer = _pBuffer != nullptr ? _pBuffer : frameBufferList().getCurrent(); if (scale == 0.0f) { - const FrameBuffer* pCurrentBuffer = _pBuffer != nullptr ? _pBuffer : frameBufferList().getCurrent(); if (pCurrentBuffer != nullptr) { scaleX = scaleY = pCurrentBuffer->m_scale; } else { @@ -236,8 +235,10 @@ void GraphicsDrawer::_updateViewport(const FrameBuffer* _pBuffer, const f32 scal } else { scaleX = scaleY = scale; } - X = 0; - Y = 0; + if (pCurrentBuffer != nullptr) { + X = roundup(static_cast(pCurrentBuffer->m_originX), scaleX); + Y = roundup(static_cast(pCurrentBuffer->m_originY), scaleY); + } WIDTH = roundup(SCREEN_SIZE_DIM, scaleX); HEIGHT = roundup(SCREEN_SIZE_DIM, scaleY); gfxContext.setViewport(X, Y, WIDTH, HEIGHT); @@ -981,47 +982,59 @@ void GraphicsDrawer::drawDMATriangles(u32 _numVtx) dropRenderState(); } -void GraphicsDrawer::_drawThickLine(u32 _v0, u32 _v1, float _width) +static void correctLineVerticesColor(SPVertex _vertexBuf[2], SPVertex& _v0) { + auto copyColors = [](f32 const* src, f32* dst1, f32* dst2) + { + for (u32 i = 0; i < 4; ++i) + dst1[i] = dst2[i] = src[i]; + }; + + auto convertNormalsToColors = [](f32 const* normals, f32* dst) + { + // Line3D* microcodes have no lighting code + // Original colors are stored in vertex normals, which need to be converted back to colors. + for (u32 i = 0; i < 3; ++i) { + f32 color = normals[i] * 0.5f; + if (color < 0.0f) + color += 1.0f; + dst[i] = color; + } + }; + + SPVertex & vtx1 = _vertexBuf[0]; + SPVertex & vtx2 = _vertexBuf[1]; if ((gSP.geometryMode & G_LIGHTING) == 0) { if ((gSP.geometryMode & G_SHADE) == 0) { - SPVertex & vtx1 = triangles.vertices[_v0]; - vtx1.flat_r = gDP.primColor.r; - vtx1.flat_g = gDP.primColor.g; - vtx1.flat_b = gDP.primColor.b; - vtx1.flat_a = gDP.primColor.a; - SPVertex & vtx2 = triangles.vertices[_v1]; - vtx2.flat_r = gDP.primColor.r; - vtx2.flat_g = gDP.primColor.g; - vtx2.flat_b = gDP.primColor.b; - vtx2.flat_a = gDP.primColor.a; - } - else if ((gSP.geometryMode & G_SHADING_SMOOTH) == 0) { + copyColors(&gDP.primColor.r, &vtx1.r, &vtx1.flat_r); + copyColors(&gDP.primColor.r, &vtx2.r, &vtx2.flat_r); + } else if ((gSP.geometryMode & G_SHADING_SMOOTH) == 0) { // Flat shading - SPVertex & vtx0 = triangles.vertices[_v0 + ((RSP.w1 >> 24) & 3)]; - SPVertex & vtx1 = triangles.vertices[_v0]; - vtx1.r = vtx1.flat_r = vtx0.r; - vtx1.g = vtx1.flat_g = vtx0.g; - vtx1.b = vtx1.flat_b = vtx0.b; - vtx1.a = vtx1.flat_a = vtx0.a; - SPVertex & vtx2 = triangles.vertices[_v1]; - vtx2.r = vtx2.flat_r = vtx0.r; - vtx2.g = vtx2.flat_g = vtx0.g; - vtx2.b = vtx2.flat_b = vtx0.b; - vtx2.a = vtx2.flat_a = vtx0.a; + copyColors(&_v0.r, &vtx1.r, &vtx1.flat_r); + copyColors(&_v0.r, &vtx2.r, &vtx2.flat_r); } } + else { + convertNormalsToColors(&vtx1.nx, &vtx1.r); + convertNormalsToColors(&vtx2.nx, &vtx2.r); + } +} + +void GraphicsDrawer::_drawThickLine(u32 _v0, u32 _v1, float _width, u32 _flag) +{ + SPVertex vertexBuf[2] = { triangles.vertices[_v0], triangles.vertices[_v1] }; + correctLineVerticesColor(vertexBuf, triangles.vertices[_flag]); setDMAVerticesSize(4); SPVertex * pVtx = getDMAVerticesData(); const f32 ySign = GBI.isNegativeY() ? -1.0f : 1.0f; - pVtx[0] = triangles.vertices[_v0]; + pVtx[0] = vertexBuf[0]; pVtx[0].x = pVtx[0].x / pVtx[0].w * gSP.viewport.vscale[0] + gSP.viewport.vtrans[0]; pVtx[0].y = ySign * pVtx[0].y / pVtx[0].w * gSP.viewport.vscale[1] + gSP.viewport.vtrans[1]; pVtx[0].z = pVtx[0].z / pVtx[0].w; pVtx[1] = pVtx[0]; - pVtx[2] = triangles.vertices[_v1]; + pVtx[2] = vertexBuf[1]; pVtx[2].x = pVtx[2].x / pVtx[2].w * gSP.viewport.vscale[0] + gSP.viewport.vtrans[0]; pVtx[2].y = ySign * pVtx[2].y / pVtx[2].w * gSP.viewport.vscale[1] + gSP.viewport.vtrans[1]; pVtx[2].z = pVtx[2].z / pVtx[2].w; @@ -1057,12 +1070,12 @@ void GraphicsDrawer::_drawThickLine(u32 _v0, u32 _v1, float _width) drawScreenSpaceTriangle(4); } -void GraphicsDrawer::drawLine(u32 _v0, u32 _v1, float _width) +void GraphicsDrawer::drawLine(u32 _v0, u32 _v1, float _width, u32 _flag) { m_texrectDrawer.draw(); m_statistics.lines++; - if (!_canDraw()) + if (!_canDraw() || _width <= 0.0f) return; f32 lineWidth = _width; @@ -1070,8 +1083,9 @@ void GraphicsDrawer::drawLine(u32 _v0, u32 _v1, float _width) lineWidth *= dwnd().getScaleX(); else lineWidth *= config.frameBufferEmulation.nativeResFactor; + if (lineWidth > m_maxLineWidth) { - _drawThickLine(_v0, _v1, _width * 0.5f); + _drawThickLine(_v0, _v1, _width * 0.5f, _flag); return; } @@ -1086,6 +1100,8 @@ void GraphicsDrawer::drawLine(u32 _v0, u32 _v1, float _width) _updateViewport(); SPVertex vertexBuf[2] = { triangles.vertices[_v0], triangles.vertices[_v1] }; + correctLineVerticesColor(vertexBuf, triangles.vertices[_flag]); + gfxContext.drawLine(lineWidth, vertexBuf); dropRenderState(); } @@ -1847,7 +1863,6 @@ void GraphicsDrawer::_initData() TFH.init(); PostProcessor::get().init(); g_zlutTexture.init(); - g_noiseTexture.init(); g_paletteTexture.init(); perf.reset(); FBInfo::fbInfo.reset(); @@ -1871,7 +1886,6 @@ void GraphicsDrawer::_destroyData() m_texrectDrawer.destroy(); g_paletteTexture.destroy(); g_zlutTexture.destroy(); - g_noiseTexture.destroy(); PostProcessor::get().destroy(); if (TFH.optionsChanged()) TFH.shutdown(); diff --git a/GLideN64/src/GraphicsDrawer.h b/GLideN64/src/GraphicsDrawer.h index 5f0c307c..466620da 100644 --- a/GLideN64/src/GraphicsDrawer.h +++ b/GLideN64/src/GraphicsDrawer.h @@ -53,7 +53,7 @@ class GraphicsDrawer void drawDMATriangles(u32 _numVtx); - void drawLine(u32 _v0, u32 _v1, float _width); + void drawLine(u32 _v0, u32 _v1, float _width, u32 _flag); void drawRect(int _ulx, int _uly, int _lrx, int _lry); @@ -210,7 +210,7 @@ class GraphicsDrawer void _updateStates(DrawingState _drawingState) const; void _prepareDrawTriangle(DrawingState _drawingState); bool _canDraw() const; - void _drawThickLine(u32 _v0, u32 _v1, float _width); + void _drawThickLine(u32 _v0, u32 _v1, float _width, u32 _flag); void _drawOSD(const char *_pText, float _x, float & _y); diff --git a/GLideN64/src/Log.cpp b/GLideN64/src/Log.cpp index 557fc798..94235187 100644 --- a/GLideN64/src/Log.cpp +++ b/GLideN64/src/Log.cpp @@ -13,6 +13,8 @@ #include #include +#ifndef MUPENPLUSAPI // zilmar spec + std::mutex g_logMutex; std::wofstream fileOutput; @@ -113,6 +115,54 @@ void LogDebug(const char* _fileName, int _line, u16 _type, const char* _format, fileOutput.flush(); } +#else // mupen64plus +#include "mupenplus/GLideN64_mupenplus.h" + +void LogDebug(const char* _fileName, int _line, u16 _type, const char* _format, ...) +{ + static const int logLevel[] = { + M64MSG_INFO, + M64MSG_ERROR, + M64MSG_INFO, + M64MSG_WARNING, + M64MSG_VERBOSE, + M64MSG_VERBOSE + }; + + if (CoreDebugCallback == nullptr || + _type > LOG_LEVEL) + { + return; + } + + // initialize use of the variable argument array + va_list vaArgs; + va_start(vaArgs, _format); + + // reliably acquire the size from a copy of + // the variable argument array + // and a functionally reliable call + // to mock the formatting + va_list vaCopy; + va_copy(vaCopy, vaArgs); + const int iLen = std::vsnprintf(NULL, 0, _format, vaCopy); + va_end(vaCopy); + + // return a formatted string without + // risking memory mismanagement + // and without assuming any compiler + // or platform specific behavior + std::vector zc(iLen + 1); + std::vsnprintf(zc.data(), zc.size(), _format, vaArgs); + va_end(vaArgs); + + std::stringstream formatString; + formatString << _fileName << ":" << _line << ", \"" << zc.data() << "\""; + + CoreDebugCallback(CoreDebugCallbackContext, logLevel[_type], formatString.str().c_str()); +} +#endif + #if defined(OS_WINDOWS) && !defined(MINGW) #include "windows/GLideN64_windows.h" void debugPrint(const char * format, ...) { diff --git a/GLideN64/src/MupenPlusPluginAPI.cpp b/GLideN64/src/MupenPlusPluginAPI.cpp index 76f3e5d4..8c7f4743 100644 --- a/GLideN64/src/MupenPlusPluginAPI.cpp +++ b/GLideN64/src/MupenPlusPluginAPI.cpp @@ -36,7 +36,7 @@ EXPORT m64p_error CALL gln64PluginStartup( void (*DebugCallback)(void *, int, const char *) ) { - return api().PluginStartup(CoreLibHandle); + return api().PluginStartup(CoreLibHandle, Context, DebugCallback); } EXPORT m64p_error CALL gln64PluginShutdown(void) diff --git a/GLideN64/src/Neon/3DMathNeon.cpp b/GLideN64/src/Neon/3DMathNeon.cpp index 9d26129a..0ff42a23 100644 --- a/GLideN64/src/Neon/3DMathNeon.cpp +++ b/GLideN64/src/Neon/3DMathNeon.cpp @@ -134,7 +134,7 @@ void InverseTransformVectorNormalize(float src[3], float dst[3], float mtx[4][4] dst[2] = product[2]; } -void InverseTransformVectorNormalize4(float src[4][3], float dst[4][3], float mtx[4][4]) +static void InverseTransformVectorNormalize4(float src[4][3], float dst[4][3], float mtx[4][4]) { // Load mtx float32x4x4_t _mtx = vld4q_f32(mtx[0]); @@ -216,7 +216,7 @@ void InverseTransformVectorNormalize4(float src[4][3], float dst[4][3], float mt dst[3][2] = product.val[3][2]; } -void InverseTransformVectorNormalize7(float src[4][3], float dst[4][3], float mtx[4][4]) +static void InverseTransformVectorNormalize7(float src[4][3], float dst[4][3], float mtx[4][4]) { // Load mtx float32x4x4_t _mtx = vld4q_f32(mtx[0]); diff --git a/GLideN64/src/Neon/gSPNeon.cpp b/GLideN64/src/Neon/gSPNeon.cpp index 9581e183..28cb8ede 100644 --- a/GLideN64/src/Neon/gSPNeon.cpp +++ b/GLideN64/src/Neon/gSPNeon.cpp @@ -140,7 +140,7 @@ void gSPInverseTransformVector_NEON(float vec[3], float mtx[4][4]) vec[2] = _mtx.val[0][2]; // store vec[2] } -void DotProductMax7FullNeon( float v0[3], float v1[7][3], float lights[7][3], float _vtx[3]) +static void DotProductMax7FullNeon( float v0[3], float v1[7][3], float lights[7][3], float _vtx[3]) { // load v1 float32x4x3_t _v10 = vld3q_f32(v1[0]); // load 4x3 mtx interleaved @@ -203,7 +203,7 @@ void DotProductMax7FullNeon( float v0[3], float v1[7][3], float lights[7][3], fl _vtx[2] += d20[0]; } -void DotProductMax4FullNeon( float v0[3], float v1[4][3], float lights[4][3], float vtx[3]) +static void DotProductMax4FullNeon( float v0[3], float v1[4][3], float lights[4][3], float vtx[3]) { float32x4x3_t _v1 = vld3q_f32(v1[0]); // load 4x3 mtx interleaved float32x4x3_t _lights = vld3q_f32(lights[0]); // load 4x3 mtx interleaved diff --git a/GLideN64/src/NoiseTexture.cpp b/GLideN64/src/NoiseTexture.cpp deleted file mode 100644 index 298803bd..00000000 --- a/GLideN64/src/NoiseTexture.cpp +++ /dev/null @@ -1,148 +0,0 @@ -#ifdef MINGW -#define _CRT_RAND_S -#endif - -#include -#include -#include -#include -#include -#include -#include -#include "FrameBuffer.h" -#include "Config.h" -#include "GBI.h" -#include "VI.h" -#include "Textures.h" -#include "NoiseTexture.h" -#include "DisplayWindow.h" -#include "DisplayLoadProgress.h" - -using namespace graphics; - -#define NOISE_TEX_WIDTH 640 -#define NOISE_TEX_HEIGHT 580 - -NoiseTexture g_noiseTexture; - -NoiseTexture::NoiseTexture() - : m_DList(0) - , m_currTex(0) - , m_prevTex(0) -{ - for (u32 i = 0; i < NOISE_TEX_NUM; ++i) - m_pTexture[i] = nullptr; -} - - -inline u32 irand() -{ - static u32 iseed = 1; - iseed *= 0x343fd; - iseed += 0x269ec3; - return ((iseed >> 16) & 0x7fff); -} - - -static -void FillTextureData(u32 _seed, NoiseTexturesData * _pData, u32 _start, u32 _stop) -{ - for (u32 i = _start; i < _stop; ++i) { - auto & vec = _pData->at(i); - const size_t sz = vec.size(); - u32 rand_value(0U); - for (size_t t = 0; t < sz; ++t) { - rand_value = irand(); - vec[t] = rand_value & 0xFF; - } - } -} - - -void NoiseTexture::_fillTextureData() -{ - displayLoadProgress(L"INIT NOISE TEXTURES. PLEASE WAIT..."); - - for (auto& vec : m_texData) - vec.resize(NOISE_TEX_WIDTH * NOISE_TEX_HEIGHT); - - FillTextureData(static_cast(time(nullptr)), &m_texData, 0, static_cast(m_texData.size())); - - displayLoadProgress(L""); -} - - -void NoiseTexture::init() -{ - if (m_texData[0].empty()) - _fillTextureData(); - - for (u32 i = 0; i < NOISE_TEX_NUM; ++i) { - m_pTexture[i] = textureCache().addFrameBufferTexture(textureTarget::TEXTURE_2D); - m_pTexture[i]->format = G_IM_FMT_RGBA; - m_pTexture[i]->clampS = 1; - m_pTexture[i]->clampT = 1; - m_pTexture[i]->frameBufferTexture = CachedTexture::fbOneSample; - m_pTexture[i]->maskS = 0; - m_pTexture[i]->maskT = 0; - m_pTexture[i]->mirrorS = 0; - m_pTexture[i]->mirrorT = 0; - m_pTexture[i]->width = NOISE_TEX_WIDTH; - m_pTexture[i]->height = NOISE_TEX_HEIGHT; - m_pTexture[i]->textureBytes = m_pTexture[i]->width * m_pTexture[i]->height; - - const FramebufferTextureFormats & fbTexFormats = gfxContext.getFramebufferTextureFormats(); - { - Context::InitTextureParams params; - params.handle = m_pTexture[i]->name; - params.textureUnitIndex = textureIndices::NoiseTex; - params.width = m_pTexture[i]->width; - params.height = m_pTexture[i]->height; - params.internalFormat = fbTexFormats.noiseInternalFormat; - params.format = fbTexFormats.noiseFormat; - params.dataType = fbTexFormats.noiseType; - params.data = m_texData[i].data(); - gfxContext.init2DTexture(params); - } - { - Context::TexParameters params; - params.handle = m_pTexture[i]->name; - params.target = textureTarget::TEXTURE_2D; - params.textureUnitIndex = textureIndices::NoiseTex; - params.minFilter = textureParameters::FILTER_NEAREST; - params.magFilter = textureParameters::FILTER_NEAREST; - gfxContext.setTextureParameters(params); - } - } -} - -void NoiseTexture::destroy() -{ - for (u32 i = 0; i < NOISE_TEX_NUM; ++i) { - textureCache().removeFrameBufferTexture(m_pTexture[i]); - m_pTexture[i] = nullptr; - } -} - -void NoiseTexture::update() -{ - if (m_texData[0].empty() || m_DList == dwnd().getBuffersSwapCount()) - return; - - u32 rand_value(0U); - while (m_currTex == m_prevTex) { - rand_value = irand(); - m_currTex = rand_value % NOISE_TEX_NUM; - } - m_prevTex = m_currTex; - if (m_pTexture[m_currTex] == nullptr) - return; - { - Context::BindTextureParameters params; - params.texture = m_pTexture[m_currTex]->name; - params.textureUnitIndex = textureIndices::NoiseTex; - params.target = textureTarget::TEXTURE_2D; - gfxContext.bindTexture(params); - } - m_DList = dwnd().getBuffersSwapCount(); -} diff --git a/GLideN64/src/NoiseTexture.h b/GLideN64/src/NoiseTexture.h deleted file mode 100644 index 47b4c798..00000000 --- a/GLideN64/src/NoiseTexture.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once -#include -#include -#include -#include "Types.h" - -#define NOISE_TEX_NUM 30 - -struct CachedTexture; -typedef std::array, NOISE_TEX_NUM> NoiseTexturesData; - -class NoiseTexture -{ -public: - NoiseTexture(); - - void init(); - void destroy(); - void update(); - -private: - void _fillTextureData(); - - CachedTexture * m_pTexture[NOISE_TEX_NUM]; - u32 m_DList; - u32 m_currTex, m_prevTex; - NoiseTexturesData m_texData; -}; - -extern NoiseTexture g_noiseTexture; diff --git a/GLideN64/src/PluginAPI.h b/GLideN64/src/PluginAPI.h index cc6e222e..896c5a29 100644 --- a/GLideN64/src/PluginAPI.h +++ b/GLideN64/src/PluginAPI.h @@ -76,7 +76,7 @@ class PluginAPI void ResizeVideoOutput(int _Width, int _Height); void ReadScreen2(void * _dest, int * _width, int * _height, int _front); - m64p_error PluginStartup(m64p_dynlib_handle _CoreLibHandle); + m64p_error PluginStartup(m64p_dynlib_handle _CoreLibHandle, void * Context, void (*DebugCallback)(void *, int, const char *)); #ifdef M64P_GLIDENUI m64p_error PluginConfig(); #endif // M64P_GLIDENUI diff --git a/GLideN64/src/TextDrawer.cpp b/GLideN64/src/TextDrawer.cpp index 63cf5973..79aa2bae 100644 --- a/GLideN64/src/TextDrawer.cpp +++ b/GLideN64/src/TextDrawer.cpp @@ -91,16 +91,16 @@ struct Atlas { m_pTexture->mirrorT = 0; m_pTexture->width = w; m_pTexture->height = h; - m_pTexture->textureBytes = m_pTexture->width * m_pTexture->height * fbTexFormats.noiseFormatBytes; + m_pTexture->textureBytes = m_pTexture->width * m_pTexture->height * fbTexFormats.fontFormatBytes; Context::InitTextureParams initParams; initParams.handle = m_pTexture->name; initParams.textureUnitIndex = textureIndices::Tex[0]; initParams.width = w; initParams.height = h; - initParams.internalFormat = fbTexFormats.noiseInternalFormat; - initParams.format = fbTexFormats.noiseFormat; - initParams.dataType = fbTexFormats.noiseType; + initParams.internalFormat = fbTexFormats.fontInternalFormat; + initParams.format = fbTexFormats.fontFormat; + initParams.dataType = fbTexFormats.fontType; gfxContext.init2DTexture(initParams); Context::TexParameters setParams; diff --git a/GLideN64/src/TextureFilterHandler.cpp b/GLideN64/src/TextureFilterHandler.cpp index 5233b85f..aa864df6 100644 --- a/GLideN64/src/TextureFilterHandler.cpp +++ b/GLideN64/src/TextureFilterHandler.cpp @@ -57,6 +57,8 @@ u32 TextureFilterHandler::_getConfigOptions() const options |= LET_TEXARTISTS_FLY; if (config.hotkeys.enabledKeys[Config::HotKey::hkTexDump] != 0 || config.textureFilter.txDump) options |= DUMP_TEX; + if (config.textureFilter.txStrongCRC) + options |= DUMP_STRONG_CRC; if (config.textureFilter.txDeposterize) options |= DEPOSTERIZE; if (config.textureFilter.txEnhancedTextureFileStorage) diff --git a/GLideN64/src/Textures.cpp b/GLideN64/src/Textures.cpp index 1030d4bf..905ff8cc 100644 --- a/GLideN64/src/Textures.cpp +++ b/GLideN64/src/Textures.cpp @@ -1153,7 +1153,7 @@ void TextureCache::_loadBackground(CachedTexture *pTexture) free(pDest); } -bool TextureCache::_loadHiresTexture(u32 _tile, CachedTexture *_pTexture, u64 & _ricecrc) +bool TextureCache::_loadHiresTexture(u32 _tile, CachedTexture *_pTexture, u64 & _ricecrc, u64 & _strongcrc) { if (config.textureFilter.txHiresEnable == 0 || !TFH.isInited()) return false; @@ -1181,8 +1181,8 @@ bool TextureCache::_loadHiresTexture(u32 _tile, CachedTexture *_pTexture, u64 & height--; } else { const gDPTile * pTile = gSP.textureTile[_tile]; - int tile_width = pTile->lrs - pTile->uls + 1; - int tile_height = pTile->lrt - pTile->ult + 1; + int tile_width = ((pTile->lrs - pTile->uls) & 0x03FF) + 1; + int tile_height = ((pTile->lrt - pTile->ult) & 0x03FF) + 1; int mask_width = (pTile->masks == 0) ? (tile_width) : (1 << pTile->masks); int mask_height = (pTile->maskt == 0) ? (tile_height) : (1 << pTile->maskt); @@ -1223,10 +1223,18 @@ bool TextureCache::_loadHiresTexture(u32 _tile, CachedTexture *_pTexture, u64 & } _ricecrc = txfilter_checksum(addr, width, height, _pTexture->size, bpl, paladdr); + if (config.textureFilter.txStrongCRC) + _strongcrc = txfilter_checksum_strong(addr, width, height, _pTexture->size, bpl, paladdr); GHQTexInfo ghqTexInfo; // TODO: fix problem with zero texture dimensions on GLideNHQ side. - if (txfilter_hirestex(_pTexture->crc, _ricecrc, palette, N64FormatSize(_pTexture->format, _pTexture->size), &ghqTexInfo) && - ghqTexInfo.width != 0 && ghqTexInfo.height != 0) { + auto hirestexFound = txfilter_hirestex(_pTexture->crc, _ricecrc, palette, N64FormatSize(_pTexture->format, _pTexture->size), &ghqTexInfo); + if (!hirestexFound) { + // Texture with RiceCRC was not found. Try alternative CRC. + if (_strongcrc == 0U) + _strongcrc = txfilter_checksum_strong(addr, width, height, _pTexture->size, bpl, paladdr); + hirestexFound = txfilter_hirestex(_pTexture->crc, _strongcrc, palette, N64FormatSize(_pTexture->format, _pTexture->size), &ghqTexInfo); + } + if (hirestexFound && ghqTexInfo.width != 0 && ghqTexInfo.height != 0) { ghqTexInfo.format = gfxContext.convertInternalTextureFormat(ghqTexInfo.format); Context::InitTextureParams params; params.handle = _pTexture->name; @@ -1392,7 +1400,8 @@ void doubleTexture(T* pTex, u32 width, u32 height) void TextureCache::_loadFast(u32 _tile, CachedTexture *_pTexture) { u64 ricecrc = 0; - if (_loadHiresTexture(_tile, _pTexture, ricecrc)) + u64 strongcrc = 0; + if (_loadHiresTexture(_tile, _pTexture, ricecrc, strongcrc)) return; s32 mipLevel = 0; @@ -1492,10 +1501,15 @@ void TextureCache::_loadFast(u32 _tile, CachedTexture *_pTexture) config.textureFilter.txHiresEnable != 0 && config.hotkeys.enabledKeys[Config::HotKey::hkTexDump] != 0) || config.textureFilter.txDump) { - txfilter_dmptx((u8*)m_tempTextureHolder.data(), tmptex.width, tmptex.height, - tmptex.width, (u16)u32(glInternalFormat), - N64FormatSize(_pTexture->format, _pTexture->size), - ricecrc); + config.textureFilter.txStrongCRC ? + txfilter_dmptx_strong((u8*)m_tempTextureHolder.data(), tmptex.width, tmptex.height, + tmptex.width, (u16)u32(glInternalFormat), + N64FormatSize(_pTexture->format, _pTexture->size), + strongcrc) : + txfilter_dmptx((u8*)m_tempTextureHolder.data(), tmptex.width, tmptex.height, + tmptex.width, (u16)u32(glInternalFormat), + N64FormatSize(_pTexture->format, _pTexture->size), + ricecrc); } bool bLoaded = false; @@ -1593,7 +1607,8 @@ void TextureCache::_loadFast(u32 _tile, CachedTexture *_pTexture) void TextureCache::_loadAccurate(u32 _tile, CachedTexture *_pTexture) { u64 ricecrc = 0; - if (_loadHiresTexture(_tile, _pTexture, ricecrc)) + u64 strongcrc = 0; + if (_loadHiresTexture(_tile, _pTexture, ricecrc, strongcrc)) return; bool force32bitFormat = false; @@ -1668,10 +1683,15 @@ void TextureCache::_loadAccurate(u32 _tile, CachedTexture *_pTexture) config.textureFilter.txHiresEnable != 0 && config.hotkeys.enabledKeys[Config::HotKey::hkTexDump] != 0) || config.textureFilter.txDump) { - txfilter_dmptx((u8*)(m_tempTextureHolder.data() + texDataOffset), tmptex.width, tmptex.height, - tmptex.width, (u16)u32(glInternalFormat), - N64FormatSize(_pTexture->format, _pTexture->size), - ricecrc); + config.textureFilter.txStrongCRC ? + txfilter_dmptx_strong((u8*)(m_tempTextureHolder.data() + texDataOffset), tmptex.width, tmptex.height, + tmptex.width, (u16)u32(glInternalFormat), + N64FormatSize(_pTexture->format, _pTexture->size), + strongcrc) : + txfilter_dmptx((u8*)(m_tempTextureHolder.data() + texDataOffset), tmptex.width, tmptex.height, + tmptex.width, (u16)u32(glInternalFormat), + N64FormatSize(_pTexture->format, _pTexture->size), + ricecrc); } texDataOffset += tmptex.width * tmptex.height; @@ -1727,7 +1747,12 @@ void TextureCache::_loadAccurate(u32 _tile, CachedTexture *_pTexture) config.textureFilter.txHiresEnable != 0 && config.hotkeys.enabledKeys[Config::HotKey::hkTexDump] != 0) || config.textureFilter.txDump) { - txfilter_dmptx((u8*)m_tempTextureHolder.data(), tmptex.width, tmptex.height, + config.textureFilter.txStrongCRC ? + txfilter_dmptx_strong((u8*)m_tempTextureHolder.data(), tmptex.width, tmptex.height, + tmptex.width, (u16)u32(glInternalFormat), + N64FormatSize(_pTexture->format, _pTexture->size), + strongcrc) : + txfilter_dmptx((u8*)m_tempTextureHolder.data(), tmptex.width, tmptex.height, tmptex.width, (u16)u32(glInternalFormat), N64FormatSize(_pTexture->format, _pTexture->size), ricecrc); diff --git a/GLideN64/src/Textures.h b/GLideN64/src/Textures.h index 2028328c..9a2b8ab2 100644 --- a/GLideN64/src/Textures.h +++ b/GLideN64/src/Textures.h @@ -90,7 +90,7 @@ struct TextureCache CachedTexture * _addTexture(u64 _crc64); void _loadFast(u32 _tile, CachedTexture *_pTexture); void _loadAccurate(u32 _tile, CachedTexture *_pTexture); - bool _loadHiresTexture(u32 _tile, CachedTexture *_pTexture, u64 & _ricecrc); + bool _loadHiresTexture(u32 _tile, CachedTexture *_pTexture, u64 & _ricecrc, u64 & _strongcrc); void _loadBackground(CachedTexture *pTexture); bool _loadHiresBackground(CachedTexture *_pTexture, u64 & _ricecrc); void _loadDepthTexture(CachedTexture * _pTexture, u16* _pDest); diff --git a/GLideN64/src/TxFilterStub.cpp b/GLideN64/src/TxFilterStub.cpp index fea57bc3..f6984a3c 100644 --- a/GLideN64/src/TxFilterStub.cpp +++ b/GLideN64/src/TxFilterStub.cpp @@ -31,12 +31,24 @@ txfilter_checksum(uint8 *src, int width, int height, int size, int rowStride, ui return 0U; } +TAPI uint64 TAPIENTRY +txfilter_checksum_strong(uint8 *src, int width, int height, int size, int rowStride, uint8 *palette) +{ + return 0U; +} + TAPI boolean TAPIENTRY txfilter_dmptx(uint8 *src, int width, int height, int rowStridePixel, uint16 gfmt, N64FormatSize n64FmtSz, Checksum r_crc64) { return 0; } +TAPI boolean TAPIENTRY +txfilter_dmptx_strong(uint8 *src, int width, int height, int rowStridePixel, uint16 gfmt, N64FormatSize n64FmtSz, Checksum r_crc64) +{ + return 0; +} + TAPI boolean TAPIENTRY txfilter_reloadhirestex() { diff --git a/GLideN64/src/VI.cpp b/GLideN64/src/VI.cpp index 27e8012c..cc9f9888 100644 --- a/GLideN64/src/VI.cpp +++ b/GLideN64/src/VI.cpp @@ -15,6 +15,7 @@ #include "DebugDump.h" #include "osal_keys.h" #include "DisplayWindow.h" +#include "TextureFilterHandler.h" #include "GLideNHQ/TxFilterExport.h" #include @@ -132,6 +133,14 @@ static void checkHotkeys() // Turn on texture dump if (osal_is_key_pressed(config.hotkeys.enabledKeys[Config::hkTexDump], 0x0001)) textureCache().toggleDumpTex(); + + if (osal_is_key_pressed(config.hotkeys.enabledKeys[Config::hkStrongCRC], 0x0001)) { + if (config.textureFilter.txStrongCRC == 0) + dwnd().getDrawer().showMessage("Enable strong CRC for textures dump\n", Milliseconds(750)); + else + dwnd().getDrawer().showMessage("Disable strong CRC for textures dump\n", Milliseconds(750)); + config.textureFilter.txStrongCRC = !config.textureFilter.txStrongCRC; + } } if (osal_is_key_pressed(config.hotkeys.enabledKeys[Config::hkTexCoordBounds], 0x0001)) { diff --git a/GLideN64/src/ZilmarGFX_1_3.h b/GLideN64/src/ZilmarGFX_1_3.h index f1cde0f1..12ca9feb 100644 --- a/GLideN64/src/ZilmarGFX_1_3.h +++ b/GLideN64/src/ZilmarGFX_1_3.h @@ -277,6 +277,14 @@ EXPORT void CALL ViWidthChanged (void); ******************************************************************/ EXPORT void CALL ReadScreen (void **dest, long *width, long *height); +/****************************************************************** + Function: DllCrtFree + Purpose: Frees the memory at the specified address with the dll's standard library + Input: none + Output: none + ******************************************************************/ +EXPORT void CALL DllCrtFree(void* addr); + #if defined(__cplusplus) } #endif diff --git a/GLideN64/src/ZilmarPluginAPI.cpp b/GLideN64/src/ZilmarPluginAPI.cpp index 89bd0284..f79aa1a6 100644 --- a/GLideN64/src/ZilmarPluginAPI.cpp +++ b/GLideN64/src/ZilmarPluginAPI.cpp @@ -55,4 +55,9 @@ EXPORT void CALL ReadScreen (void **dest, long *width, long *height) api().ReadScreen(dest, width, height); } +EXPORT void CALL DllCrtFree(void* addr) +{ + free(addr); +} + } diff --git a/GLideN64/src/gDP.cpp b/GLideN64/src/gDP.cpp index 96ae2cdc..adf9d4aa 100644 --- a/GLideN64/src/gDP.cpp +++ b/GLideN64/src/gDP.cpp @@ -441,7 +441,7 @@ bool CheckForFrameBufferTexture(u32 _address, u32 _width, u32 _bytes) break; } - for (u32 nTile = gSP.texture.tile; nTile < 6; ++nTile) { + for (u32 nTile = 0; nTile < 6; ++nTile) { if (gDP.tiles[nTile].tmem == gDP.loadTile->tmem) { gDPTile & curTile = gDP.tiles[nTile]; curTile.textureMode = gDP.loadTile->textureMode; diff --git a/GLideN64/src/gDP.h b/GLideN64/src/gDP.h index b698158a..e118b64d 100644 --- a/GLideN64/src/gDP.h +++ b/GLideN64/src/gDP.h @@ -270,6 +270,7 @@ struct gDPInfo gDPLoadTileInfo loadInfo[512]; gDPTexrectInfo lastTexRectInfo; texCoordBounds m_texCoordBounds; + bool m_subscreen{ false }; }; extern gDPInfo gDP; diff --git a/GLideN64/src/gSP.cpp b/GLideN64/src/gSP.cpp index 07fee242..b22c4957 100644 --- a/GLideN64/src/gSP.cpp +++ b/GLideN64/src/gSP.cpp @@ -314,7 +314,7 @@ void gSPLight( u32 l, s32 n ) Light *light = (Light*)&RDRAM[addrByte]; - if (n < 8) { + if (n < 9) { gSP.lights.rgb[n][R] = _FIXED2FLOATCOLOR(light->r,8); gSP.lights.rgb[n][G] = _FIXED2FLOATCOLOR(light->g,8); gSP.lights.rgb[n][B] = _FIXED2FLOATCOLOR(light->b,8); @@ -326,6 +326,8 @@ void gSPLight( u32 l, s32 n ) gSP.lights.xyz[n][Y] = light->y; gSP.lights.xyz[n][Z] = light->z; + gSP.lights.is_point[n] = 0 != light->type; + Normalize( gSP.lights.xyz[n] ); u32 addrShort = addrByte >> 1; gSP.lights.pos_xyzw[n][X] = (float)(((short*)RDRAM)[(addrShort+4)^1]); @@ -483,6 +485,17 @@ void gSPInverseTransformVector_default(float vec[3], float mtx[4][4]) vec[2] = mtx[2][0] * x + mtx[2][1] * y + mtx[2][2] * z; } +static void processDirectionalLight(const bool useFirstColor, u32 i, SPVertex& vtx) +{ + const f32 intensity = DotProduct( &vtx.nx, gSP.lights.i_xyz[i] ); + if (intensity > 0.0f) { + const float* pColor = useFirstColor ? gSP.lights.rgb[i] : gSP.lights.rgb2[i]; + vtx.r += pColor[R] * intensity; + vtx.g += pColor[G] * intensity; + vtx.b += pColor[B] * intensity; + } +} + template void gSPLightVertexStandard(u32 v, SPVertex * spVtx) { @@ -498,13 +511,7 @@ void gSPLightVertexStandard(u32 v, SPVertex * spVtx) vtx.HWLight = 0; for (u32 i = 0; i < gSP.numLights; ++i) { - const f32 intensity = DotProduct( &vtx.nx, gSP.lights.i_xyz[i] ); - if (intensity > 0.0f) { - pColor = useFirstColor ? gSP.lights.rgb[i] : gSP.lights.rgb2[i]; - vtx.r += pColor[R] * intensity; - vtx.g += pColor[G] * intensity; - vtx.b += pColor[B] * intensity; - } + processDirectionalLight(useFirstColor, i, vtx); } vtx.r = min(1.0f, vtx.r); vtx.g = min(1.0f, vtx.g); @@ -632,10 +639,53 @@ void gSPLightVertex(SPVertex & _vtx) gSPLightVertex<1>(0, &_vtx); } +static void processPointLight(u32 l, const float* vecPos, SPVertex& vtx) +{ + f32 intensity; + if (gSP.lights.ca[l] != 0.0f) { + f32 recip = FIXED2FLOATRECIP16; + // Point lighting + f32 lvec[3] = { gSP.lights.pos_xyzw[l][X], gSP.lights.pos_xyzw[l][Y], gSP.lights.pos_xyzw[l][Z] }; + lvec[0] -= vecPos[0]; + lvec[1] -= vecPos[1]; + lvec[2] -= vecPos[2]; + + const f32 K = lvec[0] * lvec[0] + lvec[1] * lvec[1] + lvec[2] * lvec[2] * 2.0f; + const f32 KS = sqrtf(K); + + gSPInverseTransformVector(lvec, gSP.matrix.modelView[gSP.matrix.modelViewi]); + + for (u32 i = 0; i < 3; ++i) { + lvec[i] = (4.0f * lvec[i] / KS); + if (lvec[i] < -1.0f) + lvec[i] = -1.0f; + if (lvec[i] > 1.0f) + lvec[i] = 1.0f; + } + + f32 V = lvec[0] * vtx.nx + lvec[1] * vtx.ny + lvec[2] * vtx.nz; + if (V < -1.0f) + V = -1.0f; + if (V > 1.0f) + V = 1.0f; + + const f32 KSF = floorf(KS); + const f32 D = (KSF * gSP.lights.la[l] * 2.0f + KSF * KSF * gSP.lights.qa[l] / 8.0f) * recip + 1.0f; + intensity = V / D; + } else { + // Standard lighting + intensity = DotProduct(&vtx.nx, gSP.lights.i_xyz[l]); + } + if (intensity > 0.0f) { + vtx.r += gSP.lights.rgb[l][R] * intensity; + vtx.g += gSP.lights.rgb[l][G] * intensity; + vtx.b += gSP.lights.rgb[l][B] * intensity; + } +} + template void gSPPointLightVertexZeldaMM(u32 v, float _vecPos[VNUM][4], SPVertex * spVtx) { - f32 intensity = 0.0f; for (int j = 0; j < VNUM; ++j) { SPVertex & vtx = spVtx[v + j]; vtx.HWLight = 0; @@ -645,45 +695,7 @@ void gSPPointLightVertexZeldaMM(u32 v, float _vecPos[VNUM][4], SPVertex * spVtx) gSPTransformVector(_vecPos[j], gSP.matrix.modelView[gSP.matrix.modelViewi]); for (u32 l = 0; l < gSP.numLights; ++l) { - if (gSP.lights.ca[l] != 0.0f) { - f32 recip = FIXED2FLOATRECIP16; - // Point lighting - f32 lvec[3] = { gSP.lights.pos_xyzw[l][X], gSP.lights.pos_xyzw[l][Y], gSP.lights.pos_xyzw[l][Z] }; - lvec[0] -= _vecPos[j][0]; - lvec[1] -= _vecPos[j][1]; - lvec[2] -= _vecPos[j][2]; - - const f32 K = lvec[0] * lvec[0] + lvec[1] * lvec[1] + lvec[2] * lvec[2] * 2.0f; - const f32 KS = sqrtf(K); - - gSPInverseTransformVector(lvec, gSP.matrix.modelView[gSP.matrix.modelViewi]); - - for (u32 i = 0; i < 3; ++i) { - lvec[i] = (4.0f * lvec[i] / KS); - if (lvec[i] < -1.0f) - lvec[i] = -1.0f; - if (lvec[i] > 1.0f) - lvec[i] = 1.0f; - } - - f32 V = lvec[0] * vtx.nx + lvec[1] * vtx.ny + lvec[2] * vtx.nz; - if (V < -1.0f) - V = -1.0f; - if (V > 1.0f) - V = 1.0f; - - const f32 KSF = floorf(KS); - const f32 D = (KSF * gSP.lights.la[l] * 2.0f + KSF * KSF * gSP.lights.qa[l] / 8.0f) * recip + 1.0f; - intensity = V / D; - } else { - // Standard lighting - intensity = DotProduct(&vtx.nx, gSP.lights.i_xyz[l]); - } - if (intensity > 0.0f) { - vtx.r += gSP.lights.rgb[l][R] * intensity; - vtx.g += gSP.lights.rgb[l][G] * intensity; - vtx.b += gSP.lights.rgb[l][B] * intensity; - } + processPointLight(l, _vecPos[j], vtx); } if (vtx.r > 1.0f) vtx.r = 1.0f; if (vtx.g > 1.0f) vtx.g = 1.0f; @@ -733,6 +745,30 @@ void gSPPointLightVertexAcclaim(u32 v, SPVertex * spVtx) } } +template +void gSPLightVertexF3DEX3(u32 v, float _vecPos[VNUM][4], SPVertex* spVtx) +{ + for (int j = 0; j < VNUM; ++j) { + const bool useFirstColor = ((v + j) & 1) == 0; + SPVertex& vtx = spVtx[v + j]; + vtx.HWLight = 0; + vtx.r = gSP.lights.rgb[gSP.numLights][R]; + vtx.g = gSP.lights.rgb[gSP.numLights][G]; + vtx.b = gSP.lights.rgb[gSP.numLights][B]; + gSPTransformVector(_vecPos[j], gSP.matrix.modelView[gSP.matrix.modelViewi]); + + for (u32 l = 0; l < gSP.numLights; ++l) { + if (gSP.lights.is_point[l]) + processPointLight(l, _vecPos[j], vtx); + else + processDirectionalLight(useFirstColor, l, vtx); + } + if (vtx.r > 1.0f) vtx.r = 1.0f; + if (vtx.g > 1.0f) vtx.g = 1.0f; + if (vtx.b > 1.0f) vtx.b = 1.0f; + } +} + template void gSPBillboardVertex(u32 v, SPVertex * spVtx) { @@ -825,10 +861,17 @@ void gSPProcessVertex(u32 v, SPVertex * spVtx) gSPClipVertex(v, spVtx); if (gSP.geometryMode & G_LIGHTING) { - if (gSP.geometryMode & G_POINT_LIGHTING) - gSPPointLightVertex(v, vPos, spVtx); + if (GBI.isLegacyVertexPipeline()) + { + if (gSP.geometryMode & G_POINT_LIGHTING) + gSPPointLightVertex(v, vPos, spVtx); + else + gSPLightVertex(v, spVtx); + } else - gSPLightVertex(v, spVtx); + { + gSPLightVertexF3DEX3(v, vPos, spVtx); + } if (gSP.geometryMode & G_ACCLAIM_LIGHTING) gSPPointLightVertexAcclaim(v, spVtx); @@ -1581,6 +1624,16 @@ void gSPSegment( s32 seg, s32 base ) DebugMsg(DEBUG_NORMAL, "gSPSegment( %s, 0x%08X );\n", SegmentText[seg], base ); } +void gSPRelSegment(s32 seg, s32 base) +{ + // extract rel offset like 0x0?123456 + const s32 offset = base & 0x00FFFFFF; + const s32 rel = (base >> 24) & 0xf; + gSP.segment[seg] = offset + gSP.segment[rel]; + + DebugMsg(DEBUG_NORMAL, "gSPRelSegment( %s, 0x%08X ) -> rel=0x%08X;\n", SegmentText[seg], base, gSP.segment[seg]); +} + void gSPClipRatio(u32 ratio) { gSP.clipRatio = std::abs(static_cast(ratio & 0xFFFF)); @@ -1696,7 +1749,7 @@ void gSPLightColor( u32 lightNum, u32 packedColor ) { --lightNum; - if (lightNum < 8) + if (lightNum < 9) { gSP.lights.rgb[lightNum][R] = _FIXED2FLOATCOLOR(_SHIFTR( packedColor, 24, 8 ),8); gSP.lights.rgb[lightNum][G] = _FIXED2FLOATCOLOR(_SHIFTR( packedColor, 16, 8 ),8); @@ -1919,18 +1972,11 @@ void gSPSetOtherMode_L(u32 _length, u32 _shift, u32 _data) DebugMsg(DEBUG_NORMAL, " result: %08x\n", gDP.otherMode.l); } -void gSPLine3D(u32 v0, u32 v1, u32 flag ) -{ - dwnd().getDrawer().drawLine(v0, v1, 1.5f); - - DebugMsg(DEBUG_NORMAL, "gSPLine3D( %i, %i, %i )\n", v0, v1, flag); -} - -void gSPLineW3D(u32 v0, u32 v1, u32 wd, u32 flag ) +void gSPLine3D(u32 v0, u32 v1, s32 wd, u32 flag ) { - dwnd().getDrawer().drawLine(v0, v1, 1.5f + wd * 0.5f); + dwnd().getDrawer().drawLine(v0, v1, 1.5f + wd * 0.5f, flag); - DebugMsg(DEBUG_NORMAL, "gSPLineW3D( %i, %i, %i, %i )\n", v0, v1, wd, flag); + DebugMsg(DEBUG_NORMAL, "gSPLine3D( %i, %i, %i, %i )\n", v0, v1, wd, flag); } void gSPSetStatus(u32 sid, u32 val) diff --git a/GLideN64/src/gSP.h b/GLideN64/src/gSP.h index 7aab7dae..84e50b22 100644 --- a/GLideN64/src/gSP.h +++ b/GLideN64/src/gSP.h @@ -72,6 +72,7 @@ struct gSPInfo f32 i_xyz[12][3]; f32 pos_xyzw[12][4]; f32 ca[12], la[12], qa[12]; + bool is_point[12]; } lights; struct @@ -179,6 +180,7 @@ void gSPCullDisplayList( u32 v0, u32 vn ); void gSPPopMatrix( u32 param ); void gSPPopMatrixN( u32 param, u32 num ); void gSPSegment( s32 seg, s32 base ); +void gSPRelSegment(s32 seg, s32 base); void gSPClipRatio( u32 ratio ); void gSPInsertMatrix( u32 where, u32 num ); void gSPModifyVertex(u32 _vtx, u32 _where, u32 _val ); @@ -193,8 +195,7 @@ void gSPSetGeometryMode( u32 mode ); void gSPClearGeometryMode( u32 mode ); void gSPSetOtherMode_H(u32 _length, u32 _shift, u32 _data); void gSPSetOtherMode_L(u32 _length, u32 _shift, u32 _data); -void gSPLine3D(u32 v0, u32 v1, u32 flag); -void gSPLineW3D( u32 v0, u32 v1, u32 wd, u32 flag ); +void gSPLine3D( u32 v0, u32 v1, s32 wd, u32 flag ); void gSPSetStatus(u32 sid, u32 val); void gSPSetDMAOffsets( u32 mtxoffset, u32 vtxoffset ); void gSPSetDMATexOffset(u32 _addr); diff --git a/GLideN64/src/mupenplus/Config_mupenplus.cpp b/GLideN64/src/mupenplus/Config_mupenplus.cpp index c937a25d..2d9315d6 100644 --- a/GLideN64/src/mupenplus/Config_mupenplus.cpp +++ b/GLideN64/src/mupenplus/Config_mupenplus.cpp @@ -53,6 +53,8 @@ const char* _hotkeyDescription(u32 _idx) return "Hotkey: toggle force gamma correction"; case Config::HotKey::hkInaccurateTexCords: return "Hotkey: toggle inaccurate texture coordinates"; + case Config::HotKey::hkStrongCRC: + return "Hotkey: toggle strong CRC for textures dump"; } return "Unknown hotkey"; } @@ -128,7 +130,7 @@ bool Config_SetDefault() assert(res == M64ERR_SUCCESS); res = ConfigSetDefaultBool(g_configVideoGliden64, "FXAA", config.video.fxaa, "Toggle Fast Approximate Anti-Aliasing (FXAA)."); assert(res == M64ERR_SUCCESS); - res = ConfigSetDefaultInt(g_configVideoGliden64, "AspectRatio", config.frameBufferEmulation.aspect, "Screen aspect ratio. (0=stretch, 1=force 4:3, 2=force 16:9, 3=adjust)"); + res = ConfigSetDefaultInt(g_configVideoGliden64, "AspectRatio", config.frameBufferEmulation.aspect, "Screen aspect ratio. (0=stretch, 1=force 4:3, 2=force 16:9, 3=adjust 4:3, 4=adjust 16:9)"); assert(res == M64ERR_SUCCESS); res = ConfigSetDefaultInt(g_configVideoGliden64, "BufferSwapMode", config.frameBufferEmulation.bufferSwapMode, "Swap frame buffers. (0=On VI update call, 1=On VI origin change, 2=On buffer update)"); assert(res == M64ERR_SUCCESS); @@ -257,6 +259,8 @@ bool Config_SetDefault() assert(res == M64ERR_SUCCESS); res = ConfigSetDefaultBool(g_configVideoGliden64, "txDump", config.textureFilter.txDump, "Dump textures"); assert(res == M64ERR_SUCCESS); + res = ConfigSetDefaultBool(g_configVideoGliden64, "txStrongCRC", config.textureFilter.txStrongCRC, "Use strong CRC for texture dump."); + assert(res == M64ERR_SUCCESS); res = ConfigSetDefaultBool(g_configVideoGliden64, "txEnhancedTextureFileStorage", config.textureFilter.txEnhancedTextureFileStorage, "Use file storage instead of memory cache for enhanced textures."); assert(res == M64ERR_SUCCESS); res = ConfigSetDefaultBool(g_configVideoGliden64, "txHiresTextureFileStorage", config.textureFilter.txHiresTextureFileStorage, "Use file storage instead of memory cache for HD textures."); @@ -465,7 +469,7 @@ void Config_LoadCustomConfig() if (result == M64ERR_SUCCESS) config.textureFilter.txHiresFullAlphaChannel = atoi(value); result = ConfigExternalGetParameter(fileHandle, sectionName, "textureFilter\\txHresAltCRC", value, sizeof(value)); if (result == M64ERR_SUCCESS) config.textureFilter.txHresAltCRC = atoi(value); - result = ConfigExternalGetParameter(fileHandle, sectionName, "textureFilter\\txDump", value, sizeof(value)); + result = ConfigExternalGetParameter(fileHandle, sectionName, "textureFilter\\txForce16bpp", value, sizeof(value)); if (result == M64ERR_SUCCESS) config.textureFilter.txForce16bpp = atoi(value); result = ConfigExternalGetParameter(fileHandle, sectionName, "textureFilter\\txCacheCompression", value, sizeof(value)); if (result == M64ERR_SUCCESS) config.textureFilter.txCacheCompression = atoi(value); @@ -473,6 +477,8 @@ void Config_LoadCustomConfig() if (result == M64ERR_SUCCESS) config.textureFilter.txSaveCache = atoi(value); result = ConfigExternalGetParameter(fileHandle, sectionName, "textureFilter\\txDump", value, sizeof(value)); if (result == M64ERR_SUCCESS) config.textureFilter.txDump = atoi(value); + result = ConfigExternalGetParameter(fileHandle, sectionName, "textureFilter\\txStrongCRC", value, sizeof(value)); + if (result == M64ERR_SUCCESS) config.textureFilter.txStrongCRC = atoi(value); result = ConfigExternalGetParameter(fileHandle, sectionName, "textureFilter\\txEnhancedTextureFileStorage", value, sizeof(value)); if (result == M64ERR_SUCCESS) config.textureFilter.txEnhancedTextureFileStorage = atoi(value); result = ConfigExternalGetParameter(fileHandle, sectionName, "textureFilter\\txHiresTextureFileStorage", value, sizeof(value)); @@ -569,6 +575,7 @@ void Config_LoadConfig() config.textureFilter.txCacheCompression = ConfigGetParamBool(g_configVideoGliden64, "txCacheCompression"); config.textureFilter.txSaveCache = ConfigGetParamBool(g_configVideoGliden64, "txSaveCache"); config.textureFilter.txDump = ConfigGetParamBool(g_configVideoGliden64, "txDump"); + config.textureFilter.txStrongCRC = ConfigGetParamBool(g_configVideoGliden64, "txStrongCRC"); config.textureFilter.txEnhancedTextureFileStorage = ConfigGetParamBool(g_configVideoGliden64, "txEnhancedTextureFileStorage"); config.textureFilter.txHiresTextureFileStorage = ConfigGetParamBool(g_configVideoGliden64, "txHiresTextureFileStorage"); config.textureFilter.txNoTextureFileStorage = ConfigGetParamBool(g_configVideoGliden64, "txNoTextureFileStorage"); diff --git a/GLideN64/src/mupenplus/GLideN64_mupenplus.h b/GLideN64/src/mupenplus/GLideN64_mupenplus.h index 532708cc..3f4620a6 100644 --- a/GLideN64/src/mupenplus/GLideN64_mupenplus.h +++ b/GLideN64/src/mupenplus/GLideN64_mupenplus.h @@ -4,6 +4,7 @@ #include "m64p_common.h" #include "m64p_config.h" #include "m64p_vidext.h" +#include "m64p_frontend.h" #define PLUGIN_VERSION 0x020000 #define VIDEO_PLUGIN_API_VERSION 0x020200 diff --git a/GLideN64/src/mupenplus/MupenPlusAPIImpl.cpp b/GLideN64/src/mupenplus/MupenPlusAPIImpl.cpp index 06e3915e..1f620ac3 100644 --- a/GLideN64/src/mupenplus/MupenPlusAPIImpl.cpp +++ b/GLideN64/src/mupenplus/MupenPlusAPIImpl.cpp @@ -6,7 +6,7 @@ void(*renderCallback)(int) = nullptr; -m64p_error PluginAPI::PluginStartup(m64p_dynlib_handle _CoreLibHandle) +m64p_error PluginAPI::PluginStartup(m64p_dynlib_handle _CoreLibHandle, void* Context, void (*DebugCallback)(void *, int, const char *)) { return M64ERR_SUCCESS; } diff --git a/GLideN64/src/uCodes/F3DDKR.cpp b/GLideN64/src/uCodes/F3DDKR.cpp index e38cfa4d..0bd91909 100644 --- a/GLideN64/src/uCodes/F3DDKR.cpp +++ b/GLideN64/src/uCodes/F3DDKR.cpp @@ -62,7 +62,8 @@ void F3DJFG_DMA_Vtx(u32 w0, u32 w1) void F3DDKR_DMA_Tri(u32 w0, u32 w1) { - gSPDMATriangles( w1, _SHIFTR( w0, 4, 12 ) ); + gSP.texture.on = _SHIFTR(w0, 16, 4); + gSPDMATriangles(w1, _SHIFTR(w0, 20, 4) + 1); gSP.vertexi = 0; } diff --git a/GLideN64/src/uCodes/F3DEX095.cpp b/GLideN64/src/uCodes/F3DEX095.cpp new file mode 100644 index 00000000..047c5f73 --- /dev/null +++ b/GLideN64/src/uCodes/F3DEX095.cpp @@ -0,0 +1,12 @@ +#include "F3D.h" +#include "F3DEX.h" +#include "F3DEX095.h" +#include "gSP.h" +#include "GBI.h" + +// See #2774 for details +void F3DEX095_Init() +{ + F3DEX_Init(); + GBI_SetGBI(G_CULLDL, F3D_CULLDL, F3D_CullDL); +} diff --git a/GLideN64/src/uCodes/F3DEX095.h b/GLideN64/src/uCodes/F3DEX095.h new file mode 100644 index 00000000..3e22270c --- /dev/null +++ b/GLideN64/src/uCodes/F3DEX095.h @@ -0,0 +1,7 @@ +#ifndef F3DEX095_H +#define F3DEX095_H + +void F3DEX095_Init(); + +#endif + diff --git a/GLideN64/src/uCodes/F3DEX3.cpp b/GLideN64/src/uCodes/F3DEX3.cpp new file mode 100644 index 00000000..056c23f1 --- /dev/null +++ b/GLideN64/src/uCodes/F3DEX3.cpp @@ -0,0 +1,294 @@ +#include "GLideN64.h" +#include "F3D.h" +#include "F3DEX.h" +#include "F3DEX2.h" +#include "F3DZEX2.h" +#include "F3DEX3.h" +#include "gSP.h" + +#define F3DEX3_BRANCH_WZ 0x04 + +#define F3DEX3_TRISTRIP 0x08 +#define F3DEX3_TRIFAN 0x09 +#define F3DEX3_LIGHTTORDP 0x0A +#define F3DEX3_RELSEGMENT 0x0B + +#define F3DEX3_G_MW_FX 0x00 +#define F3DEX3_G_MW_LIGHTCOL 0x0A + +#define F3DEX3_G_MV_MMTX 2 + +#define F3DEX3_G_MW_HALFWORD_FLAG 0x8000 + +#define F3DEX3_G_MWO_AO_AMBIENT 0x00 +#define F3DEX3_G_MWO_AO_DIRECTIONAL 0x02 +#define F3DEX3_G_MWO_AO_POINT 0x04 +#define F3DEX3_G_MWO_PERSPNORM 0x06 +#define F3DEX3_G_MWO_FRESNEL_SCALE 0x0C +#define F3DEX3_G_MWO_FRESNEL_OFFSET 0x0E +#define F3DEX3_G_MWO_ATTR_OFFSET_S 0x10 +#define F3DEX3_G_MWO_ATTR_OFFSET_T 0x12 +#define F3DEX3_G_MWO_ATTR_OFFSET_Z 0x14 +#define F3DEX3_G_MWO_ALPHA_COMPARE_CULL 0x16 +#define F3DEX3_G_MWO_NORMALS_MODE 0x18 +#define F3DEX3_G_MWO_LAST_MAT_DL_ADDR 0x1A + +#define F3DEX3_G_MAX_LIGHTS 9 + +struct F3DEX3_Ambient +{ + u8 pad0, b, g, r; + u8 pad1, b2, g2, r2; +}; + +struct F3DEX3_Light +{ + u8 pad0, b, g, r; + u8 pad1, b2, g2, r2; + s8 pad2, z, y, x; + u8 size, pad3[3]; +}; + +// Notice how it looks like F3DEX3_Light but the ending so there are 8 bytes of difference +struct F3DEX3_LookAt +{ + s8 pad, z, y, x; +}; + +struct F3DEX3_LookAtOld +{ + u8 pad0, b, g, r; + u8 pad1, b2, g2, r2; + s8 pad2, z, y, x; +}; + +// TODO: This seems incorrect to me? +static void F3DZEX3_Branch_W(u32 w0, u32 w1) +{ + gSPBranchLessW(gDP.half_1, _SHIFTR(w0, 1, 7), w1); +} + +#define _LIGHT_TO_OFFSET(n) (((n) - 1) * 0x10 + 0x10) /* The + 0x10 skips cam pos and lookat */ + +static void writeLight(int off, u32 w) +{ + if (0 == off) + { + // CameraWorld not supported + } + if (0x8 == off) + { + // gSPLookAt would like to have a F3DEX3_LookAtOld struct, so we need to do some math magic + gSPLookAt(w - (sizeof(F3DEX3_LookAtOld) - sizeof(F3DEX3_LookAt)), 0); + gSPLookAt(w - (sizeof(F3DEX3_LookAtOld) - sizeof(F3DEX3_LookAt)) + sizeof(F3DEX3_LookAt), 1); + } + + for (u32 i = 1; i <= gSP.numLights; i++) + { + if (_LIGHT_TO_OFFSET(i) == off) + { + gSPLight(w, i); + } + } + + if (_LIGHT_TO_OFFSET(gSP.numLights + 1) == off) + { + // TODO: Write ambient lights + } + if ((F3DEX3_G_MAX_LIGHTS * 0x10) + 0x18 == off) + { + // TODO: OcclusionPlane not supported + } +} + +void F3DEX3_MoveMem(u32 w0, u32 w1) +{ + switch (_SHIFTR(w0, 0, 8)) + { + case F3DEX3_G_MV_MMTX: + // TODO: Not supported! + break; + case F3DEX2_MV_VIEWPORT: + gSPViewport(w1); + break; + case G_MV_LIGHT: + { + const u32 ofs = _SHIFTR(w0, 8, 8) * 8; + const u32 len = (1 + _SHIFTR(w0, 19, 5)) * 8; + for (u32 i = 0; i < len; i += 4) + { + writeLight(ofs + i, w1 + i); + } + } + break; + } +} + +void F3DEX3_MoveWord(u32 w0, u32 w1) +{ + switch (_SHIFTR(w0, 16, 8)) + { + case F3DEX3_G_MW_FX: + { + const u32 value = _SHIFTR(w0, 0, 16); + u32 what = w1; + if (value & F3DEX3_G_MW_HALFWORD_FLAG) + what &= 0xffff; + + switch (value & ~F3DEX3_G_MW_HALFWORD_FLAG) + { + case F3DEX3_G_MWO_AO_AMBIENT: + break; + case F3DEX3_G_MWO_AO_DIRECTIONAL: + break; + case F3DEX3_G_MWO_AO_POINT: + break; + case F3DEX3_G_MWO_PERSPNORM: + gSPPerspNormalize(what); + break; + case F3DEX3_G_MWO_FRESNEL_SCALE: + break; + case F3DEX3_G_MWO_FRESNEL_OFFSET: + break; + case F3DEX3_G_MWO_ATTR_OFFSET_S: + break; + case F3DEX3_G_MWO_ATTR_OFFSET_T: + break; + case F3DEX3_G_MWO_ATTR_OFFSET_Z: + break; + case F3DEX3_G_MWO_ALPHA_COMPARE_CULL: + break; + case F3DEX3_G_MWO_NORMALS_MODE: + break; + case F3DEX3_G_MWO_LAST_MAT_DL_ADDR: + break; + } + } + break; + case G_MW_NUMLIGHT: + gSPNumLights(w1 / 0x10); + break; + case G_MW_SEGMENT: + gSPSegment(_SHIFTR(w0, 2, 4), w1 & 0x00FFFFFF); + break; + case G_MW_FOG: + gSPFogFactor((s16)_SHIFTR(w1, 16, 16), (s16)_SHIFTR(w1, 0, 16)); + break; + case G_MW_LIGHTCOL: + int off = _SHIFTR(w0, 0, 16); + gSPLightColor((off / 0x10) + 1, w1); + break; + } +} + +struct Vertices7 +{ + u8 v[7]; + + inline bool valid(u8 i) const + { return v[i] < 64; } +}; + +static inline Vertices7 unpackVertices7(u32 w0, u32 w1) +{ + Vertices7 v; + v.v[0] = _SHIFTR(w0, 17, 7); + v.v[1] = _SHIFTR(w0, 9, 7); + v.v[2] = _SHIFTR(w0, 1, 7); + v.v[3] = _SHIFTR(w1, 25, 7); + v.v[4] = _SHIFTR(w1, 17, 7); + v.v[5] = _SHIFTR(w1, 9, 7); + v.v[6] = _SHIFTR(w1, 1, 7); + return v; +} + +static void F3DEX3_TriStrip(u32 w0, u32 w1) +{ + Vertices7 vertices = unpackVertices7(w0, w1); + // *v1 - v2 - v3, v3 - v2 - v4, v3 - v4 - v5, v5 - v4 - v6, v5 - v6 - v7 + if (!vertices.valid(0) || !vertices.valid(1) || !vertices.valid(2)) return; + gSPTriangle(vertices.v[0], vertices.v[1], vertices.v[2]); + + if (!vertices.valid(3)) return; + gSPTriangle(vertices.v[2], vertices.v[1], vertices.v[3]); + + if (!vertices.valid(4)) return; + gSPTriangle(vertices.v[2], vertices.v[3], vertices.v[4]); + + if (!vertices.valid(5)) return; + gSPTriangle(vertices.v[4], vertices.v[3], vertices.v[5]); + + if (!vertices.valid(6)) return; + gSPTriangle(vertices.v[4], vertices.v[5], vertices.v[6]); +} + +static void F3DEX3_TriFan(u32 w0, u32 w1) +{ + Vertices7 vertices = unpackVertices7(w0, w1); + // *v1 - v2 - v3, v1 - v3 - v4, v1 - v4 - v5, v1 - v5 - v6, v1 - v6 - v7 + if (!vertices.valid(0) || !vertices.valid(1) || !vertices.valid(2)) return; + gSPTriangle(vertices.v[0], vertices.v[1], vertices.v[2]); + + if (!vertices.valid(3)) return; + gSPTriangle(vertices.v[0], vertices.v[2], vertices.v[3]); + + if (!vertices.valid(4)) return; + gSPTriangle(vertices.v[0], vertices.v[3], vertices.v[4]); + + if (!vertices.valid(5)) return; + gSPTriangle(vertices.v[0], vertices.v[3], vertices.v[5]); + + if (!vertices.valid(6)) return; + gSPTriangle(vertices.v[0], vertices.v[5], vertices.v[6]); +} + +static void F3DEX3_LightToRDP(u32 w0, u32 w1) +{ + // unsupported, i do not know what this is for +} + +static void F3DEX3_RelSegment(u32 w0, u32 w1) +{ + gSPRelSegment(_SHIFTR(w0, 2, 4), w1 & 0x00FFFFFF); +} + +void F3DEX3_Init() +{ + gSPSetupFunctions(); + // Set GeometryMode flags + GBI_InitFlags(F3DEX2); + + GBI.PCStackSize = 18; + + // GBI Command Command Value Command Function + GBI_SetGBI(G_RDPHALF_2, F3DEX2_RDPHALF_2, F3D_RDPHalf_2); + GBI_SetGBI(G_SETOTHERMODE_H, F3DEX2_SETOTHERMODE_H, F3DEX2_SetOtherMode_H); + GBI_SetGBI(G_SETOTHERMODE_L, F3DEX2_SETOTHERMODE_L, F3DEX2_SetOtherMode_L); + GBI_SetGBI(G_RDPHALF_1, F3DEX2_RDPHALF_1, F3D_RDPHalf_1); + GBI_SetGBI(G_SPNOOP, F3DEX2_SPNOOP, F3D_SPNoOp); + GBI_SetGBI(G_ENDDL, F3DEX2_ENDDL, F3D_EndDL); + GBI_SetGBI(G_DL, F3DEX2_DL, F3D_DList); + GBI_SetGBI(G_LOAD_UCODE, F3DEX2_LOAD_UCODE, F3DEX_Load_uCode); + GBI_SetGBI(G_MOVEMEM, F3DEX2_MOVEMEM, F3DEX3_MoveMem); + GBI_SetGBI(G_MOVEWORD, F3DEX2_MOVEWORD, F3DEX3_MoveWord); + GBI_SetGBI(G_MTX, F3DEX2_MTX, F3DEX2_Mtx); + GBI_SetGBI(G_GEOMETRYMODE, F3DEX2_GEOMETRYMODE, F3DEX2_GeometryMode); + GBI_SetGBI(G_POPMTX, F3DEX2_POPMTX, F3DEX2_PopMtx); + GBI_SetGBI(G_TEXTURE, F3DEX2_TEXTURE, F3DEX2_Texture); + GBI_SetGBI(G_DMA_IO, F3DEX2_DMA_IO, F3DEX2_DMAIO); + GBI_SetGBI(G_SPECIAL_1, F3DEX2_SPECIAL_1, F3DEX2_Special_1); + GBI_SetGBI(G_SPECIAL_2, F3DEX2_SPECIAL_2, F3DEX2_Special_2); + GBI_SetGBI(G_SPECIAL_3, F3DEX2_SPECIAL_3, F3DEX2_Special_3); + + GBI_SetGBI(G_VTX, F3DEX2_VTX, F3DEX2_Vtx); + GBI_SetGBI(G_MODIFYVTX, F3DEX2_MODIFYVTX, F3DEX_ModifyVtx); + GBI_SetGBI(G_CULLDL, F3DEX2_CULLDL, F3DEX_CullDL); + GBI_SetGBI(G_BRANCH_W, F3DEX3_BRANCH_WZ, F3DZEX3_Branch_W); + GBI_SetGBI(G_TRI1, F3DEX2_TRI1, F3DEX2_Tri1); + GBI_SetGBI(G_TRI2, F3DEX2_TRI2, F3DEX_Tri2); + GBI_SetGBI(G_QUAD, F3DEX2_QUAD, F3DEX2_Quad); + GBI_SetGBI(G_TRISTRIP, F3DEX3_TRISTRIP, F3DEX3_TriStrip); + GBI_SetGBI(G_TRIFAN, F3DEX3_TRIFAN, F3DEX3_TriFan); + GBI_SetGBI(G_LIGHTTORDP, F3DEX3_LIGHTTORDP, F3DEX3_LightToRDP); + GBI_SetGBI(G_RELSEGMENT, F3DEX3_RELSEGMENT, F3DEX3_RelSegment); +} diff --git a/GLideN64/src/uCodes/F3DEX3.h b/GLideN64/src/uCodes/F3DEX3.h new file mode 100644 index 00000000..bf0189f3 --- /dev/null +++ b/GLideN64/src/uCodes/F3DEX3.h @@ -0,0 +1,6 @@ +#ifndef F3DEX3_H +#define F3DEX3_H + +void F3DEX3_Init(); + +#endif // F3DEX3_H diff --git a/GLideN64/src/uCodes/L3D.cpp b/GLideN64/src/uCodes/L3D.cpp index 6fc8e1ff..26100784 100644 --- a/GLideN64/src/uCodes/L3D.cpp +++ b/GLideN64/src/uCodes/L3D.cpp @@ -11,12 +11,34 @@ void L3D_Line3D( u32 w0, u32 w1 ) { - u32 wd = _SHIFTR( w1, 0, 8 ); + s32 wd = static_cast(_SHIFTR( w1, 0, 8 )); + u32 v0 = _SHIFTR(w1, 16, 8) / 10; + u32 v1 = _SHIFTR(w1, 8, 8) / 10; + u32 flag = _SHIFTR(w1, 24, 8); + gSPLine3D( v0, v1, wd, flag == 0 ? v0 : v1 ); +} - if (wd == 0) - gSPLine3D( _SHIFTR( w1, 16, 8 ) / 10, _SHIFTR( w1, 8, 8 ) / 10, _SHIFTR( w1, 24, 8 ) ); - else - gSPLineW3D( _SHIFTR( w1, 16, 8 ) / 10, _SHIFTR( w1, 8, 8 ) / 10, wd, _SHIFTR( w1, 24, 8 ) ); +void L3D_Tri1(u32 w0, u32 w1) +{ + u32 v0 = _SHIFTR(w1, 16, 8) / 10; + u32 v1 = _SHIFTR(w1, 8, 8) / 10; + u32 v2 = _SHIFTR(w0, 0, 8) / 10; + u32 flag = _SHIFTR(w1, 24, 8); + u32 flatShadeVtx = v0; + switch (flag) { + case 0x01: + flatShadeVtx = v1; + break; + case 0x02: + flatShadeVtx = v2; + break; + } + if (v0 != v1) + gSPLine3D(v0, v1, 0, flatShadeVtx); + if (v1 != v2) + gSPLine3D(v1, v2, 0, flatShadeVtx); + if (v2 != v0) + gSPLine3D(v2, v0, 0, flatShadeVtx); } void L3D_Init() @@ -39,7 +61,7 @@ void L3D_Init() GBI_SetGBI( G_RESERVED3, F3D_RESERVED3, F3D_Reserved3 ); GBI_SetGBI( G_SPRITE2D_BASE, F3D_SPRITE2D_BASE, F3D_Sprite2D_Base ); -// GBI_SetGBI( G_TRI1, F3D_TRI1, F3D_Tri1 ); + GBI_SetGBI( G_TRI1, L3D_TRI1, L3D_Tri1 ); GBI_SetGBI( G_CULLDL, F3D_CULLDL, F3D_CullDL ); GBI_SetGBI( G_POPMTX, F3D_POPMTX, F3D_PopMtx ); GBI_SetGBI( G_MOVEWORD, F3D_MOVEWORD, F3D_MoveWord ); diff --git a/GLideN64/src/uCodes/L3D.h b/GLideN64/src/uCodes/L3D.h index 485293b2..afbb2771 100644 --- a/GLideN64/src/uCodes/L3D.h +++ b/GLideN64/src/uCodes/L3D.h @@ -3,6 +3,7 @@ #include "Types.h" #define L3D_LINE3D 0xB5 +#define L3D_TRI1 0xBF void L3D_Line3D( u32 w0, u32 w1 ); void L3D_Init(); diff --git a/GLideN64/src/uCodes/L3DEX.cpp b/GLideN64/src/uCodes/L3DEX.cpp index f8ddf636..b32be7e4 100644 --- a/GLideN64/src/uCodes/L3DEX.cpp +++ b/GLideN64/src/uCodes/L3DEX.cpp @@ -13,12 +13,49 @@ void L3DEX_Line3D( u32 w0, u32 w1 ) { - u32 wd = _SHIFTR( w1, 0, 8 ); + u32 v0 = _SHIFTR(w1, 17, 7); + u32 v1 = _SHIFTR(w1, 9, 7); + s32 wd = static_cast(_SHIFTR( w1, 0, 8 )); + gSPLine3D( v0, v1, wd, v0 ); +} + +void L3DEX_Tri1(u32 w0, u32 w1) +{ + u32 v0 = _SHIFTR(w1, 17, 7); + u32 v1 = _SHIFTR(w1, 9, 7); + u32 v2 = _SHIFTR(w1, 1, 7); + + if (v0 != v1) + gSPLine3D(v0, v1, 0, v0); + if (v1 != v2) + gSPLine3D(v1, v2, 0, v0); + if (v2 != v0) + gSPLine3D(v2, v0, 0, v0); +} + +void L3DEX_Tri2(u32 w0, u32 w1) +{ + u32 v00 = _SHIFTR(w0, 17, 7); + u32 v01 = _SHIFTR(w0, 9, 7); + u32 v02 = _SHIFTR(w0, 1, 7); + + u32 v10 = _SHIFTR(w1, 17, 7); + u32 v11 = _SHIFTR(w1, 9, 7); + u32 v12 = _SHIFTR(w1, 1, 7); + + if (v00 != v01) + gSPLine3D(v00, v01, 0, v00); + if (v01 != v02) + gSPLine3D(v01, v02, 0, v00); + if (v02 != v00) + gSPLine3D(v02, v00, 0, v00); - if (wd == 0) - gSPLine3D( _SHIFTR( w1, 17, 7 ), _SHIFTR( w1, 9, 7 ), 0 ); - else - gSPLineW3D( _SHIFTR( w1, 17, 7 ), _SHIFTR( w1, 9, 7 ), wd, 0 ); + if (v10 != v11) + gSPLine3D(v10, v11, 0, v10); + if (v11 != v12) + gSPLine3D(v11, v12, 0, v10); + if (v12 != v10) + gSPLine3D(v12, v10, 0, v10); } void L3DEX_Init() @@ -41,7 +78,7 @@ void L3DEX_Init() GBI_SetGBI( G_RESERVED3, F3D_RESERVED3, F3D_Reserved3 ); GBI_SetGBI( G_SPRITE2D_BASE, F3D_SPRITE2D_BASE, F3D_Sprite2D_Base ); -// GBI_SetGBI( G_TRI1, F3D_TRI1, F3DEX_Tri1 ); + GBI_SetGBI( G_TRI1, L3DEX_TRI1, L3DEX_Tri1 ); GBI_SetGBI( G_CULLDL, F3D_CULLDL, F3DEX_CullDL ); GBI_SetGBI( G_POPMTX, F3D_POPMTX, F3D_PopMtx ); GBI_SetGBI( G_MOVEWORD, F3D_MOVEWORD, F3D_MoveWord ); @@ -51,11 +88,11 @@ void L3DEX_Init() GBI_SetGBI( G_ENDDL, F3D_ENDDL, F3D_EndDL ); GBI_SetGBI( G_SETGEOMETRYMODE, F3D_SETGEOMETRYMODE, F3D_SetGeometryMode ); GBI_SetGBI( G_CLEARGEOMETRYMODE, F3D_CLEARGEOMETRYMODE, F3D_ClearGeometryMode ); - GBI_SetGBI( G_LINE3D, L3D_LINE3D, L3DEX_Line3D ); + GBI_SetGBI( G_LINE3D, L3DEX_LINE3D, L3DEX_Line3D ); GBI_SetGBI( G_RDPHALF_1, F3D_RDPHALF_1, F3D_RDPHalf_1 ); GBI_SetGBI( G_RDPHALF_2, F3D_RDPHALF_2, F3D_RDPHalf_2 ); GBI_SetGBI( G_MODIFYVTX, F3DEX_MODIFYVTX, F3DEX_ModifyVtx ); -// GBI_SetGBI( G_TRI2, F3DEX_TRI2, F3DEX_Tri2 ); + GBI_SetGBI( G_TRI2, L3DEX_TRI2, L3DEX_Tri2 ); GBI_SetGBI( G_BRANCH_Z, F3DEX_BRANCH_Z, F3DEX_Branch_Z ); GBI_SetGBI( G_LOAD_UCODE, F3DEX_LOAD_UCODE, F3DEX_Load_uCode ); } diff --git a/GLideN64/src/uCodes/L3DEX.h b/GLideN64/src/uCodes/L3DEX.h index ddcfb418..3c728fd2 100644 --- a/GLideN64/src/uCodes/L3DEX.h +++ b/GLideN64/src/uCodes/L3DEX.h @@ -2,6 +2,10 @@ #define L3DEX_H #include "Types.h" +#define L3DEX_TRI1 0xBF +#define L3DEX_TRI2 0xB1 +#define L3DEX_LINE3D 0xB5 + void L3DEX_Line3D( u32 w0, u32 w1 ); void L3DEX_Init(); #endif diff --git a/GLideN64/src/uCodes/L3DEX2.cpp b/GLideN64/src/uCodes/L3DEX2.cpp index eaa01a63..055bd259 100644 --- a/GLideN64/src/uCodes/L3DEX2.cpp +++ b/GLideN64/src/uCodes/L3DEX2.cpp @@ -13,12 +13,50 @@ void L3DEX2_Line3D( u32 w0, u32 w1 ) { - u32 wd = _SHIFTR( (w0 + 1), 0, 8 ); + s32 wd = static_cast(_SHIFTR( w0, 0, 8 )); + u32 v0 = _SHIFTR(w0, 17, 7); + u32 v1 = _SHIFTR(w0, 9, 7); + gSPLine3D( v0, v1, wd, v0 ); +} + +void L3DEX2_Tri1(u32 w0, u32 w1) +{ + s32 wd = static_cast(_SHIFTR(w1, 24, 8)); + u32 v0 = _SHIFTR(w0, 17, 7); + u32 v1 = _SHIFTR(w0, 9, 7); + u32 v2 = _SHIFTR(w0, 1, 7); + + if (v0 != v1) + gSPLine3D(v0, v1, wd, v0); + if (v1 != v2) + gSPLine3D(v1, v2, wd, v0); + if (v2 != v0) + gSPLine3D(v2, v0, wd, v0); +} + +void L3DEX2_Tri2(u32 w0, u32 w1) +{ + s32 wd = static_cast(_SHIFTR(w1, 24, 8)); + u32 v0 = _SHIFTR(w0, 17, 7); + u32 v1 = _SHIFTR(w0, 9, 7); + u32 v2 = _SHIFTR(w0, 1, 7); + u32 v3 = _SHIFTR(w1, 17, 7); + u32 v4 = _SHIFTR(w1, 9, 7); + u32 v5 = _SHIFTR(w1, 1, 7); + + if (v0 != v1) + gSPLine3D(v0, v1, wd, v0); + if (v1 != v2) + gSPLine3D(v1, v2, wd, v0); + if (v2 != v0) + gSPLine3D(v2, v0, wd, v0); - if (wd == 0) - gSPLine3D( _SHIFTR( w0, 17, 7 ), _SHIFTR( w0, 9, 7 ), 0 ); - else - gSPLineW3D( _SHIFTR( w0, 17, 7 ), _SHIFTR( w0, 9, 7 ), wd, 0 ); + if (v3 != v4) + gSPLine3D(v3, v4, wd, v3); + if (v4 != v5) + gSPLine3D(v4, v5, wd, v3); + if (v5 != v3) + gSPLine3D(v5, v3, wd, v3); } void L3DEX2_Init() @@ -54,8 +92,8 @@ void L3DEX2_Init() GBI_SetGBI( G_MODIFYVTX, F3DEX2_MODIFYVTX, F3DEX_ModifyVtx ); GBI_SetGBI( G_CULLDL, F3DEX2_CULLDL, F3DEX_CullDL ); GBI_SetGBI( G_BRANCH_Z, F3DEX2_BRANCH_Z, F3DEX_Branch_Z ); -// GBI_SetGBI( G_TRI1, F3DEX2_TRI1, F3DEX2_Tri1 ); -// GBI_SetGBI( G_TRI2, F3DEX2_TRI2, F3DEX_Tri2 ); + GBI_SetGBI( G_TRI1, L3DEX2_TRI1, L3DEX2_Tri1 ); + GBI_SetGBI( G_TRI2, L3DEX2_TRI2, L3DEX2_Tri2 ); // GBI_SetGBI( G_QUAD, F3DEX2_QUAD, F3DEX2_Quad ); GBI_SetGBI( G_LINE3D, L3DEX2_LINE3D, L3DEX2_Line3D ); } diff --git a/GLideN64/src/uCodes/L3DEX2.h b/GLideN64/src/uCodes/L3DEX2.h index b957efef..1fd29858 100644 --- a/GLideN64/src/uCodes/L3DEX2.h +++ b/GLideN64/src/uCodes/L3DEX2.h @@ -2,9 +2,10 @@ #define L3DEX2_H #include "Types.h" -#define L3DEX2_LINE3D 0x08 +#define L3DEX2_TRI1 0x05 +#define L3DEX2_TRI2 0x06 +#define L3DEX2_LINE3D 0x08 -void L3DEX2_Line3D( u32 w0, u32 w1 ); void L3DEX2_Init(); #endif diff --git a/GLideN64/src/windows/ScreenShot.cpp b/GLideN64/src/windows/ScreenShot.cpp index dfced835..c8201e59 100644 --- a/GLideN64/src/windows/ScreenShot.cpp +++ b/GLideN64/src/windows/ScreenShot.cpp @@ -81,7 +81,8 @@ void SaveScreenshot(const wchar_t * _folder, const char * _name, int _width, int const wchar_t * fileExt = L"png"; std::wstring folder = _folder; - if (folder.size() > 1 && folder[folder.size() - 1] == L'\\') folder.resize(folder.size() - 1); + if (folder.size() > 1 && (folder[folder.size() - 1] == L'\\' || folder[folder.size() - 1] == L'/')) + folder.resize(folder.size() - 1); WIN32_FIND_DATA FindData = { 0 }; HANDLE hFindFile = FindFirstFile(folder.c_str(), &FindData); // Find anything From fe4a92ff9bd60e80adcc12a0d98fd19758151517 Mon Sep 17 00:00:00 2001 From: M4xw Date: Tue, 15 Oct 2024 14:33:02 +0200 Subject: [PATCH 23/58] Fixes after GLideN64 rebase Note: Removal of NoiseTex --- GLideN64/src/Log.cpp | 11 ++++------- Makefile.common | 3 ++- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/GLideN64/src/Log.cpp b/GLideN64/src/Log.cpp index 94235187..93589862 100644 --- a/GLideN64/src/Log.cpp +++ b/GLideN64/src/Log.cpp @@ -116,7 +116,9 @@ void LogDebug(const char* _fileName, int _line, u16 _type, const char* _format, } #else // mupen64plus +#include #include "mupenplus/GLideN64_mupenplus.h" +extern retro_log_printf_t log_cb; void LogDebug(const char* _fileName, int _line, u16 _type, const char* _format, ...) { @@ -129,12 +131,6 @@ void LogDebug(const char* _fileName, int _line, u16 _type, const char* _format, M64MSG_VERBOSE }; - if (CoreDebugCallback == nullptr || - _type > LOG_LEVEL) - { - return; - } - // initialize use of the variable argument array va_list vaArgs; va_start(vaArgs, _format); @@ -159,7 +155,8 @@ void LogDebug(const char* _fileName, int _line, u16 _type, const char* _format, std::stringstream formatString; formatString << _fileName << ":" << _line << ", \"" << zc.data() << "\""; - CoreDebugCallback(CoreDebugCallbackContext, logLevel[_type], formatString.str().c_str()); + //CoreDebugCallback(CoreDebugCallbackContext, logLevel[_type], formatString.str().c_str()); + log_cb(RETRO_LOG_INFO, formatString.str().c_str()); } #endif diff --git a/Makefile.common b/Makefile.common index 75b68aac..72aef3ab 100644 --- a/Makefile.common +++ b/Makefile.common @@ -291,7 +291,6 @@ SOURCES_CXX += \ $(VIDEODIR_GLIDEN64)/src/VI.cpp \ $(VIDEODIR_GLIDEN64)/src/ZlutTexture.cpp \ $(VIDEODIR_GLIDEN64)/src/common/CommonAPIImpl_common.cpp \ - $(VIDEODIR_GLIDEN64)/src/NoiseTexture.cpp \ $(VIDEODIR_GLIDEN64)/src/DepthBufferRender/ClipPolygon.cpp \ $(VIDEODIR_GLIDEN64)/src/DepthBufferRender/DepthBufferRender.cpp \ $(VIDEODIR_GLIDEN64)/src/BufferCopy/BlueNoiseTexture.cpp \ @@ -345,6 +344,8 @@ SOURCES_CXX += \ $(VIDEODIR_GLIDEN64)/src/uCodes/F3DDKR.cpp \ $(VIDEODIR_GLIDEN64)/src/uCodes/F3DEX.cpp \ $(VIDEODIR_GLIDEN64)/src/uCodes/F3DEX2.cpp \ + $(VIDEODIR_GLIDEN64)/src/uCodes/F3DEX3.cpp \ + $(VIDEODIR_GLIDEN64)/src/uCodes/F3DEX095.cpp \ $(VIDEODIR_GLIDEN64)/src/uCodes/F3DEX2ACCLAIM.cpp \ $(VIDEODIR_GLIDEN64)/src/uCodes/F3DEX2CBFD.cpp \ $(VIDEODIR_GLIDEN64)/src/uCodes/F3DZEX2.cpp \ From 72602d781f612c4ac59b2fd911a7a370d2c526af Mon Sep 17 00:00:00 2001 From: M4xw Date: Tue, 15 Oct 2024 14:38:30 +0200 Subject: [PATCH 24/58] [GLideN64]: Update INI --- custom/GLideN64/GLideN64.custom.ini.h | 1 + 1 file changed, 1 insertion(+) diff --git a/custom/GLideN64/GLideN64.custom.ini.h b/custom/GLideN64/GLideN64.custom.ini.h index 2159b643..1af023bf 100644 --- a/custom/GLideN64/GLideN64.custom.ini.h +++ b/custom/GLideN64/GLideN64.custom.ini.h @@ -185,6 +185,7 @@ char customini[] = "Good_Name=Mario Kart 64 (E)(J)(U)\n" "graphics2D\\enableNativeResTexrects=1\n" "graphics2D\\enableTexCoordBounds=1\n" +"frameBufferEmulation\\copyToRDRAM=2\n" "\n" "[MARIO%20STORY]\n" "Good_Name=Mario Story (J)\n" From c7009fc91974d19eca8ada30f29c03ebbac86123 Mon Sep 17 00:00:00 2001 From: Eric Warmenhoven Date: Thu, 22 Aug 2024 22:31:40 -0400 Subject: [PATCH 25/58] Fix gliden64 reinitialization on second playthrough --- libretro/libretro.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libretro/libretro.c b/libretro/libretro.c index 1938b75a..e960b564 100644 --- a/libretro/libretro.c +++ b/libretro/libretro.c @@ -135,6 +135,8 @@ static bool first_context_reset = false; static bool initializing = true; static bool load_game_successful = false; +static bool context_setup_first_init = false; + bool libretro_swap_buffer; uint32_t *blitter_buf = NULL; @@ -1781,16 +1783,14 @@ static void format_saved_memory(void) void context_reset(void) { - static bool first_init = true; - if(current_rdp_type == RDP_PLUGIN_GLIDEN64) { log_cb(RETRO_LOG_DEBUG, CORE_NAME ": context_reset()\n"); glsm_ctl(GLSM_CTL_STATE_CONTEXT_RESET, NULL); - if (first_init) + if (!context_setup_first_init) { glsm_ctl(GLSM_CTL_STATE_SETUP, NULL); - first_init = false; + context_setup_first_init = true; } } @@ -2006,6 +2006,7 @@ void retro_unload_game(void) cleanup_global_paths(); emu_initialized = false; + context_setup_first_init = false; // Reset savestate job var retro_savestate_complete = false; From 4249e39b2c200e5f0895385f76d99928785f2bea Mon Sep 17 00:00:00 2001 From: M4xw Date: Tue, 29 Oct 2024 12:09:29 +0100 Subject: [PATCH 26/58] git subrepo push GLideN64 subrepo: subdir: "GLideN64" merged: "50604252b" upstream: origin: "git@github.com:libretro/GLideN64.git" branch: "develop" commit: "50604252b" git-subrepo: version: "0.4.6" origin: "???" commit: "???" --- GLideN64/.gitrepo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/GLideN64/.gitrepo b/GLideN64/.gitrepo index 69bb6f54..ff7c1bfa 100644 --- a/GLideN64/.gitrepo +++ b/GLideN64/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = git@github.com:libretro/GLideN64.git branch = develop - commit = ff0793c9c4bec49a1ae40e727c69f9d2a33e61ed - parent = c2f6acfe3b7b07ab86c3e4cd89f61a9911191793 + commit = 50604252bfc9fc0151d30cc72f267c3b4a9876b8 + parent = c7009fc91974d19eca8ada30f29c03ebbac86123 method = rebase cmdver = 0.4.6 From df0ba2d21d039c4ab6378e9d44fd509f7cab7e24 Mon Sep 17 00:00:00 2001 From: M4xw Date: Tue, 21 Jan 2025 12:17:09 +0100 Subject: [PATCH 27/58] git subrepo clone --force --branch=develop ../GLideN64 GLideN64/ subrepo: subdir: "GLideN64" merged: "b33fa191b" upstream: origin: "../GLideN64" branch: "develop" commit: "b33fa191b" git-subrepo: version: "0.4.6" origin: "https://github.com/ingydotnet/git-subrepo.git" commit: "110b9eb" --- GLideN64/.gitignore | 1 + GLideN64/.gitrepo | 4 +- GLideN64/ini/GLideN64.custom.ini | 2 +- GLideN64/projects/msvc/GLideN64.sln | 2 + GLideN64/src/CMakeLists.txt | 4 +- GLideN64/src/GBI.cpp | 75 +- GLideN64/src/GLideNHQ/CMakeLists.txt | 4 +- GLideN64/src/GLideNHQ/lib/libz.a | Bin 137782 -> 91136 bytes GLideN64/src/GLideNUI-wtl/GLideNUI.cpp | 15 +- GLideN64/src/GLideNUI-wtl/GLideNUI.h | 8 +- GLideN64/src/GLideNUI/ConfigDialog.cpp | 20 +- GLideN64/src/Textures.cpp | 56 +- GLideN64/src/gSP.cpp | 15 +- GLideN64/src/mupenplus/Config_mupenplus.cpp | 2 + GLideN64/src/osal/CMakeLists.txt | 4 +- GLideN64/src/uCodes/F3DEX3.cpp | 22 +- GLideN64/src/xxHash/xxhash.h | 5416 ++++++++++++++----- 17 files changed, 4061 insertions(+), 1589 deletions(-) diff --git a/GLideN64/.gitignore b/GLideN64/.gitignore index c2542132..e78c9a18 100644 --- a/GLideN64/.gitignore +++ b/GLideN64/.gitignore @@ -22,4 +22,5 @@ src/Revision.h /projects/cmake/mupen64plus-video-GLideN64_autogen/ .vs /translations/wtl +.DS_Store .vscode/ipch/* diff --git a/GLideN64/.gitrepo b/GLideN64/.gitrepo index ff7c1bfa..e2e5c481 100644 --- a/GLideN64/.gitrepo +++ b/GLideN64/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = git@github.com:libretro/GLideN64.git branch = develop - commit = 50604252bfc9fc0151d30cc72f267c3b4a9876b8 - parent = c7009fc91974d19eca8ada30f29c03ebbac86123 + commit = b33fa191b823600252213a50e6d1f1e2353dcccf + parent = 4249e39b2c200e5f0895385f76d99928785f2bea method = rebase cmdver = 0.4.6 diff --git a/GLideN64/ini/GLideN64.custom.ini b/GLideN64/ini/GLideN64.custom.ini index f5e5cc9b..6157ef57 100644 --- a/GLideN64/ini/GLideN64.custom.ini +++ b/GLideN64/ini/GLideN64.custom.ini @@ -18,7 +18,7 @@ generalEmulation\enableLegacyBlending=0 Good_Name=Bio F.R.E.A.K.S. (E)(U) frameBufferEmulation\copyToRDRAM=1 -[BioHazard%20II] +[BIOHAZARD%20II] Good_Name=Biohazard 2 (J) frameBufferEmulation\copyFromRDRAM=1 frameBufferEmulation\copyToRDRAM=0 diff --git a/GLideN64/projects/msvc/GLideN64.sln b/GLideN64/projects/msvc/GLideN64.sln index dee72972..ca71efa4 100644 --- a/GLideN64/projects/msvc/GLideN64.sln +++ b/GLideN64/projects/msvc/GLideN64.sln @@ -4,7 +4,9 @@ VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GLideN64", "GLideN64.vcxproj", "{37D31D7F-C4E7-45B0-AEF6-D6824A243CF7}" ProjectSection(ProjectDependencies) = postProject + {37CAB375-A7A6-3CAB-BD56-0E954D3FD2FE} = {37CAB375-A7A6-3CAB-BD56-0E954D3FD2FE} {7BF6F100-31DB-44AE-A2A5-5DDEED9A909C} = {7BF6F100-31DB-44AE-A2A5-5DDEED9A909C} + {9E05B70F-A294-44A9-A151-B2CC95AA884E} = {9E05B70F-A294-44A9-A151-B2CC95AA884E} {DA965BCF-2219-47AF-ACE7-EAF76D5D4756} = {DA965BCF-2219-47AF-ACE7-EAF76D5D4756} EndProjectSection EndProject diff --git a/GLideN64/src/CMakeLists.txt b/GLideN64/src/CMakeLists.txt index a49783cc..e6b65908 100644 --- a/GLideN64/src/CMakeLists.txt +++ b/GLideN64/src/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.9) +cmake_minimum_required(VERSION 3.10) option(EGL "Set to ON if targeting an EGL device" ${EGL}) option(PANDORA "Set to ON if targeting an OpenPandora" ${PANDORA}) @@ -522,7 +522,7 @@ else(EGL) endif(NOT OPENGL_FOUND) endif(EGL) -if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") +if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") #check for compiler version execute_process(COMMAND ${CMAKE_CXX_COMPILER} -dumpversion OUTPUT_VARIABLE G++_VERSION) if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND G++_VERSION VERSION_LESS 4.8) diff --git a/GLideN64/src/GBI.cpp b/GLideN64/src/GBI.cpp index 3313627f..8f733d41 100644 --- a/GLideN64/src/GBI.cpp +++ b/GLideN64/src/GBI.cpp @@ -52,48 +52,50 @@ struct SpecialMicrocodeInfo bool NoN; // ucode does not use near clipping bool negativeY; // Y is inverted bool fast3DPerspNorm; // ucode is from Fast3D family and has G_PERSPNORMALIZE. See #1303 + bool legacyVertexPipeline; u32 crc; }; static const std::vector specialMicrocodes = { - { S2DEX2, false, true, false, 0x02c399dd }, // Animal Forest - { F3DEX095, false, false, true, 0x0ace4c3f }, // Mario Kart 64 - { F3D, true, false, false, 0x16c3a775 }, // AeroFighters - { F3DEX2CBFD, true, true, false, 0x1b4ace88 }, // Conker's Bad Fur Day - { F3DPD, true, true, false, 0x1c4f7869 }, // Perfect Dark - { F3D, false, false, true, 0x1f24cc84 }, // Wayne Gretzky's 3D Hockey (U) - { F5Indi_Naboo, false, false, false, 0x23fef05f }, // SW Ep.1 Battle for Naboo - { Turbo3D, false, true, false, 0x2bdcfc8a }, // Dark Rift, Turbo3D - { F3DSETA, false, true, true, 0x2edee7be }, // RSP SW Version: 2.0D, 04-01-96 - { F3DGOLDEN, true, true, false, 0x302bca09 }, // RSP SW Version: 2.0G, 09-30-96 GoldenEye - { F3D, false, false, false, 0x4AED6B3B }, // Vivid Dolls [ALECK64] - { F3D, true, true, true, 0x54c558ba }, // RSP SW Version: 2.0D, 04-01-96 Pilot Wings, Blast Corps - { ZSortBOSS, false, false, false, 0x553538cc }, // World Driver Championship - { F3D, false, false, true, 0x55be9bad }, // RSP SW Version: 2.0D, 04-01-96, Mischief Makers, Mortal Combat Trilogy, J.League Live - { F3DEX, true, true, true, 0x637b4b58 }, // RSP SW Version: 2.0D, 04-01-96 Power League - { F5Indi_Naboo, false, false, false, 0x6859bf8e }, // Indiana Jones - { F3D, false, false, true, 0x6932365f }, // Super Mario 64 - { ZSortBOSS, false, false, false, 0x6a76f8dd }, // Stunt Racer - { F3DDKR, false, true, true, 0x6e6fc893 }, // Diddy Kong Racing - { ZSortBOSS, false, false, false, 0x75ed44cc }, // World Driver Championship, European - { F3D, true, false, true, 0x77195a68 }, // Dark Rift - { L3D, true, true, true, 0x771ce0c4 }, // RSP SW Version: 2.0D, 04-01-96 Blast Corps - { F3D, false, false, false, 0x7d372819 }, // Pachinko nichi 365 - { F3DDKR, false, true, true, 0x8d91244f }, // Diddy Kong Racing - { F3DBETA, false, true, true, 0x94c4c833 }, // Star Wars Shadows of Empire - { S2DEX_1_05, false, true, false, 0x9df31081 }, // RSP Gfx ucode S2DEX 1.06 Yoshitaka Yasumoto Nintendo - { T3DUX, false, true, false, 0xbad437f2 }, // T3DUX vers 0.83 for Toukon Road - { F3DJFG, false, true, true, 0xbde9d1fb }, // Jet Force Gemini, Mickey - { T3DUX, false, true, false, 0xd0a1aa3d }, // T3DUX vers 0.85 for Toukon Road 2 - { F3DBETA, false, true, true, 0xd17906e2 }, // RSP SW Version: 2.0D, 04-01-96, Wave Race (U) - { F3DZEX2MM, true, true, false, 0xd39a0d4f }, // Animal Forest - { F3D, false, false, true, 0xd3ab59b2 }, // Cruise'n USA - { F5Rogue, false, false, false, 0xda51ccdb }, // Star Wars RS - { F3D, false, false, false, 0xe01e14be }, // Eikou no Saint Andrews - { F3DEX2ACCLAIM,true, true, false, 0xe44df568 }, // Acclaim games: Turok2 & 3, Armories and South park - { F3D, false, true, false, 0xe62a706d }, // Fast3D + { S2DEX2, false, true, false, true, 0x02c399dd }, // Animal Forest + { F3DEX095, false, false, true, true, 0x0ace4c3f }, // Mario Kart 64 + { F3D, true, false, false, true, 0x16c3a775 }, // AeroFighters + { F3DEX2CBFD, true, true, false, true, 0x1b4ace88 }, // Conker's Bad Fur Day + { F3DPD, true, true, false, true, 0x1c4f7869 }, // Perfect Dark + { F3D, false, false, true, true, 0x1f24cc84 }, // Wayne Gretzky's 3D Hockey (U) + { F5Indi_Naboo, false, false, false, true, 0x23fef05f }, // SW Ep.1 Battle for Naboo + { Turbo3D, false, true, false, true, 0x2bdcfc8a }, // Dark Rift, Turbo3D + { F3DSETA, false, true, true, true, 0x2edee7be }, // RSP SW Version: 2.0D, 04-01-96 + { F3DGOLDEN, true, true, false, true, 0x302bca09 }, // RSP SW Version: 2.0G, 09-30-96 GoldenEye + { F3D, false, false, false, true, 0x4AED6B3B }, // Vivid Dolls [ALECK64] + { F3D, true, true, true, true, 0x54c558ba }, // RSP SW Version: 2.0D, 04-01-96 Pilot Wings, Blast Corps + { ZSortBOSS, false, false, false, true, 0x553538cc }, // World Driver Championship + { F3D, false, false, true, true, 0x55be9bad }, // RSP SW Version: 2.0D, 04-01-96, Mischief Makers, Mortal Combat Trilogy, J.League Live + { F3DEX, true, true, true, true, 0x637b4b58 }, // RSP SW Version: 2.0D, 04-01-96 Power League + { F5Indi_Naboo, false, false, false, true, 0x6859bf8e }, // Indiana Jones + { F3D, false, false, true, true, 0x6932365f }, // Super Mario 64 + { ZSortBOSS, false, false, false, true, 0x6a76f8dd }, // Stunt Racer + { F3DDKR, false, true, true, true, 0x6e6fc893 }, // Diddy Kong Racing + { ZSortBOSS, false, false, false, true, 0x75ed44cc }, // World Driver Championship, European + { F3D, true, false, true, true, 0x77195a68 }, // Dark Rift + { L3D, true, true, true, true, 0x771ce0c4 }, // RSP SW Version: 2.0D, 04-01-96 Blast Corps + { F3D, false, false, false, true, 0x7d372819 }, // Pachinko nichi 365 + { F3DDKR, false, true, true, true, 0x8d91244f }, // Diddy Kong Racing + { F3DBETA, false, true, true, true, 0x94c4c833 }, // Star Wars Shadows of Empire + { S2DEX_1_05, false, true, false, true, 0x9df31081 }, // RSP Gfx ucode S2DEX 1.06 Yoshitaka Yasumoto Nintendo + { T3DUX, false, true, false, true, 0xbad437f2 }, // T3DUX vers 0.83 for Toukon Road + { F3DJFG, false, true, true, true, 0xbde9d1fb }, // Jet Force Gemini, Mickey + { T3DUX, false, true, false, true, 0xd0a1aa3d }, // T3DUX vers 0.85 for Toukon Road 2 + { F3DBETA, false, true, true, true, 0xd17906e2 }, // RSP SW Version: 2.0D, 04-01-96, Wave Race (U) + { F3DZEX2MM, true, true, false, true, 0xd39a0d4f }, // Animal Forest + { F3D, false, false, true, true, 0xd3ab59b2 }, // Cruise'n USA + { F5Rogue, false, false, false, true, 0xda51ccdb }, // Star Wars RS + { F3D, false, false, false, true, 0xe01e14be }, // Eikou no Saint Andrews + { F3DEX2ACCLAIM,true, true, false, true, 0xe44df568 }, // Acclaim games: Turok2 & 3, Armories and South park + { F3D, false, true, false, true, 0xe62a706d }, // Fast3D + { F3D, false, true, false, false, 0xfff0637d }, // Caribbean Nights }; u32 G_RDPHALF_1, G_RDPHALF_2, G_RDPHALF_CONT; @@ -403,6 +405,7 @@ void GBIInfo::loadMicrocode(u32 uc_start, u32 uc_dstart, u16 uc_dsize) current.NoN = info.NoN; current.negativeY = info.negativeY; current.fast3DPersp = info.fast3DPerspNorm; + current.f3dex3.legacyVertexPipeline = info.legacyVertexPipeline; LOG(LOG_VERBOSE, "Load microcode type: %d crc: 0x%08x romname: %s", current.type, uc_crc, RSP.romname); _makeCurrent(¤t); return; diff --git a/GLideN64/src/GLideNHQ/CMakeLists.txt b/GLideN64/src/GLideNHQ/CMakeLists.txt index 8cf33276..7376ce1e 100644 --- a/GLideN64/src/GLideNHQ/CMakeLists.txt +++ b/GLideN64/src/GLideNHQ/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.9) +cmake_minimum_required(VERSION 3.10) project( GLideNHQ ) @@ -65,7 +65,7 @@ if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") add_definitions( -D__MSC__) endif() -if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") +if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") SET(GCC_CPP11_COMPILE_FLAGS "-std=c++0x -Wno-unused-result") if ( NOT MINGW ) SET ( PIC_FLAGS "-fPIC" ) diff --git a/GLideN64/src/GLideNHQ/lib/libz.a b/GLideN64/src/GLideNHQ/lib/libz.a index dd30f7919208a903ac6d86b8ed57a48f31ad0ba7..98a63cca48db37ad745fafe9cf630483a7640499 100644 GIT binary patch literal 91136 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 literal 137782 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 diff --git a/GLideN64/src/GLideNUI-wtl/GLideNUI.cpp b/GLideN64/src/GLideNUI-wtl/GLideNUI.cpp index b7f3a2fd..f1943f46 100644 --- a/GLideN64/src/GLideNUI-wtl/GLideNUI.cpp +++ b/GLideN64/src/GLideNUI-wtl/GLideNUI.cpp @@ -15,8 +15,7 @@ Q_IMPORT_PLUGIN(QICOPlugin) //#define RUN_DIALOG_IN_THREAD -static -int openConfigDialog(const wchar_t * _strFileName, const char * _romName, unsigned int _maxMSAALevel, unsigned int _maxAnisotropy, bool & _accepted) +static int openConfigDialog(const wchar_t * _strFileName, const wchar_t * _strSharedFileName, const char * _romName, unsigned int _maxMSAALevel, unsigned int _maxAnisotropy, bool & _accepted) { std::string IniFolder; uint32_t slength = WideCharToMultiByte(CP_ACP, 0, _strFileName, -1, NULL, 0, NULL, NULL); @@ -40,22 +39,22 @@ int openConfigDialog(const wchar_t * _strFileName, const char * _romName, unsign return 0; } -bool runConfigThread(const wchar_t * _strFileName, const char * _romName, unsigned int _maxMSAALevel, unsigned int _maxAnisotropy) +bool runConfigThread(const wchar_t * _strFileName, const wchar_t * _strSharedFileName, const char * _romName, unsigned int _maxMSAALevel, unsigned int _maxAnisotropy) { bool accepted = false; #ifdef RUN_DIALOG_IN_THREAD std::thread configThread(openConfigDialog, _strFileName, _maxMSAALevel, std::ref(accepted)); configThread.join(); #else - openConfigDialog(_strFileName, _romName, _maxMSAALevel, _maxAnisotropy, accepted); + openConfigDialog(_strFileName, _strSharedFileName, _romName, _maxMSAALevel, _maxAnisotropy, accepted); #endif return accepted; } -EXPORT bool CALL RunConfig(const wchar_t * _strFileName, const char * _romName, unsigned int _maxMSAALevel, unsigned int _maxAnisotropy) +EXPORT bool CALL RunConfig(const wchar_t * _strFileName, const wchar_t * _strSharedFileName, const char * _romName, unsigned int _maxMSAALevel, unsigned int _maxAnisotropy) { - return runConfigThread(_strFileName, _romName, _maxMSAALevel, _maxAnisotropy); + return runConfigThread(_strFileName, _strSharedFileName, _romName, _maxMSAALevel, _maxAnisotropy); } EXPORT int CALL RunAbout(const wchar_t * _strFileName) @@ -73,7 +72,7 @@ EXPORT int CALL RunAbout(const wchar_t * _strFileName) return 0; } -EXPORT void CALL LoadConfig(const wchar_t * _strFileName) +EXPORT void CALL LoadConfig(const wchar_t * _strFileName, const wchar_t * _strSharedFileName) { std::string IniFolder; uint32_t slength = WideCharToMultiByte(CP_ACP, 0, _strFileName, -1, NULL, 0, NULL, NULL); @@ -84,7 +83,7 @@ EXPORT void CALL LoadConfig(const wchar_t * _strFileName) loadSettings(IniFolder.c_str()); } -EXPORT void CALL LoadCustomRomSettings(const wchar_t * _strFileName, const char * _romName) +EXPORT void CALL LoadCustomRomSettings(const wchar_t * _strFileName, const wchar_t * _strSharedFileName, const char * _romName) { std::string IniFolder; uint32_t slength = WideCharToMultiByte(CP_ACP, 0, _strFileName, -1, NULL, 0, NULL, NULL); diff --git a/GLideN64/src/GLideNUI-wtl/GLideNUI.h b/GLideN64/src/GLideNUI-wtl/GLideNUI.h index 0b6ceb14..c3335aaa 100644 --- a/GLideN64/src/GLideNUI-wtl/GLideNUI.h +++ b/GLideN64/src/GLideNUI-wtl/GLideNUI.h @@ -13,10 +13,10 @@ extern "C" { #define CALL #endif -EXPORT bool CALL RunConfig(const wchar_t * _strFileName, const char * _romName, unsigned int _maxMSAALevel, unsigned int _maxAnisotropy); -EXPORT int CALL RunAbout(const wchar_t * _strFileName); -EXPORT void CALL LoadConfig(const wchar_t * _strFileName); -EXPORT void CALL LoadCustomRomSettings(const wchar_t * _strFileName, const char * _romName); +EXPORT bool CALL RunConfig(const wchar_t * _strFileName, const wchar_t * _strSharedFileName, const char * _romName, unsigned int _maxMSAALevel, unsigned int _maxAnisotropy); + EXPORT int CALL RunAbout(const wchar_t * _strFileName); +EXPORT void CALL LoadConfig(const wchar_t * _strFileName, const wchar_t * _strSharedFileName); + EXPORT void CALL LoadCustomRomSettings(const wchar_t * _strFileName, const wchar_t * _strSharedFileName, const char * _romName); #if defined(__cplusplus) } diff --git a/GLideN64/src/GLideNUI/ConfigDialog.cpp b/GLideN64/src/GLideNUI/ConfigDialog.cpp index 0bb7d380..ecde4153 100644 --- a/GLideN64/src/GLideNUI/ConfigDialog.cpp +++ b/GLideN64/src/GLideNUI/ConfigDialog.cpp @@ -1025,17 +1025,19 @@ void ConfigDialog::on_tabWidget_currentChanged(int tab) ui->tabWidget->setCursor(QCursor(Qt::WaitCursor)); QMap internalFontList; - QString fontDir = QStandardPaths::locate(QStandardPaths::FontsLocation, QString(), QStandardPaths::LocateDirectory); QStringList fontFilter; fontFilter << "*.ttf"; - QDirIterator fontIt(fontDir, fontFilter, QDir::Files, QDirIterator::Subdirectories); - while (fontIt.hasNext()) { - QString font = fontIt.next(); - int id = QFontDatabase::addApplicationFont(font); - QStringList fontListFamilies = QFontDatabase::applicationFontFamilies(id); - if (!fontListFamilies.isEmpty()) { - QString fontListFamily = fontListFamilies.at(0); - internalFontList[fontListFamily].append(font); + QStringList fontDirs = QStandardPaths::locateAll(QStandardPaths::FontsLocation, QString(), QStandardPaths::LocateDirectory); + for (const QString& fontDir : fontDirs) { + QDirIterator fontIt(fontDir, fontFilter, QDir::Files, QDirIterator::Subdirectories); + while (fontIt.hasNext()) { + QString font = fontIt.next(); + int id = QFontDatabase::addApplicationFont(font); + QStringList fontListFamilies = QFontDatabase::applicationFontFamilies(id); + if (!fontListFamilies.isEmpty()) { + QString fontListFamily = fontListFamilies.at(0); + internalFontList[fontListFamily].append(font); + } } } diff --git a/GLideN64/src/Textures.cpp b/GLideN64/src/Textures.cpp index 905ff8cc..abfdaa36 100644 --- a/GLideN64/src/Textures.cpp +++ b/GLideN64/src/Textures.cpp @@ -53,9 +53,9 @@ u32 GetCI4IA_RGBA4444(u16 offset, u16 x, u16 i, u8 palette) const u8 color4B = Get4BitPaletteColor(offset, x, i); if (x & 1) - return IA88_RGBA4444(*(u16*)&TMEM[(0x100 + (palette << 4) + (color4B & 0x0F)) & 0x1FF]); + return IA88_RGBA4444(static_cast(TMEM[(0x100 + (palette << 4) + (color4B & 0x0F)) & 0x1FF] & 0xFFFF)); else - return IA88_RGBA4444(*(u16*)&TMEM[(0x100 + (palette << 4) + (color4B >> 4)) & 0x1FF]); + return IA88_RGBA4444(static_cast(TMEM[(0x100 + (palette << 4) + (color4B >> 4)) & 0x1FF] & 0xFFFF)); } u32 GetCI4IA_RGBA8888(u16 offset, u16 x, u16 i, u8 palette) @@ -63,9 +63,9 @@ u32 GetCI4IA_RGBA8888(u16 offset, u16 x, u16 i, u8 palette) const u8 color4B = Get4BitPaletteColor(offset, x, i); if (x & 1) - return IA88_RGBA8888(*(u16*)&TMEM[(0x100 + (palette << 4) + (color4B & 0x0F)) & 0x1FF]); + return IA88_RGBA8888(static_cast(TMEM[(0x100 + (palette << 4) + (color4B & 0x0F)) & 0x1FF] & 0xFFFF)); else - return IA88_RGBA8888(*(u16*)&TMEM[(0x100 + (palette << 4) + (color4B >> 4)) & 0x1FF]); + return IA88_RGBA8888(static_cast(TMEM[(0x100 + (palette << 4) + (color4B >> 4)) & 0x1FF] & 0xFFFF)); } u32 GetCI4RGBA_RGBA5551(u16 offset, u16 x, u16 i, u8 palette) @@ -73,9 +73,9 @@ u32 GetCI4RGBA_RGBA5551(u16 offset, u16 x, u16 i, u8 palette) const u8 color4B = Get4BitPaletteColor(offset, x, i); if (x & 1) - return RGBA5551_RGBA5551(*(u16*)&TMEM[(0x100 + (palette << 4) + (color4B & 0x0F)) & 0x1FF]); + return RGBA5551_RGBA5551(static_cast(TMEM[(0x100 + (palette << 4) + (color4B & 0x0F)) & 0x1FF] & 0xFFFF)); else - return RGBA5551_RGBA5551(*(u16*)&TMEM[(0x100 + (palette << 4) + (color4B >> 4)) & 0x1FF]); + return RGBA5551_RGBA5551(static_cast(TMEM[(0x100 + (palette << 4) + (color4B >> 4)) & 0x1FF] & 0xFFFF)); } u32 GetCI4RGBA_RGBA8888(u16 offset, u16 x, u16 i, u8 palette) @@ -83,9 +83,9 @@ u32 GetCI4RGBA_RGBA8888(u16 offset, u16 x, u16 i, u8 palette) const u8 color4B = Get4BitPaletteColor(offset, x, i); if (x & 1) - return RGBA5551_RGBA8888(*(u16*)&TMEM[(0x100 + (palette << 4) + (color4B & 0x0F)) & 0x1FF]); + return RGBA5551_RGBA8888(static_cast(TMEM[(0x100 + (palette << 4) + (color4B & 0x0F)) & 0x1FF] & 0xFFFF)); else - return RGBA5551_RGBA8888(*(u16*)&TMEM[(0x100 + (palette << 4) + (color4B >> 4)) & 0x1FF]); + return RGBA5551_RGBA8888(static_cast(TMEM[(0x100 + (palette << 4) + (color4B >> 4)) & 0x1FF] & 0xFFFF)); } u32 GetIA31_RGBA8888(u16 offset, u16 x, u16 i, u8 palette) @@ -121,25 +121,25 @@ inline u8 Get8BitPaletteColor(u16 offset, u16 x, u16 i) u32 GetCI8IA_RGBA4444(u16 offset, u16 x, u16 i, u8 palette) { const u8 color = Get8BitPaletteColor(offset, x, i); - return IA88_RGBA4444(*(u16*)&TMEM[(0x100 + color) & 0x1FF]); + return IA88_RGBA4444(static_cast(TMEM[(0x100 + color) & 0x1FF] & 0xFFFF)); } u32 GetCI8IA_RGBA8888(u16 offset, u16 x, u16 i, u8 palette) { const u8 color = Get8BitPaletteColor(offset, x, i); - return IA88_RGBA8888(*(u16*)&TMEM[(0x100 + color) & 0x1FF]); + return IA88_RGBA8888(static_cast(TMEM[(0x100 + color) & 0x1FF] & 0xFFFF)); } u32 GetCI8RGBA_RGBA5551(u16 offset, u16 x, u16 i, u8 palette) { const u8 color = Get8BitPaletteColor(offset, x, i); - return RGBA5551_RGBA5551(*(u16*)&TMEM[(0x100 + color) & 0x1FF]); + return RGBA5551_RGBA5551(static_cast(TMEM[(0x100 + color) & 0x1FF] & 0xFFFF)); } u32 GetCI8RGBA_RGBA8888(u16 offset, u16 x, u16 i, u8 palette) { const u8 color = Get8BitPaletteColor(offset, x, i); - return RGBA5551_RGBA8888(*(u16*)&TMEM[(0x100 + color) & 0x1FF]); + return RGBA5551_RGBA8888(static_cast(TMEM[(0x100 + color) & 0x1FF] & 0xFFFF)); } u32 GetIA44_RGBA8888(u16 offset, u16 x, u16 i, u8 palette) @@ -194,7 +194,7 @@ u32 GetI16_RGBA4444(u16 offset, u16 x, u16 i, u8 palette) u32 GetCI16IA_RGBA8888(u16 offset, u16 x, u16 i, u8 palette) { const u16 tex = Get16BitColor(offset, x, i); - const u16 col = (*(u16*)&TMEM[0x100 + (tex & 0xFF)]); + const u16 col = (static_cast(TMEM[0x100 + (tex & 0xFF)] & 0xFFFF)); const u16 c = col >> 8; const u16 a = col & 0xFF; return (a << 24) | (c << 16) | (c << 8) | c; @@ -203,7 +203,7 @@ u32 GetCI16IA_RGBA8888(u16 offset, u16 x, u16 i, u8 palette) u32 GetCI16IA_RGBA4444(u16 offset, u16 x, u16 i, u8 palette) { const u16 tex = Get16BitColor(offset, x, i); - const u16 col = (*(u16*)&TMEM[0x100 + (tex & 0xFF)]); + const u16 col = (static_cast(TMEM[0x100 + (tex & 0xFF)] & 0xFFFF)); const u16 c = col >> 12; const u16 a = col & 0x0F; return (a << 12) | (c << 8) | (c << 4) | c; @@ -304,9 +304,9 @@ u32 GetCI4IA_RGBA4444_BG(u64 *src, u16 x, u16 i, u8 palette) u8 color4B = ((u8*)src)[(x >> 1) ^ (i << 1)]; if (x & 1) - return IA88_RGBA4444(*(u16*)&TMEM[256 + (palette << 4) + (color4B & 0x0F)]); + return IA88_RGBA4444(static_cast(TMEM[256 + (palette << 4) + (color4B & 0x0F)] & 0xFFFF)); else - return IA88_RGBA4444(*(u16*)&TMEM[256 + (palette << 4) + (color4B >> 4)]); + return IA88_RGBA4444(static_cast(TMEM[256 + (palette << 4) + (color4B >> 4)] & 0xFFFF)); } u32 GetCI4IA_RGBA8888_BG(u64 *src, u16 x, u16 i, u8 palette) @@ -314,9 +314,9 @@ u32 GetCI4IA_RGBA8888_BG(u64 *src, u16 x, u16 i, u8 palette) u8 color4B = ((u8*)src)[(x >> 1) ^ (i << 1)]; if (x & 1) - return IA88_RGBA8888(*(u16*)&TMEM[256 + (palette << 4) + (color4B & 0x0F)]); + return IA88_RGBA8888(static_cast(TMEM[256 + (palette << 4) + (color4B & 0x0F)] & 0xFFFF)); else - return IA88_RGBA8888(*(u16*)&TMEM[256 + (palette << 4) + (color4B >> 4)]); + return IA88_RGBA8888(static_cast(TMEM[256 + (palette << 4) + (color4B >> 4)] & 0xFFFF)); } u32 GetCI4RGBA_RGBA5551_BG(u64 *src, u16 x, u16 i, u8 palette) @@ -324,9 +324,9 @@ u32 GetCI4RGBA_RGBA5551_BG(u64 *src, u16 x, u16 i, u8 palette) u8 color4B = ((u8*)src)[(x >> 1) ^ (i << 1)]; if (x & 1) - return RGBA5551_RGBA5551(*(u16*)&TMEM[256 + (palette << 4) + (color4B & 0x0F)]); + return RGBA5551_RGBA5551(static_cast(TMEM[256 + (palette << 4) + (color4B & 0x0F)] & 0xFFFF)); else - return RGBA5551_RGBA5551(*(u16*)&TMEM[256 + (palette << 4) + (color4B >> 4)]); + return RGBA5551_RGBA5551(static_cast(TMEM[256 + (palette << 4) + (color4B >> 4)] & 0xFFFF)); } u32 GetCI4RGBA_RGBA8888_BG(u64 *src, u16 x, u16 i, u8 palette) @@ -334,9 +334,9 @@ u32 GetCI4RGBA_RGBA8888_BG(u64 *src, u16 x, u16 i, u8 palette) u8 color4B = ((u8*)src)[(x >> 1) ^ (i << 1)]; if (x & 1) - return RGBA5551_RGBA8888(*(u16*)&TMEM[256 + (palette << 4) + (color4B & 0x0F)]); + return RGBA5551_RGBA8888(static_cast(TMEM[256 + (palette << 4) + (color4B & 0x0F)] & 0xFFFF)); else - return RGBA5551_RGBA8888(*(u16*)&TMEM[256 + (palette << 4) + (color4B >> 4)]); + return RGBA5551_RGBA8888(static_cast(TMEM[256 + (palette << 4) + (color4B >> 4)] & 0xFFFF)); } u32 GetIA31_RGBA8888_BG(u64 *src, u16 x, u16 i, u8 palette) @@ -369,22 +369,22 @@ u32 GetI4_RGBA4444_BG(u64 *src, u16 x, u16 i, u8 palette) u32 GetCI8IA_RGBA4444_BG(u64 *src, u16 x, u16 i, u8 palette) { - return IA88_RGBA4444(*(u16*)&TMEM[256 + ((u8*)src)[x ^ (i << 1)]]); + return IA88_RGBA4444(static_cast(TMEM[256 + ((u8*)src)[x ^ (i << 1)]] & 0xFFFF)); } u32 GetCI8IA_RGBA8888_BG(u64 *src, u16 x, u16 i, u8 palette) { - return IA88_RGBA8888(*(u16*)&TMEM[256 + ((u8*)src)[x ^ (i << 1)]]); + return IA88_RGBA8888(static_cast(TMEM[256 + ((u8*)src)[x ^ (i << 1)]] & 0xFFFF)); } u32 GetCI8RGBA_RGBA5551_BG(u64 *src, u16 x, u16 i, u8 palette) { - return RGBA5551_RGBA5551(*(u16*)&TMEM[256 + ((u8*)src)[x ^ (i << 1)]]); + return RGBA5551_RGBA5551(static_cast(TMEM[256 + ((u8*)src)[x ^ (i << 1)]] & 0xFFFF)); } u32 GetCI8RGBA_RGBA8888_BG(u64 *src, u16 x, u16 i, u8 palette) { - return RGBA5551_RGBA8888(*(u16*)&TMEM[256 + ((u8*)src)[x ^ (i << 1)]]); + return RGBA5551_RGBA8888(static_cast(TMEM[256 + ((u8*)src)[x ^ (i << 1)]] & 0xFFFF)); } u32 GetIA44_RGBA8888_BG(u64 *src, u16 x, u16 i, u8 palette) @@ -430,7 +430,7 @@ u32 GetI16_RGBA4444_BG(u64 *src, u16 x, u16 i, u8 palette) u32 GetCI16IA_RGBA8888_BG(u64 *src, u16 x, u16 i, u8 palette) { const u16 tex = ((u16*)src)[x^i]; - const u16 col = (*(u16*)&TMEM[256 + (tex & 0xFF)]); + const u16 col = (static_cast(TMEM[256 + (tex & 0xFF)] & 0xFFFF)); const u16 c = col >> 8; const u16 a = col & 0xFF; return (a << 24) | (c << 16) | (c << 8) | c; @@ -439,7 +439,7 @@ u32 GetCI16IA_RGBA8888_BG(u64 *src, u16 x, u16 i, u8 palette) u32 GetCI16IA_RGBA4444_BG(u64 *src, u16 x, u16 i, u8 palette) { const u16 tex = ((u16*)src)[x^i]; - const u16 col = (*(u16*)&TMEM[256 + (tex & 0xFF)]); + const u16 col = (static_cast(TMEM[256 + (tex & 0xFF)] & 0xFFFF)); const u16 c = col >> 12; const u16 a = col & 0x0F; return (a << 12) | (c << 8) | (c << 4) | c; diff --git a/GLideN64/src/gSP.cpp b/GLideN64/src/gSP.cpp index b22c4957..d6662d95 100644 --- a/GLideN64/src/gSP.cpp +++ b/GLideN64/src/gSP.cpp @@ -248,6 +248,8 @@ void gSPDMAMatrix( u32 matrix, u8 index, u8 multiply ) mtx[3][0], mtx[3][1], mtx[3][2], mtx[3][3] ); } +#define G_F3DEX3_NEW_MAXZ 0x7FFF + void gSPViewport( u32 v ) { u32 address = RSP_SegmentToPhysical( v ); @@ -267,8 +269,15 @@ void gSPViewport( u32 v ) gSP.viewport.vtrans[2] = _FIXED2FLOAT( *(s16*)&RDRAM[address + 14], 10 );// * 0.00097847357f; gSP.viewport.vtrans[3] = *(s16*)&RDRAM[address + 12]; - if (gSP.viewport.vscale[1] < 0.0f && !GBI.isNegativeY()) - gSP.viewport.vscale[1] = -gSP.viewport.vscale[1]; + if (gSP.viewport.vscale[1] < 0.0f) { + if (!GBI.isNegativeY()) { + gSP.viewport.vscale[1] = -gSP.viewport.vscale[1]; + if (F3DEX3 == GBI.getMicrocodeType()) { + gSP.viewport.vscale[2] /= (G_F3DEX3_NEW_MAXZ / 2) / 511.f; + gSP.viewport.vtrans[2] /= (G_F3DEX3_NEW_MAXZ / 2) / 511.f; + } + } + } gSP.viewport.x = gSP.viewport.vtrans[0] - gSP.viewport.vscale[0]; gSP.viewport.y = gSP.viewport.vtrans[1] - gSP.viewport.vscale[1]; @@ -314,7 +323,7 @@ void gSPLight( u32 l, s32 n ) Light *light = (Light*)&RDRAM[addrByte]; - if (n < 9) { + if (n < 10) { gSP.lights.rgb[n][R] = _FIXED2FLOATCOLOR(light->r,8); gSP.lights.rgb[n][G] = _FIXED2FLOATCOLOR(light->g,8); gSP.lights.rgb[n][B] = _FIXED2FLOATCOLOR(light->b,8); diff --git a/GLideN64/src/mupenplus/Config_mupenplus.cpp b/GLideN64/src/mupenplus/Config_mupenplus.cpp index 2d9315d6..97e38a94 100644 --- a/GLideN64/src/mupenplus/Config_mupenplus.cpp +++ b/GLideN64/src/mupenplus/Config_mupenplus.cpp @@ -340,6 +340,8 @@ void Config_LoadCustomConfig() ROMname.replace(pos, 1, "%20"); for (size_t pos = ROMname.find('\''); pos != std::string::npos; pos = ROMname.find('\'', pos)) ROMname.replace(pos, 1, "%27"); + for (size_t pos = ROMname.find('&'); pos != std::string::npos; pos = ROMname.find('&', pos)) + ROMname.replace(pos, 1, "%26"); std::transform(ROMname.begin(), ROMname.end(), ROMname.begin(), ::toupper); const char* sectionName = ROMname.c_str(); m64p_handle fileHandle; diff --git a/GLideN64/src/osal/CMakeLists.txt b/GLideN64/src/osal/CMakeLists.txt index c0e600c7..aed3ac6d 100644 --- a/GLideN64/src/osal/CMakeLists.txt +++ b/GLideN64/src/osal/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.9) +cmake_minimum_required(VERSION 3.10) project( osal ) @@ -39,7 +39,7 @@ if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") add_definitions( -D__MSC__) endif() -if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") +if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") SET(GCC_CPP11_COMPILE_FLAGS "-std=c++0x") if (NOT MINGW) SET( PIC_FLAGS "-fPIC") diff --git a/GLideN64/src/uCodes/F3DEX3.cpp b/GLideN64/src/uCodes/F3DEX3.cpp index 056c23f1..e1fc9d51 100644 --- a/GLideN64/src/uCodes/F3DEX3.cpp +++ b/GLideN64/src/uCodes/F3DEX3.cpp @@ -35,21 +35,7 @@ #define F3DEX3_G_MAX_LIGHTS 9 -struct F3DEX3_Ambient -{ - u8 pad0, b, g, r; - u8 pad1, b2, g2, r2; -}; - -struct F3DEX3_Light -{ - u8 pad0, b, g, r; - u8 pad1, b2, g2, r2; - s8 pad2, z, y, x; - u8 size, pad3[3]; -}; - -// Notice how it looks like F3DEX3_Light but the ending so there are 8 bytes of difference +// Notice how it looks like Light but the ending so there are 8 bytes of difference struct F3DEX3_LookAt { s8 pad, z, y, x; @@ -83,7 +69,7 @@ static void writeLight(int off, u32 w) gSPLookAt(w - (sizeof(F3DEX3_LookAtOld) - sizeof(F3DEX3_LookAt)) + sizeof(F3DEX3_LookAt), 1); } - for (u32 i = 1; i <= gSP.numLights; i++) + for (u32 i = 1; i <= gSP.numLights + 1; i++) { if (_LIGHT_TO_OFFSET(i) == off) { @@ -91,10 +77,6 @@ static void writeLight(int off, u32 w) } } - if (_LIGHT_TO_OFFSET(gSP.numLights + 1) == off) - { - // TODO: Write ambient lights - } if ((F3DEX3_G_MAX_LIGHTS * 0x10) + 0x18 == off) { // TODO: OcclusionPlane not supported diff --git a/GLideN64/src/xxHash/xxhash.h b/GLideN64/src/xxHash/xxhash.h index 2d56d23c..78fc2e8d 100644 --- a/GLideN64/src/xxHash/xxhash.h +++ b/GLideN64/src/xxHash/xxhash.h @@ -1,7 +1,7 @@ /* * xxHash - Extremely Fast Hash algorithm * Header File - * Copyright (C) 2012-2020 Yann Collet + * Copyright (C) 2012-2023 Yann Collet * * BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php) * @@ -33,43 +33,213 @@ * - xxHash source repository: https://github.com/Cyan4973/xxHash */ -/* TODO: update */ -/* Notice extracted from xxHash homepage: - -xxHash is an extremely fast hash algorithm, running at RAM speed limits. -It also successfully passes all tests from the SMHasher suite. - -Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2 Duo @3GHz) - -Name Speed Q.Score Author -xxHash 5.4 GB/s 10 -CrapWow 3.2 GB/s 2 Andrew -MumurHash 3a 2.7 GB/s 10 Austin Appleby -SpookyHash 2.0 GB/s 10 Bob Jenkins -SBox 1.4 GB/s 9 Bret Mulvey -Lookup3 1.2 GB/s 9 Bob Jenkins -SuperFastHash 1.2 GB/s 1 Paul Hsieh -CityHash64 1.05 GB/s 10 Pike & Alakuijala -FNV 0.55 GB/s 5 Fowler, Noll, Vo -CRC32 0.43 GB/s 9 -MD5-32 0.33 GB/s 10 Ronald L. Rivest -SHA1-32 0.28 GB/s 10 - -Q.Score is a measure of quality of the hash function. -It depends on successfully passing SMHasher test set. -10 is a perfect score. - -Note: SMHasher's CRC32 implementation is not the fastest one. -Other speed-oriented implementations can be faster, -especially in combination with PCLMUL instruction: -https://fastcompression.blogspot.com/2019/03/presenting-xxh3.html?showComment=1552696407071#c3490092340461170735 - -A 64-bit version, named XXH64, is available since r35. -It offers much better speed, but for 64-bit applications only. -Name Speed on 64 bits Speed on 32 bits -XXH64 13.8 GB/s 1.9 GB/s -XXH32 6.8 GB/s 6.0 GB/s -*/ +/*! + * @mainpage xxHash + * + * xxHash is an extremely fast non-cryptographic hash algorithm, working at RAM speed + * limits. + * + * It is proposed in four flavors, in three families: + * 1. @ref XXH32_family + * - Classic 32-bit hash function. Simple, compact, and runs on almost all + * 32-bit and 64-bit systems. + * 2. @ref XXH64_family + * - Classic 64-bit adaptation of XXH32. Just as simple, and runs well on most + * 64-bit systems (but _not_ 32-bit systems). + * 3. @ref XXH3_family + * - Modern 64-bit and 128-bit hash function family which features improved + * strength and performance across the board, especially on smaller data. + * It benefits greatly from SIMD and 64-bit without requiring it. + * + * Benchmarks + * --- + * The reference system uses an Intel i7-9700K CPU, and runs Ubuntu x64 20.04. + * The open source benchmark program is compiled with clang v10.0 using -O3 flag. + * + * | Hash Name | ISA ext | Width | Large Data Speed | Small Data Velocity | + * | -------------------- | ------- | ----: | ---------------: | ------------------: | + * | XXH3_64bits() | @b AVX2 | 64 | 59.4 GB/s | 133.1 | + * | MeowHash | AES-NI | 128 | 58.2 GB/s | 52.5 | + * | XXH3_128bits() | @b AVX2 | 128 | 57.9 GB/s | 118.1 | + * | CLHash | PCLMUL | 64 | 37.1 GB/s | 58.1 | + * | XXH3_64bits() | @b SSE2 | 64 | 31.5 GB/s | 133.1 | + * | XXH3_128bits() | @b SSE2 | 128 | 29.6 GB/s | 118.1 | + * | RAM sequential read | | N/A | 28.0 GB/s | N/A | + * | ahash | AES-NI | 64 | 22.5 GB/s | 107.2 | + * | City64 | | 64 | 22.0 GB/s | 76.6 | + * | T1ha2 | | 64 | 22.0 GB/s | 99.0 | + * | City128 | | 128 | 21.7 GB/s | 57.7 | + * | FarmHash | AES-NI | 64 | 21.3 GB/s | 71.9 | + * | XXH64() | | 64 | 19.4 GB/s | 71.0 | + * | SpookyHash | | 64 | 19.3 GB/s | 53.2 | + * | Mum | | 64 | 18.0 GB/s | 67.0 | + * | CRC32C | SSE4.2 | 32 | 13.0 GB/s | 57.9 | + * | XXH32() | | 32 | 9.7 GB/s | 71.9 | + * | City32 | | 32 | 9.1 GB/s | 66.0 | + * | Blake3* | @b AVX2 | 256 | 4.4 GB/s | 8.1 | + * | Murmur3 | | 32 | 3.9 GB/s | 56.1 | + * | SipHash* | | 64 | 3.0 GB/s | 43.2 | + * | Blake3* | @b SSE2 | 256 | 2.4 GB/s | 8.1 | + * | HighwayHash | | 64 | 1.4 GB/s | 6.0 | + * | FNV64 | | 64 | 1.2 GB/s | 62.7 | + * | Blake2* | | 256 | 1.1 GB/s | 5.1 | + * | SHA1* | | 160 | 0.8 GB/s | 5.6 | + * | MD5* | | 128 | 0.6 GB/s | 7.8 | + * @note + * - Hashes which require a specific ISA extension are noted. SSE2 is also noted, + * even though it is mandatory on x64. + * - Hashes with an asterisk are cryptographic. Note that MD5 is non-cryptographic + * by modern standards. + * - Small data velocity is a rough average of algorithm's efficiency for small + * data. For more accurate information, see the wiki. + * - More benchmarks and strength tests are found on the wiki: + * https://github.com/Cyan4973/xxHash/wiki + * + * Usage + * ------ + * All xxHash variants use a similar API. Changing the algorithm is a trivial + * substitution. + * + * @pre + * For functions which take an input and length parameter, the following + * requirements are assumed: + * - The range from [`input`, `input + length`) is valid, readable memory. + * - The only exception is if the `length` is `0`, `input` may be `NULL`. + * - For C++, the objects must have the *TriviallyCopyable* property, as the + * functions access bytes directly as if it was an array of `unsigned char`. + * + * @anchor single_shot_example + * **Single Shot** + * + * These functions are stateless functions which hash a contiguous block of memory, + * immediately returning the result. They are the easiest and usually the fastest + * option. + * + * XXH32(), XXH64(), XXH3_64bits(), XXH3_128bits() + * + * @code{.c} + * #include + * #include "xxhash.h" + * + * // Example for a function which hashes a null terminated string with XXH32(). + * XXH32_hash_t hash_string(const char* string, XXH32_hash_t seed) + * { + * // NULL pointers are only valid if the length is zero + * size_t length = (string == NULL) ? 0 : strlen(string); + * return XXH32(string, length, seed); + * } + * @endcode + * + * + * @anchor streaming_example + * **Streaming** + * + * These groups of functions allow incremental hashing of unknown size, even + * more than what would fit in a size_t. + * + * XXH32_reset(), XXH64_reset(), XXH3_64bits_reset(), XXH3_128bits_reset() + * + * @code{.c} + * #include + * #include + * #include "xxhash.h" + * // Example for a function which hashes a FILE incrementally with XXH3_64bits(). + * XXH64_hash_t hashFile(FILE* f) + * { + * // Allocate a state struct. Do not just use malloc() or new. + * XXH3_state_t* state = XXH3_createState(); + * assert(state != NULL && "Out of memory!"); + * // Reset the state to start a new hashing session. + * XXH3_64bits_reset(state); + * char buffer[4096]; + * size_t count; + * // Read the file in chunks + * while ((count = fread(buffer, 1, sizeof(buffer), f)) != 0) { + * // Run update() as many times as necessary to process the data + * XXH3_64bits_update(state, buffer, count); + * } + * // Retrieve the finalized hash. This will not change the state. + * XXH64_hash_t result = XXH3_64bits_digest(state); + * // Free the state. Do not use free(). + * XXH3_freeState(state); + * return result; + * } + * @endcode + * + * Streaming functions generate the xxHash value from an incremental input. + * This method is slower than single-call functions, due to state management. + * For small inputs, prefer `XXH32()` and `XXH64()`, which are better optimized. + * + * An XXH state must first be allocated using `XXH*_createState()`. + * + * Start a new hash by initializing the state with a seed using `XXH*_reset()`. + * + * Then, feed the hash state by calling `XXH*_update()` as many times as necessary. + * + * The function returns an error code, with 0 meaning OK, and any other value + * meaning there is an error. + * + * Finally, a hash value can be produced anytime, by using `XXH*_digest()`. + * This function returns the nn-bits hash as an int or long long. + * + * It's still possible to continue inserting input into the hash state after a + * digest, and generate new hash values later on by invoking `XXH*_digest()`. + * + * When done, release the state using `XXH*_freeState()`. + * + * + * @anchor canonical_representation_example + * **Canonical Representation** + * + * The default return values from XXH functions are unsigned 32, 64 and 128 bit + * integers. + * This the simplest and fastest format for further post-processing. + * + * However, this leaves open the question of what is the order on the byte level, + * since little and big endian conventions will store the same number differently. + * + * The canonical representation settles this issue by mandating big-endian + * convention, the same convention as human-readable numbers (large digits first). + * + * When writing hash values to storage, sending them over a network, or printing + * them, it's highly recommended to use the canonical representation to ensure + * portability across a wider range of systems, present and future. + * + * The following functions allow transformation of hash values to and from + * canonical format. + * + * XXH32_canonicalFromHash(), XXH32_hashFromCanonical(), + * XXH64_canonicalFromHash(), XXH64_hashFromCanonical(), + * XXH128_canonicalFromHash(), XXH128_hashFromCanonical(), + * + * @code{.c} + * #include + * #include "xxhash.h" + * + * // Example for a function which prints XXH32_hash_t in human readable format + * void printXxh32(XXH32_hash_t hash) + * { + * XXH32_canonical_t cano; + * XXH32_canonicalFromHash(&cano, hash); + * size_t i; + * for(i = 0; i < sizeof(cano.digest); ++i) { + * printf("%02x", cano.digest[i]); + * } + * printf("\n"); + * } + * + * // Example for a function which converts XXH32_canonical_t to XXH32_hash_t + * XXH32_hash_t convertCanonicalToXxh32(XXH32_canonical_t cano) + * { + * XXH32_hash_t hash = XXH32_hashFromCanonical(&cano); + * return hash; + * } + * @endcode + * + * + * @file xxhash.h + * xxHash prototypes and implementation + */ #if defined (__cplusplus) extern "C" { @@ -79,21 +249,80 @@ extern "C" { * INLINE mode ******************************/ /*! - * XXH_INLINE_ALL (and XXH_PRIVATE_API) + * @defgroup public Public API + * Contains details on the public xxHash functions. + * @{ + */ +#ifdef XXH_DOXYGEN +/*! + * @brief Gives access to internal state declaration, required for static allocation. + * + * Incompatible with dynamic linking, due to risks of ABI changes. + * + * Usage: + * @code{.c} + * #define XXH_STATIC_LINKING_ONLY + * #include "xxhash.h" + * @endcode + */ +# define XXH_STATIC_LINKING_ONLY +/* Do not undef XXH_STATIC_LINKING_ONLY for Doxygen */ + +/*! + * @brief Gives access to internal definitions. + * + * Usage: + * @code{.c} + * #define XXH_STATIC_LINKING_ONLY + * #define XXH_IMPLEMENTATION + * #include "xxhash.h" + * @endcode + */ +# define XXH_IMPLEMENTATION +/* Do not undef XXH_IMPLEMENTATION for Doxygen */ + +/*! + * @brief Exposes the implementation and marks all functions as `inline`. + * * Use these build macros to inline xxhash into the target unit. * Inlining improves performance on small inputs, especially when the length is * expressed as a compile-time constant: * - * https://fastcompression.blogspot.com/2018/03/xxhash-for-small-keys-impressive-power.html + * https://fastcompression.blogspot.com/2018/03/xxhash-for-small-keys-impressive-power.html * * It also keeps xxHash symbols private to the unit, so they are not exported. * * Usage: + * @code{.c} * #define XXH_INLINE_ALL * #include "xxhash.h" - * + * @endcode * Do not compile and link xxhash.o as a separate object, as it is not useful. */ +# define XXH_INLINE_ALL +# undef XXH_INLINE_ALL +/*! + * @brief Exposes the implementation without marking functions as inline. + */ +# define XXH_PRIVATE_API +# undef XXH_PRIVATE_API +/*! + * @brief Emulate a namespace by transparently prefixing all symbols. + * + * If you want to include _and expose_ xxHash functions from within your own + * library, but also want to avoid symbol collisions with other libraries which + * may also include xxHash, you can use @ref XXH_NAMESPACE to automatically prefix + * any public symbol from xxhash library with the value of @ref XXH_NAMESPACE + * (therefore, avoid empty or numeric values). + * + * Note that no change is required within the calling program as long as it + * includes `xxhash.h`: Regular symbol names will be automatically translated + * by this header. + */ +# define XXH_NAMESPACE /* YOUR NAME HERE */ +# undef XXH_NAMESPACE +#endif + #if (defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API)) \ && !defined(XXH_INLINE_ALL_31684351384) /* this section should be traversed only once */ @@ -104,7 +333,7 @@ extern "C" { /* make all functions private */ # undef XXH_PUBLIC_API # if defined(__GNUC__) -# define XXH_PUBLIC_API static __inline __attribute__((unused)) +# define XXH_PUBLIC_API static __inline __attribute__((__unused__)) # elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) # define XXH_PUBLIC_API static inline # elif defined(_MSC_VER) @@ -116,29 +345,80 @@ extern "C" { /* * This part deals with the special case where a unit wants to inline xxHash, - * but "xxhash.h" has previously been included without XXH_INLINE_ALL, such - * as part of some previously included *.h header file. + * but "xxhash.h" has previously been included without XXH_INLINE_ALL, + * such as part of some previously included *.h header file. * Without further action, the new include would just be ignored, * and functions would effectively _not_ be inlined (silent failure). * The following macros solve this situation by prefixing all inlined names, * avoiding naming collision with previous inclusions. */ -# ifdef XXH_NAMESPACE -# error "XXH_INLINE_ALL with XXH_NAMESPACE is not supported" - /* - * Note: Alternative: #undef all symbols (it's a pretty large list). - * Without #error: it compiles, but functions are actually not inlined. - */ -# endif + /* Before that, we unconditionally #undef all symbols, + * in case they were already defined with XXH_NAMESPACE. + * They will then be redefined for XXH_INLINE_ALL + */ +# undef XXH_versionNumber + /* XXH32 */ +# undef XXH32 +# undef XXH32_createState +# undef XXH32_freeState +# undef XXH32_reset +# undef XXH32_update +# undef XXH32_digest +# undef XXH32_copyState +# undef XXH32_canonicalFromHash +# undef XXH32_hashFromCanonical + /* XXH64 */ +# undef XXH64 +# undef XXH64_createState +# undef XXH64_freeState +# undef XXH64_reset +# undef XXH64_update +# undef XXH64_digest +# undef XXH64_copyState +# undef XXH64_canonicalFromHash +# undef XXH64_hashFromCanonical + /* XXH3_64bits */ +# undef XXH3_64bits +# undef XXH3_64bits_withSecret +# undef XXH3_64bits_withSeed +# undef XXH3_64bits_withSecretandSeed +# undef XXH3_createState +# undef XXH3_freeState +# undef XXH3_copyState +# undef XXH3_64bits_reset +# undef XXH3_64bits_reset_withSeed +# undef XXH3_64bits_reset_withSecret +# undef XXH3_64bits_update +# undef XXH3_64bits_digest +# undef XXH3_generateSecret + /* XXH3_128bits */ +# undef XXH128 +# undef XXH3_128bits +# undef XXH3_128bits_withSeed +# undef XXH3_128bits_withSecret +# undef XXH3_128bits_reset +# undef XXH3_128bits_reset_withSeed +# undef XXH3_128bits_reset_withSecret +# undef XXH3_128bits_reset_withSecretandSeed +# undef XXH3_128bits_update +# undef XXH3_128bits_digest +# undef XXH128_isEqual +# undef XXH128_cmp +# undef XXH128_canonicalFromHash +# undef XXH128_hashFromCanonical + /* Finally, free the namespace itself */ +# undef XXH_NAMESPACE + + /* employ the namespace for XXH_INLINE_ALL */ # define XXH_NAMESPACE XXH_INLINE_ /* - * Some identifiers (enums, type names) are not symbols, but they must - * still be renamed to avoid redeclaration. + * Some identifiers (enums, type names) are not symbols, + * but they must nonetheless be renamed to avoid redeclaration. * Alternative solution: do not redeclare them. - * However, this requires some #ifdefs, and is a more dispersed action. - * Meanwhile, renaming can be achieved in a single block + * However, this requires some #ifdefs, and has a more dispersed impact. + * Meanwhile, renaming can be achieved in a single place. */ -# define XXH_IPREF(Id) XXH_INLINE_ ## Id +# define XXH_IPREF(Id) XXH_NAMESPACE ## Id # define XXH_OK XXH_IPREF(XXH_OK) # define XXH_ERROR XXH_IPREF(XXH_ERROR) # define XXH_errorcode XXH_IPREF(XXH_errorcode) @@ -157,17 +437,15 @@ extern "C" { # undef XXHASH_H_STATIC_13879238742 #endif /* XXH_INLINE_ALL || XXH_PRIVATE_API */ - - /* **************************************************************** * Stable API *****************************************************************/ #ifndef XXHASH_H_5627135585666179 #define XXHASH_H_5627135585666179 1 -/* specific declaration modes for Windows */ +/*! @brief Marks a global symbol. */ #if !defined(XXH_INLINE_ALL) && !defined(XXH_PRIVATE_API) -# if defined(WIN32) && defined(_MSC_VER) && (defined(XXH_IMPORT) || defined(XXH_EXPORT)) +# if defined(_WIN32) && defined(_MSC_VER) && (defined(XXH_IMPORT) || defined(XXH_EXPORT)) # ifdef XXH_EXPORT # define XXH_PUBLIC_API __declspec(dllexport) # elif XXH_IMPORT @@ -178,19 +456,6 @@ extern "C" { # endif #endif -/*! - * XXH_NAMESPACE, aka Namespace Emulation: - * - * If you want to include _and expose_ xxHash functions from within your own - * library, but also want to avoid symbol collisions with other libraries which - * may also include xxHash, you can use XXH_NAMESPACE to automatically prefix - * any public symbol from xxhash library with the value of XXH_NAMESPACE - * (therefore, avoid empty or numeric values). - * - * Note that no change is required within the calling program as long as it - * includes `xxhash.h`: Regular symbol names will be automatically translated - * by this header. - */ #ifdef XXH_NAMESPACE # define XXH_CAT(A,B) A##B # define XXH_NAME2(A,B) XXH_CAT(A,B) @@ -219,23 +484,28 @@ extern "C" { # define XXH3_64bits XXH_NAME2(XXH_NAMESPACE, XXH3_64bits) # define XXH3_64bits_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_withSecret) # define XXH3_64bits_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_withSeed) +# define XXH3_64bits_withSecretandSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_withSecretandSeed) # define XXH3_createState XXH_NAME2(XXH_NAMESPACE, XXH3_createState) # define XXH3_freeState XXH_NAME2(XXH_NAMESPACE, XXH3_freeState) # define XXH3_copyState XXH_NAME2(XXH_NAMESPACE, XXH3_copyState) # define XXH3_64bits_reset XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset) # define XXH3_64bits_reset_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset_withSeed) # define XXH3_64bits_reset_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset_withSecret) +# define XXH3_64bits_reset_withSecretandSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset_withSecretandSeed) # define XXH3_64bits_update XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_update) # define XXH3_64bits_digest XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_digest) # define XXH3_generateSecret XXH_NAME2(XXH_NAMESPACE, XXH3_generateSecret) +# define XXH3_generateSecret_fromSeed XXH_NAME2(XXH_NAMESPACE, XXH3_generateSecret_fromSeed) /* XXH3_128bits */ # define XXH128 XXH_NAME2(XXH_NAMESPACE, XXH128) # define XXH3_128bits XXH_NAME2(XXH_NAMESPACE, XXH3_128bits) # define XXH3_128bits_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_withSeed) # define XXH3_128bits_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_withSecret) +# define XXH3_128bits_withSecretandSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_withSecretandSeed) # define XXH3_128bits_reset XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset) # define XXH3_128bits_reset_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset_withSeed) # define XXH3_128bits_reset_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset_withSecret) +# define XXH3_128bits_reset_withSecretandSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset_withSecretandSeed) # define XXH3_128bits_update XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_update) # define XXH3_128bits_digest XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_digest) # define XXH128_isEqual XXH_NAME2(XXH_NAMESPACE, XXH128_isEqual) @@ -245,353 +515,1128 @@ extern "C" { #endif +/* ************************************* +* Compiler specifics +***************************************/ + +/* specific declaration modes for Windows */ +#if !defined(XXH_INLINE_ALL) && !defined(XXH_PRIVATE_API) +# if defined(_WIN32) && defined(_MSC_VER) && (defined(XXH_IMPORT) || defined(XXH_EXPORT)) +# ifdef XXH_EXPORT +# define XXH_PUBLIC_API __declspec(dllexport) +# elif XXH_IMPORT +# define XXH_PUBLIC_API __declspec(dllimport) +# endif +# else +# define XXH_PUBLIC_API /* do nothing */ +# endif +#endif + +#if defined (__GNUC__) +# define XXH_CONSTF __attribute__((__const__)) +# define XXH_PUREF __attribute__((__pure__)) +# define XXH_MALLOCF __attribute__((__malloc__)) +#else +# define XXH_CONSTF /* disable */ +# define XXH_PUREF +# define XXH_MALLOCF +#endif + /* ************************************* * Version ***************************************/ #define XXH_VERSION_MAJOR 0 #define XXH_VERSION_MINOR 8 -#define XXH_VERSION_RELEASE 0 +#define XXH_VERSION_RELEASE 3 +/*! @brief Version number, encoded as two digits each */ #define XXH_VERSION_NUMBER (XXH_VERSION_MAJOR *100*100 + XXH_VERSION_MINOR *100 + XXH_VERSION_RELEASE) -XXH_PUBLIC_API unsigned XXH_versionNumber (void); + +/*! + * @brief Obtains the xxHash version. + * + * This is mostly useful when xxHash is compiled as a shared library, + * since the returned value comes from the library, as opposed to header file. + * + * @return @ref XXH_VERSION_NUMBER of the invoked library. + */ +XXH_PUBLIC_API XXH_CONSTF unsigned XXH_versionNumber (void); /* **************************** -* Definitions +* Common basic types ******************************/ #include /* size_t */ -typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode; +/*! + * @brief Exit code for the streaming API. + */ +typedef enum { + XXH_OK = 0, /*!< OK */ + XXH_ERROR /*!< Error */ +} XXH_errorcode; /*-********************************************************************** * 32-bit hash ************************************************************************/ -#if !defined (__VMS) \ +#if defined(XXH_DOXYGEN) /* Don't show include */ +/*! + * @brief An unsigned 32-bit integer. + * + * Not necessarily defined to `uint32_t` but functionally equivalent. + */ +typedef uint32_t XXH32_hash_t; + +#elif !defined (__VMS) \ && (defined (__cplusplus) \ || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) -# include +# ifdef _AIX +# include +# else +# include +# endif typedef uint32_t XXH32_hash_t; + #else # include # if UINT_MAX == 0xFFFFFFFFUL typedef unsigned int XXH32_hash_t; +# elif ULONG_MAX == 0xFFFFFFFFUL + typedef unsigned long XXH32_hash_t; # else -# if ULONG_MAX == 0xFFFFFFFFUL - typedef unsigned long XXH32_hash_t; -# else -# error "unsupported platform: need a 32-bit type" -# endif +# error "unsupported platform: need a 32-bit type" # endif #endif /*! - * XXH32(): - * Calculate the 32-bit hash of sequence "length" bytes stored at memory address "input". - * The memory between input & input+length must be valid (allocated and read-accessible). - * "seed" can be used to alter the result predictably. - * Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark): 5.4 GB/s + * @} * - * Note: XXH3 provides competitive speed for both 32-bit and 64-bit systems, - * and offers true 64/128 bit hash results. It provides a superior level of - * dispersion, and greatly reduces the risks of collisions. + * @defgroup XXH32_family XXH32 family + * @ingroup public + * Contains functions used in the classic 32-bit xxHash algorithm. + * + * @note + * XXH32 is useful for older platforms, with no or poor 64-bit performance. + * Note that the @ref XXH3_family provides competitive speed for both 32-bit + * and 64-bit systems, and offers true 64/128 bit hash results. + * + * @see @ref XXH64_family, @ref XXH3_family : Other xxHash families + * @see @ref XXH32_impl for implementation details + * @{ */ -XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t length, XXH32_hash_t seed); -/******* Streaming *******/ +/*! + * @brief Calculates the 32-bit hash of @p input using xxHash32. + * + * @param input The block of data to be hashed, at least @p length bytes in size. + * @param length The length of @p input, in bytes. + * @param seed The 32-bit seed to alter the hash's output predictably. + * + * @pre + * The memory between @p input and @p input + @p length must be valid, + * readable, contiguous memory. However, if @p length is `0`, @p input may be + * `NULL`. In C++, this also must be *TriviallyCopyable*. + * + * @return The calculated 32-bit xxHash32 value. + * + * @see @ref single_shot_example "Single Shot Example" for an example. + */ +XXH_PUBLIC_API XXH_PUREF XXH32_hash_t XXH32 (const void* input, size_t length, XXH32_hash_t seed); -/* - * Streaming functions generate the xxHash value from an incrememtal input. - * This method is slower than single-call functions, due to state management. - * For small inputs, prefer `XXH32()` and `XXH64()`, which are better optimized. +#ifndef XXH_NO_STREAM +/*! + * @typedef struct XXH32_state_s XXH32_state_t + * @brief The opaque state struct for the XXH32 streaming API. * - * An XXH state must first be allocated using `XXH*_createState()`. + * @see XXH32_state_s for details. + * @see @ref streaming_example "Streaming Example" + */ +typedef struct XXH32_state_s XXH32_state_t; + +/*! + * @brief Allocates an @ref XXH32_state_t. * - * Start a new hash by initializing the state with a seed using `XXH*_reset()`. + * @return An allocated pointer of @ref XXH32_state_t on success. + * @return `NULL` on failure. * - * Then, feed the hash state by calling `XXH*_update()` as many times as necessary. + * @note Must be freed with XXH32_freeState(). * - * The function returns an error code, with 0 meaning OK, and any other value - * meaning there is an error. + * @see @ref streaming_example "Streaming Example" + */ +XXH_PUBLIC_API XXH_MALLOCF XXH32_state_t* XXH32_createState(void); +/*! + * @brief Frees an @ref XXH32_state_t. * - * Finally, a hash value can be produced anytime, by using `XXH*_digest()`. - * This function returns the nn-bits hash as an int or long long. + * @param statePtr A pointer to an @ref XXH32_state_t allocated with @ref XXH32_createState(). * - * It's still possible to continue inserting input into the hash state after a - * digest, and generate new hash values later on by invoking `XXH*_digest()`. + * @return @ref XXH_OK. + * + * @note @p statePtr must be allocated with XXH32_createState(). + * + * @see @ref streaming_example "Streaming Example" * - * When done, release the state using `XXH*_freeState()`. */ - -typedef struct XXH32_state_s XXH32_state_t; /* incomplete type */ -XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void); XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr); +/*! + * @brief Copies one @ref XXH32_state_t to another. + * + * @param dst_state The state to copy to. + * @param src_state The state to copy from. + * @pre + * @p dst_state and @p src_state must not be `NULL` and must not overlap. + */ XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dst_state, const XXH32_state_t* src_state); +/*! + * @brief Resets an @ref XXH32_state_t to begin a new hash. + * + * @param statePtr The state struct to reset. + * @param seed The 32-bit seed to alter the hash result predictably. + * + * @pre + * @p statePtr must not be `NULL`. + * + * @return @ref XXH_OK on success. + * @return @ref XXH_ERROR on failure. + * + * @note This function resets and seeds a state. Call it before @ref XXH32_update(). + * + * @see @ref streaming_example "Streaming Example" + */ XXH_PUBLIC_API XXH_errorcode XXH32_reset (XXH32_state_t* statePtr, XXH32_hash_t seed); + +/*! + * @brief Consumes a block of @p input to an @ref XXH32_state_t. + * + * @param statePtr The state struct to update. + * @param input The block of data to be hashed, at least @p length bytes in size. + * @param length The length of @p input, in bytes. + * + * @pre + * @p statePtr must not be `NULL`. + * @pre + * The memory between @p input and @p input + @p length must be valid, + * readable, contiguous memory. However, if @p length is `0`, @p input may be + * `NULL`. In C++, this also must be *TriviallyCopyable*. + * + * @return @ref XXH_OK on success. + * @return @ref XXH_ERROR on failure. + * + * @note Call this to incrementally consume blocks of data. + * + * @see @ref streaming_example "Streaming Example" + */ XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* statePtr, const void* input, size_t length); -XXH_PUBLIC_API XXH32_hash_t XXH32_digest (const XXH32_state_t* statePtr); + +/*! + * @brief Returns the calculated hash value from an @ref XXH32_state_t. + * + * @param statePtr The state struct to calculate the hash from. + * + * @pre + * @p statePtr must not be `NULL`. + * + * @return The calculated 32-bit xxHash32 value from that state. + * + * @note + * Calling XXH32_digest() will not affect @p statePtr, so you can update, + * digest, and update again. + * + * @see @ref streaming_example "Streaming Example" + */ +XXH_PUBLIC_API XXH_PUREF XXH32_hash_t XXH32_digest (const XXH32_state_t* statePtr); +#endif /* !XXH_NO_STREAM */ /******* Canonical representation *******/ -/* - * The default return values from XXH functions are unsigned 32 and 64 bit - * integers. - * This the simplest and fastest format for further post-processing. +/*! + * @brief Canonical (big endian) representation of @ref XXH32_hash_t. + */ +typedef struct { + unsigned char digest[4]; /*!< Hash bytes, big endian */ +} XXH32_canonical_t; + +/*! + * @brief Converts an @ref XXH32_hash_t to a big endian @ref XXH32_canonical_t. * - * However, this leaves open the question of what is the order on the byte level, - * since little and big endian conventions will store the same number differently. + * @param dst The @ref XXH32_canonical_t pointer to be stored to. + * @param hash The @ref XXH32_hash_t to be converted. * - * The canonical representation settles this issue by mandating big-endian - * convention, the same convention as human-readable numbers (large digits first). + * @pre + * @p dst must not be `NULL`. * - * When writing hash values to storage, sending them over a network, or printing - * them, it's highly recommended to use the canonical representation to ensure - * portability across a wider range of systems, present and future. + * @see @ref canonical_representation_example "Canonical Representation Example" + */ +XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash); + +/*! + * @brief Converts an @ref XXH32_canonical_t to a native @ref XXH32_hash_t. * - * The following functions allow transformation of hash values to and from - * canonical format. + * @param src The @ref XXH32_canonical_t to convert. + * + * @pre + * @p src must not be `NULL`. + * + * @return The converted hash. + * + * @see @ref canonical_representation_example "Canonical Representation Example" */ +XXH_PUBLIC_API XXH_PUREF XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src); + + +/*! @cond Doxygen ignores this part */ +#ifdef __has_attribute +# define XXH_HAS_ATTRIBUTE(x) __has_attribute(x) +#else +# define XXH_HAS_ATTRIBUTE(x) 0 +#endif +/*! @endcond */ + +/*! @cond Doxygen ignores this part */ +/* + * C23 __STDC_VERSION__ number hasn't been specified yet. For now + * leave as `201711L` (C17 + 1). + * TODO: Update to correct value when its been specified. + */ +#define XXH_C23_VN 201711L +/*! @endcond */ + +/*! @cond Doxygen ignores this part */ +/* C-language Attributes are added in C23. */ +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= XXH_C23_VN) && defined(__has_c_attribute) +# define XXH_HAS_C_ATTRIBUTE(x) __has_c_attribute(x) +#else +# define XXH_HAS_C_ATTRIBUTE(x) 0 +#endif +/*! @endcond */ + +/*! @cond Doxygen ignores this part */ +#if defined(__cplusplus) && defined(__has_cpp_attribute) +# define XXH_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) +#else +# define XXH_HAS_CPP_ATTRIBUTE(x) 0 +#endif +/*! @endcond */ + +/*! @cond Doxygen ignores this part */ +/* + * Define XXH_FALLTHROUGH macro for annotating switch case with the 'fallthrough' attribute + * introduced in CPP17 and C23. + * CPP17 : https://en.cppreference.com/w/cpp/language/attributes/fallthrough + * C23 : https://en.cppreference.com/w/c/language/attributes/fallthrough + */ +#if XXH_HAS_C_ATTRIBUTE(fallthrough) || XXH_HAS_CPP_ATTRIBUTE(fallthrough) +# define XXH_FALLTHROUGH [[fallthrough]] +#elif XXH_HAS_ATTRIBUTE(__fallthrough__) +# define XXH_FALLTHROUGH __attribute__ ((__fallthrough__)) +#else +# define XXH_FALLTHROUGH /* fallthrough */ +#endif +/*! @endcond */ + +/*! @cond Doxygen ignores this part */ +/* + * Define XXH_NOESCAPE for annotated pointers in public API. + * https://clang.llvm.org/docs/AttributeReference.html#noescape + * As of writing this, only supported by clang. + */ +#if XXH_HAS_ATTRIBUTE(noescape) +# define XXH_NOESCAPE __attribute__((__noescape__)) +#else +# define XXH_NOESCAPE +#endif +/*! @endcond */ -typedef struct { unsigned char digest[4]; } XXH32_canonical_t; -XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash); -XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src); +/*! + * @} + * @ingroup public + * @{ + */ #ifndef XXH_NO_LONG_LONG /*-********************************************************************** * 64-bit hash ************************************************************************/ -#if !defined (__VMS) \ +#if defined(XXH_DOXYGEN) /* don't include */ +/*! + * @brief An unsigned 64-bit integer. + * + * Not necessarily defined to `uint64_t` but functionally equivalent. + */ +typedef uint64_t XXH64_hash_t; +#elif !defined (__VMS) \ && (defined (__cplusplus) \ || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) -# include - typedef uint64_t XXH64_hash_t; +# ifdef _AIX +# include +# else +# include +# endif + typedef uint64_t XXH64_hash_t; #else - /* the following type must have a width of 64-bit */ - typedef unsigned long long XXH64_hash_t; +# include +# if defined(__LP64__) && ULONG_MAX == 0xFFFFFFFFFFFFFFFFULL + /* LP64 ABI says uint64_t is unsigned long */ + typedef unsigned long XXH64_hash_t; +# else + /* the following type must have a width of 64-bit */ + typedef unsigned long long XXH64_hash_t; +# endif #endif /*! - * XXH64(): - * Returns the 64-bit hash of sequence of length @length stored at memory - * address @input. - * @seed can be used to alter the result predictably. + * @} + * + * @defgroup XXH64_family XXH64 family + * @ingroup public + * @{ + * Contains functions used in the classic 64-bit xxHash algorithm. + * + * @note + * XXH3 provides competitive speed for both 32-bit and 64-bit systems, + * and offers true 64/128 bit hash results. + * It provides better speed for systems with vector processing capabilities. + */ + +/*! + * @brief Calculates the 64-bit hash of @p input using xxHash64. + * + * @param input The block of data to be hashed, at least @p length bytes in size. + * @param length The length of @p input, in bytes. + * @param seed The 64-bit seed to alter the hash's output predictably. + * + * @pre + * The memory between @p input and @p input + @p length must be valid, + * readable, contiguous memory. However, if @p length is `0`, @p input may be + * `NULL`. In C++, this also must be *TriviallyCopyable*. * - * This function usually runs faster on 64-bit systems, but slower on 32-bit - * systems (see benchmark). + * @return The calculated 64-bit xxHash64 value. * - * Note: XXH3 provides competitive speed for both 32-bit and 64-bit systems, - * and offers true 64/128 bit hash results. It provides a superior level of - * dispersion, and greatly reduces the risks of collisions. + * @see @ref single_shot_example "Single Shot Example" for an example. */ -XXH_PUBLIC_API XXH64_hash_t XXH64 (const void* input, size_t length, XXH64_hash_t seed); +XXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH64(XXH_NOESCAPE const void* input, size_t length, XXH64_hash_t seed); /******* Streaming *******/ +#ifndef XXH_NO_STREAM +/*! + * @brief The opaque state struct for the XXH64 streaming API. + * + * @see XXH64_state_s for details. + * @see @ref streaming_example "Streaming Example" + */ typedef struct XXH64_state_s XXH64_state_t; /* incomplete type */ -XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void); + +/*! + * @brief Allocates an @ref XXH64_state_t. + * + * @return An allocated pointer of @ref XXH64_state_t on success. + * @return `NULL` on failure. + * + * @note Must be freed with XXH64_freeState(). + * + * @see @ref streaming_example "Streaming Example" + */ +XXH_PUBLIC_API XXH_MALLOCF XXH64_state_t* XXH64_createState(void); + +/*! + * @brief Frees an @ref XXH64_state_t. + * + * @param statePtr A pointer to an @ref XXH64_state_t allocated with @ref XXH64_createState(). + * + * @return @ref XXH_OK. + * + * @note @p statePtr must be allocated with XXH64_createState(). + * + * @see @ref streaming_example "Streaming Example" + */ XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr); -XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dst_state, const XXH64_state_t* src_state); -XXH_PUBLIC_API XXH_errorcode XXH64_reset (XXH64_state_t* statePtr, XXH64_hash_t seed); -XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* statePtr, const void* input, size_t length); -XXH_PUBLIC_API XXH64_hash_t XXH64_digest (const XXH64_state_t* statePtr); +/*! + * @brief Copies one @ref XXH64_state_t to another. + * + * @param dst_state The state to copy to. + * @param src_state The state to copy from. + * @pre + * @p dst_state and @p src_state must not be `NULL` and must not overlap. + */ +XXH_PUBLIC_API void XXH64_copyState(XXH_NOESCAPE XXH64_state_t* dst_state, const XXH64_state_t* src_state); + +/*! + * @brief Resets an @ref XXH64_state_t to begin a new hash. + * + * @param statePtr The state struct to reset. + * @param seed The 64-bit seed to alter the hash result predictably. + * + * @pre + * @p statePtr must not be `NULL`. + * + * @return @ref XXH_OK on success. + * @return @ref XXH_ERROR on failure. + * + * @note This function resets and seeds a state. Call it before @ref XXH64_update(). + * + * @see @ref streaming_example "Streaming Example" + */ +XXH_PUBLIC_API XXH_errorcode XXH64_reset (XXH_NOESCAPE XXH64_state_t* statePtr, XXH64_hash_t seed); + +/*! + * @brief Consumes a block of @p input to an @ref XXH64_state_t. + * + * @param statePtr The state struct to update. + * @param input The block of data to be hashed, at least @p length bytes in size. + * @param length The length of @p input, in bytes. + * + * @pre + * @p statePtr must not be `NULL`. + * @pre + * The memory between @p input and @p input + @p length must be valid, + * readable, contiguous memory. However, if @p length is `0`, @p input may be + * `NULL`. In C++, this also must be *TriviallyCopyable*. + * + * @return @ref XXH_OK on success. + * @return @ref XXH_ERROR on failure. + * + * @note Call this to incrementally consume blocks of data. + * + * @see @ref streaming_example "Streaming Example" + */ +XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH_NOESCAPE XXH64_state_t* statePtr, XXH_NOESCAPE const void* input, size_t length); +/*! + * @brief Returns the calculated hash value from an @ref XXH64_state_t. + * + * @param statePtr The state struct to calculate the hash from. + * + * @pre + * @p statePtr must not be `NULL`. + * + * @return The calculated 64-bit xxHash64 value from that state. + * + * @note + * Calling XXH64_digest() will not affect @p statePtr, so you can update, + * digest, and update again. + * + * @see @ref streaming_example "Streaming Example" + */ +XXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH64_digest (XXH_NOESCAPE const XXH64_state_t* statePtr); +#endif /* !XXH_NO_STREAM */ /******* Canonical representation *******/ + +/*! + * @brief Canonical (big endian) representation of @ref XXH64_hash_t. + */ typedef struct { unsigned char digest[sizeof(XXH64_hash_t)]; } XXH64_canonical_t; -XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash); -XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src); + +/*! + * @brief Converts an @ref XXH64_hash_t to a big endian @ref XXH64_canonical_t. + * + * @param dst The @ref XXH64_canonical_t pointer to be stored to. + * @param hash The @ref XXH64_hash_t to be converted. + * + * @pre + * @p dst must not be `NULL`. + * + * @see @ref canonical_representation_example "Canonical Representation Example" + */ +XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH_NOESCAPE XXH64_canonical_t* dst, XXH64_hash_t hash); + +/*! + * @brief Converts an @ref XXH64_canonical_t to a native @ref XXH64_hash_t. + * + * @param src The @ref XXH64_canonical_t to convert. + * + * @pre + * @p src must not be `NULL`. + * + * @return The converted hash. + * + * @see @ref canonical_representation_example "Canonical Representation Example" + */ +XXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH64_hashFromCanonical(XXH_NOESCAPE const XXH64_canonical_t* src); + +#ifndef XXH_NO_XXH3 + +/*! + * @} + * ************************************************************************ + * @defgroup XXH3_family XXH3 family + * @ingroup public + * @{ + * + * XXH3 is a more recent hash algorithm featuring: + * - Improved speed for both small and large inputs + * - True 64-bit and 128-bit outputs + * - SIMD acceleration + * - Improved 32-bit viability + * + * Speed analysis methodology is explained here: + * + * https://fastcompression.blogspot.com/2019/03/presenting-xxh3.html + * + * Compared to XXH64, expect XXH3 to run approximately + * ~2x faster on large inputs and >3x faster on small ones, + * exact differences vary depending on platform. + * + * XXH3's speed benefits greatly from SIMD and 64-bit arithmetic, + * but does not require it. + * Most 32-bit and 64-bit targets that can run XXH32 smoothly can run XXH3 + * at competitive speeds, even without vector support. Further details are + * explained in the implementation. + * + * XXH3 has a fast scalar implementation, but it also includes accelerated SIMD + * implementations for many common platforms: + * - AVX512 + * - AVX2 + * - SSE2 + * - ARM NEON + * - WebAssembly SIMD128 + * - POWER8 VSX + * - s390x ZVector + * This can be controlled via the @ref XXH_VECTOR macro, but it automatically + * selects the best version according to predefined macros. For the x86 family, an + * automatic runtime dispatcher is included separately in @ref xxh_x86dispatch.c. + * + * XXH3 implementation is portable: + * it has a generic C90 formulation that can be compiled on any platform, + * all implementations generate exactly the same hash value on all platforms. + * Starting from v0.8.0, it's also labelled "stable", meaning that + * any future version will also generate the same hash value. + * + * XXH3 offers 2 variants, _64bits and _128bits. + * + * When only 64 bits are needed, prefer invoking the _64bits variant, as it + * reduces the amount of mixing, resulting in faster speed on small inputs. + * It's also generally simpler to manipulate a scalar return type than a struct. + * + * The API supports one-shot hashing, streaming mode, and custom secrets. + */ + +/*! + * @ingroup tuning + * @brief Possible values for @ref XXH_VECTOR. + * + * Unless set explicitly, determined automatically. + */ +# define XXH_SCALAR 0 /*!< Portable scalar version */ +# define XXH_SSE2 1 /*!< SSE2 for Pentium 4, Opteron, all x86_64. */ +# define XXH_AVX2 2 /*!< AVX2 for Haswell and Bulldozer */ +# define XXH_AVX512 3 /*!< AVX512 for Skylake and Icelake */ +# define XXH_NEON 4 /*!< NEON for most ARMv7-A, all AArch64, and WASM SIMD128 */ +# define XXH_VSX 5 /*!< VSX and ZVector for POWER8/z13 (64-bit) */ +# define XXH_SVE 6 /*!< SVE for some ARMv8-A and ARMv9-A */ +# define XXH_LSX 7 /*!< LSX (128-bit SIMD) for LoongArch64 */ + + +/*-********************************************************************** +* XXH3 64-bit variant +************************************************************************/ + +/*! + * @brief Calculates 64-bit unseeded variant of XXH3 hash of @p input. + * + * @param input The block of data to be hashed, at least @p length bytes in size. + * @param length The length of @p input, in bytes. + * + * @pre + * The memory between @p input and @p input + @p length must be valid, + * readable, contiguous memory. However, if @p length is `0`, @p input may be + * `NULL`. In C++, this also must be *TriviallyCopyable*. + * + * @return The calculated 64-bit XXH3 hash value. + * + * @note + * This is equivalent to @ref XXH3_64bits_withSeed() with a seed of `0`, however + * it may have slightly better performance due to constant propagation of the + * defaults. + * + * @see + * XXH3_64bits_withSeed(), XXH3_64bits_withSecret(): other seeding variants + * @see @ref single_shot_example "Single Shot Example" for an example. + */ +XXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH3_64bits(XXH_NOESCAPE const void* input, size_t length); + +/*! + * @brief Calculates 64-bit seeded variant of XXH3 hash of @p input. + * + * @param input The block of data to be hashed, at least @p length bytes in size. + * @param length The length of @p input, in bytes. + * @param seed The 64-bit seed to alter the hash result predictably. + * + * @pre + * The memory between @p input and @p input + @p length must be valid, + * readable, contiguous memory. However, if @p length is `0`, @p input may be + * `NULL`. In C++, this also must be *TriviallyCopyable*. + * + * @return The calculated 64-bit XXH3 hash value. + * + * @note + * seed == 0 produces the same results as @ref XXH3_64bits(). + * + * This variant generates a custom secret on the fly based on default secret + * altered using the @p seed value. + * + * While this operation is decently fast, note that it's not completely free. + * + * @see @ref single_shot_example "Single Shot Example" for an example. + */ +XXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH3_64bits_withSeed(XXH_NOESCAPE const void* input, size_t length, XXH64_hash_t seed); + +/*! + * The bare minimum size for a custom secret. + * + * @see + * XXH3_64bits_withSecret(), XXH3_64bits_reset_withSecret(), + * XXH3_128bits_withSecret(), XXH3_128bits_reset_withSecret(). + */ +#define XXH3_SECRET_SIZE_MIN 136 + +/*! + * @brief Calculates 64-bit variant of XXH3 with a custom "secret". + * + * @param data The block of data to be hashed, at least @p len bytes in size. + * @param len The length of @p data, in bytes. + * @param secret The secret data. + * @param secretSize The length of @p secret, in bytes. + * + * @return The calculated 64-bit XXH3 hash value. + * + * @pre + * The memory between @p data and @p data + @p len must be valid, + * readable, contiguous memory. However, if @p length is `0`, @p data may be + * `NULL`. In C++, this also must be *TriviallyCopyable*. + * + * It's possible to provide any blob of bytes as a "secret" to generate the hash. + * This makes it more difficult for an external actor to prepare an intentional collision. + * The main condition is that @p secretSize *must* be large enough (>= @ref XXH3_SECRET_SIZE_MIN). + * However, the quality of the secret impacts the dispersion of the hash algorithm. + * Therefore, the secret _must_ look like a bunch of random bytes. + * Avoid "trivial" or structured data such as repeated sequences or a text document. + * Whenever in doubt about the "randomness" of the blob of bytes, + * consider employing @ref XXH3_generateSecret() instead (see below). + * It will generate a proper high entropy secret derived from the blob of bytes. + * Another advantage of using XXH3_generateSecret() is that + * it guarantees that all bits within the initial blob of bytes + * will impact every bit of the output. + * This is not necessarily the case when using the blob of bytes directly + * because, when hashing _small_ inputs, only a portion of the secret is employed. + * + * @see @ref single_shot_example "Single Shot Example" for an example. + */ +XXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH3_64bits_withSecret(XXH_NOESCAPE const void* data, size_t len, XXH_NOESCAPE const void* secret, size_t secretSize); + + +/******* Streaming *******/ +#ifndef XXH_NO_STREAM +/* + * Streaming requires state maintenance. + * This operation costs memory and CPU. + * As a consequence, streaming is slower than one-shot hashing. + * For better performance, prefer one-shot functions whenever applicable. + */ + +/*! + * @brief The opaque state struct for the XXH3 streaming API. + * + * @see XXH3_state_s for details. + * @see @ref streaming_example "Streaming Example" + */ +typedef struct XXH3_state_s XXH3_state_t; +XXH_PUBLIC_API XXH_MALLOCF XXH3_state_t* XXH3_createState(void); +XXH_PUBLIC_API XXH_errorcode XXH3_freeState(XXH3_state_t* statePtr); + +/*! + * @brief Copies one @ref XXH3_state_t to another. + * + * @param dst_state The state to copy to. + * @param src_state The state to copy from. + * @pre + * @p dst_state and @p src_state must not be `NULL` and must not overlap. + */ +XXH_PUBLIC_API void XXH3_copyState(XXH_NOESCAPE XXH3_state_t* dst_state, XXH_NOESCAPE const XXH3_state_t* src_state); + +/*! + * @brief Resets an @ref XXH3_state_t to begin a new hash. + * + * @param statePtr The state struct to reset. + * + * @pre + * @p statePtr must not be `NULL`. + * + * @return @ref XXH_OK on success. + * @return @ref XXH_ERROR on failure. + * + * @note + * - This function resets `statePtr` and generate a secret with default parameters. + * - Call this function before @ref XXH3_64bits_update(). + * - Digest will be equivalent to `XXH3_64bits()`. + * + * @see @ref streaming_example "Streaming Example" + * + */ +XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset(XXH_NOESCAPE XXH3_state_t* statePtr); + +/*! + * @brief Resets an @ref XXH3_state_t with 64-bit seed to begin a new hash. + * + * @param statePtr The state struct to reset. + * @param seed The 64-bit seed to alter the hash result predictably. + * + * @pre + * @p statePtr must not be `NULL`. + * + * @return @ref XXH_OK on success. + * @return @ref XXH_ERROR on failure. + * + * @note + * - This function resets `statePtr` and generate a secret from `seed`. + * - Call this function before @ref XXH3_64bits_update(). + * - Digest will be equivalent to `XXH3_64bits_withSeed()`. + * + * @see @ref streaming_example "Streaming Example" + * + */ +XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSeed(XXH_NOESCAPE XXH3_state_t* statePtr, XXH64_hash_t seed); + +/*! + * @brief Resets an @ref XXH3_state_t with secret data to begin a new hash. + * + * @param statePtr The state struct to reset. + * @param secret The secret data. + * @param secretSize The length of @p secret, in bytes. + * + * @pre + * @p statePtr must not be `NULL`. + * + * @return @ref XXH_OK on success. + * @return @ref XXH_ERROR on failure. + * + * @note + * `secret` is referenced, it _must outlive_ the hash streaming session. + * + * Similar to one-shot API, `secretSize` must be >= @ref XXH3_SECRET_SIZE_MIN, + * and the quality of produced hash values depends on secret's entropy + * (secret's content should look like a bunch of random bytes). + * When in doubt about the randomness of a candidate `secret`, + * consider employing `XXH3_generateSecret()` instead (see below). + * + * @see @ref streaming_example "Streaming Example" + */ +XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSecret(XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* secret, size_t secretSize); + +/*! + * @brief Consumes a block of @p input to an @ref XXH3_state_t. + * + * @param statePtr The state struct to update. + * @param input The block of data to be hashed, at least @p length bytes in size. + * @param length The length of @p input, in bytes. + * + * @pre + * @p statePtr must not be `NULL`. + * @pre + * The memory between @p input and @p input + @p length must be valid, + * readable, contiguous memory. However, if @p length is `0`, @p input may be + * `NULL`. In C++, this also must be *TriviallyCopyable*. + * + * @return @ref XXH_OK on success. + * @return @ref XXH_ERROR on failure. + * + * @note Call this to incrementally consume blocks of data. + * + * @see @ref streaming_example "Streaming Example" + */ +XXH_PUBLIC_API XXH_errorcode XXH3_64bits_update (XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* input, size_t length); + +/*! + * @brief Returns the calculated XXH3 64-bit hash value from an @ref XXH3_state_t. + * + * @param statePtr The state struct to calculate the hash from. + * + * @pre + * @p statePtr must not be `NULL`. + * + * @return The calculated XXH3 64-bit hash value from that state. + * + * @note + * Calling XXH3_64bits_digest() will not affect @p statePtr, so you can update, + * digest, and update again. + * + * @see @ref streaming_example "Streaming Example" + */ +XXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH3_64bits_digest (XXH_NOESCAPE const XXH3_state_t* statePtr); +#endif /* !XXH_NO_STREAM */ + +/* note : canonical representation of XXH3 is the same as XXH64 + * since they both produce XXH64_hash_t values */ /*-********************************************************************** -* XXH3 64-bit variant +* XXH3 128-bit variant ************************************************************************/ -/* ************************************************************************ - * XXH3 is a new hash algorithm featuring: - * - Improved speed for both small and large inputs - * - True 64-bit and 128-bit outputs - * - SIMD acceleration - * - Improved 32-bit viability +/*! + * @brief The return value from 128-bit hashes. * - * Speed analysis methodology is explained here: + * Stored in little endian order, although the fields themselves are in native + * endianness. + */ +typedef struct { + XXH64_hash_t low64; /*!< `value & 0xFFFFFFFFFFFFFFFF` */ + XXH64_hash_t high64; /*!< `value >> 64` */ +} XXH128_hash_t; + +/*! + * @brief Calculates 128-bit unseeded variant of XXH3 of @p data. * - * https://fastcompression.blogspot.com/2019/03/presenting-xxh3.html + * @param data The block of data to be hashed, at least @p length bytes in size. + * @param len The length of @p data, in bytes. * - * In general, expect XXH3 to run about ~2x faster on large inputs and >3x - * faster on small ones compared to XXH64, though exact differences depend on - * the platform. + * @return The calculated 128-bit variant of XXH3 value. * - * The algorithm is portable: Like XXH32 and XXH64, it generates the same hash - * on all platforms. + * The 128-bit variant of XXH3 has more strength, but it has a bit of overhead + * for shorter inputs. * - * It benefits greatly from SIMD and 64-bit arithmetic, but does not require it. + * This is equivalent to @ref XXH3_128bits_withSeed() with a seed of `0`, however + * it may have slightly better performance due to constant propagation of the + * defaults. * - * Almost all 32-bit and 64-bit targets that can run XXH32 smoothly can run - * XXH3 at competitive speeds, even if XXH64 runs slowly. Further details are - * explained in the implementation. + * @see XXH3_128bits_withSeed(), XXH3_128bits_withSecret(): other seeding variants + * @see @ref single_shot_example "Single Shot Example" for an example. + */ +XXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH3_128bits(XXH_NOESCAPE const void* data, size_t len); +/*! @brief Calculates 128-bit seeded variant of XXH3 hash of @p data. * - * Optimized implementations are provided for AVX512, AVX2, SSE2, NEON, POWER8, - * ZVector and scalar targets. This can be controlled with the XXH_VECTOR macro. + * @param data The block of data to be hashed, at least @p length bytes in size. + * @param len The length of @p data, in bytes. + * @param seed The 64-bit seed to alter the hash result predictably. * - * XXH3 offers 2 variants, _64bits and _128bits. - * When only 64 bits are needed, prefer calling the _64bits variant, as it - * reduces the amount of mixing, resulting in faster speed on small inputs. + * @return The calculated 128-bit variant of XXH3 value. * - * It's also generally simpler to manipulate a scalar return type than a struct. + * @note + * seed == 0 produces the same results as @ref XXH3_64bits(). * - * The 128-bit version adds additional strength, but it is slightly slower. + * This variant generates a custom secret on the fly based on default secret + * altered using the @p seed value. * - * The XXH3 algorithm is still in development. - * The results it produces may still change in future versions. + * While this operation is decently fast, note that it's not completely free. * - * Results produced by v0.7.x are not comparable with results from v0.7.y. - * However, the API is completely stable, and it can safely be used for - * ephemeral data (local sessions). + * @see XXH3_128bits(), XXH3_128bits_withSecret(): other seeding variants + * @see @ref single_shot_example "Single Shot Example" for an example. + */ +XXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH3_128bits_withSeed(XXH_NOESCAPE const void* data, size_t len, XXH64_hash_t seed); +/*! + * @brief Calculates 128-bit variant of XXH3 with a custom "secret". * - * Avoid storing values in long-term storage until the algorithm is finalized. - * XXH3's return values will be officially finalized upon reaching v0.8.0. + * @param data The block of data to be hashed, at least @p len bytes in size. + * @param len The length of @p data, in bytes. + * @param secret The secret data. + * @param secretSize The length of @p secret, in bytes. * - * After which, return values of XXH3 and XXH128 will no longer change in - * future versions. + * @return The calculated 128-bit variant of XXH3 value. * - * The API supports one-shot hashing, streaming mode, and custom secrets. - */ - -/* XXH3_64bits(): - * default 64-bit variant, using default secret and default seed of 0. - * It's the fastest variant. */ -XXH_PUBLIC_API XXH64_hash_t XXH3_64bits(const void* data, size_t len); - -/* - * XXH3_64bits_withSeed(): - * This variant generates a custom secret on the fly - * based on default secret altered using the `seed` value. - * While this operation is decently fast, note that it's not completely free. - * Note: seed==0 produces the same results as XXH3_64bits(). - */ -XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSeed(const void* data, size_t len, XXH64_hash_t seed); - -/* - * XXH3_64bits_withSecret(): * It's possible to provide any blob of bytes as a "secret" to generate the hash. * This makes it more difficult for an external actor to prepare an intentional collision. - * The main condition is that secretSize *must* be large enough (>= XXH3_SECRET_SIZE_MIN). - * However, the quality of produced hash values depends on secret's entropy. - * Technically, the secret must look like a bunch of random bytes. + * The main condition is that @p secretSize *must* be large enough (>= @ref XXH3_SECRET_SIZE_MIN). + * However, the quality of the secret impacts the dispersion of the hash algorithm. + * Therefore, the secret _must_ look like a bunch of random bytes. * Avoid "trivial" or structured data such as repeated sequences or a text document. - * Whenever unsure about the "randomness" of the blob of bytes, - * consider relabelling it as a "custom seed" instead, - * and employ "XXH3_generateSecret()" (see below) - * to generate a high entropy secret derived from the custom seed. + * Whenever in doubt about the "randomness" of the blob of bytes, + * consider employing @ref XXH3_generateSecret() instead (see below). + * It will generate a proper high entropy secret derived from the blob of bytes. + * Another advantage of using XXH3_generateSecret() is that + * it guarantees that all bits within the initial blob of bytes + * will impact every bit of the output. + * This is not necessarily the case when using the blob of bytes directly + * because, when hashing _small_ inputs, only a portion of the secret is employed. + * + * @see @ref single_shot_example "Single Shot Example" for an example. */ -#define XXH3_SECRET_SIZE_MIN 136 -XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSecret(const void* data, size_t len, const void* secret, size_t secretSize); - +XXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH3_128bits_withSecret(XXH_NOESCAPE const void* data, size_t len, XXH_NOESCAPE const void* secret, size_t secretSize); /******* Streaming *******/ +#ifndef XXH_NO_STREAM /* * Streaming requires state maintenance. * This operation costs memory and CPU. * As a consequence, streaming is slower than one-shot hashing. * For better performance, prefer one-shot functions whenever applicable. + * + * XXH3_128bits uses the same XXH3_state_t as XXH3_64bits(). + * Use already declared XXH3_createState() and XXH3_freeState(). + * + * All reset and streaming functions have same meaning as their 64-bit counterpart. */ -typedef struct XXH3_state_s XXH3_state_t; -XXH_PUBLIC_API XXH3_state_t* XXH3_createState(void); -XXH_PUBLIC_API XXH_errorcode XXH3_freeState(XXH3_state_t* statePtr); -XXH_PUBLIC_API void XXH3_copyState(XXH3_state_t* dst_state, const XXH3_state_t* src_state); -/* - * XXH3_64bits_reset(): - * Initialize with default parameters. - * digest will be equivalent to `XXH3_64bits()`. +/*! + * @brief Resets an @ref XXH3_state_t to begin a new hash. + * + * @param statePtr The state struct to reset. + * + * @pre + * @p statePtr must not be `NULL`. + * + * @return @ref XXH_OK on success. + * @return @ref XXH_ERROR on failure. + * + * @note + * - This function resets `statePtr` and generate a secret with default parameters. + * - Call it before @ref XXH3_128bits_update(). + * - Digest will be equivalent to `XXH3_128bits()`. + * + * @see @ref streaming_example "Streaming Example" */ -XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset(XXH3_state_t* statePtr); -/* - * XXH3_64bits_reset_withSeed(): - * Generate a custom secret from `seed`, and store it into `statePtr`. - * digest will be equivalent to `XXH3_64bits_withSeed()`. +XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset(XXH_NOESCAPE XXH3_state_t* statePtr); + +/*! + * @brief Resets an @ref XXH3_state_t with 64-bit seed to begin a new hash. + * + * @param statePtr The state struct to reset. + * @param seed The 64-bit seed to alter the hash result predictably. + * + * @pre + * @p statePtr must not be `NULL`. + * + * @return @ref XXH_OK on success. + * @return @ref XXH_ERROR on failure. + * + * @note + * - This function resets `statePtr` and generate a secret from `seed`. + * - Call it before @ref XXH3_128bits_update(). + * - Digest will be equivalent to `XXH3_128bits_withSeed()`. + * + * @see @ref streaming_example "Streaming Example" */ -XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSeed(XXH3_state_t* statePtr, XXH64_hash_t seed); -/* - * XXH3_64bits_reset_withSecret(): +XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSeed(XXH_NOESCAPE XXH3_state_t* statePtr, XXH64_hash_t seed); +/*! + * @brief Resets an @ref XXH3_state_t with secret data to begin a new hash. + * + * @param statePtr The state struct to reset. + * @param secret The secret data. + * @param secretSize The length of @p secret, in bytes. + * + * @pre + * @p statePtr must not be `NULL`. + * + * @return @ref XXH_OK on success. + * @return @ref XXH_ERROR on failure. + * * `secret` is referenced, it _must outlive_ the hash streaming session. - * Similar to one-shot API, `secretSize` must be >= `XXH3_SECRET_SIZE_MIN`, + * Similar to one-shot API, `secretSize` must be >= @ref XXH3_SECRET_SIZE_MIN, * and the quality of produced hash values depends on secret's entropy * (secret's content should look like a bunch of random bytes). * When in doubt about the randomness of a candidate `secret`, * consider employing `XXH3_generateSecret()` instead (see below). + * + * @see @ref streaming_example "Streaming Example" */ -XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSecret(XXH3_state_t* statePtr, const void* secret, size_t secretSize); - -XXH_PUBLIC_API XXH_errorcode XXH3_64bits_update (XXH3_state_t* statePtr, const void* input, size_t length); -XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_digest (const XXH3_state_t* statePtr); - -/* note : canonical representation of XXH3 is the same as XXH64 - * since they both produce XXH64_hash_t values */ - - -/*-********************************************************************** -* XXH3 128-bit variant -************************************************************************/ - -typedef struct { - XXH64_hash_t low64; - XXH64_hash_t high64; -} XXH128_hash_t; +XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSecret(XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* secret, size_t secretSize); -XXH_PUBLIC_API XXH128_hash_t XXH3_128bits(const void* data, size_t len); -XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSeed(const void* data, size_t len, XXH64_hash_t seed); -XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSecret(const void* data, size_t len, const void* secret, size_t secretSize); - -/******* Streaming *******/ -/* - * Streaming requires state maintenance. - * This operation costs memory and CPU. - * As a consequence, streaming is slower than one-shot hashing. - * For better performance, prefer one-shot functions whenever applicable. +/*! + * @brief Consumes a block of @p input to an @ref XXH3_state_t. * - * XXH3_128bits uses the same XXH3_state_t as XXH3_64bits(). - * Use already declared XXH3_createState() and XXH3_freeState(). + * Call this to incrementally consume blocks of data. + * + * @param statePtr The state struct to update. + * @param input The block of data to be hashed, at least @p length bytes in size. + * @param length The length of @p input, in bytes. + * + * @pre + * @p statePtr must not be `NULL`. + * + * @return @ref XXH_OK on success. + * @return @ref XXH_ERROR on failure. + * + * @note + * The memory between @p input and @p input + @p length must be valid, + * readable, contiguous memory. However, if @p length is `0`, @p input may be + * `NULL`. In C++, this also must be *TriviallyCopyable*. * - * All reset and streaming functions have same meaning as their 64-bit counterpart. */ +XXH_PUBLIC_API XXH_errorcode XXH3_128bits_update (XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* input, size_t length); -XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset(XXH3_state_t* statePtr); -XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSeed(XXH3_state_t* statePtr, XXH64_hash_t seed); -XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSecret(XXH3_state_t* statePtr, const void* secret, size_t secretSize); - -XXH_PUBLIC_API XXH_errorcode XXH3_128bits_update (XXH3_state_t* statePtr, const void* input, size_t length); -XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_digest (const XXH3_state_t* statePtr); +/*! + * @brief Returns the calculated XXH3 128-bit hash value from an @ref XXH3_state_t. + * + * @param statePtr The state struct to calculate the hash from. + * + * @pre + * @p statePtr must not be `NULL`. + * + * @return The calculated XXH3 128-bit hash value from that state. + * + * @note + * Calling XXH3_128bits_digest() will not affect @p statePtr, so you can update, + * digest, and update again. + * + */ +XXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH3_128bits_digest (XXH_NOESCAPE const XXH3_state_t* statePtr); +#endif /* !XXH_NO_STREAM */ /* Following helper functions make it possible to compare XXH128_hast_t values. * Since XXH128_hash_t is a structure, this capability is not offered by the language. * Note: For better performance, these functions can be inlined using XXH_INLINE_ALL */ /*! - * XXH128_isEqual(): - * Return: 1 if `h1` and `h2` are equal, 0 if they are not. + * @brief Check equality of two XXH128_hash_t values + * + * @param h1 The 128-bit hash value. + * @param h2 Another 128-bit hash value. + * + * @return `1` if `h1` and `h2` are equal. + * @return `0` if they are not. */ -XXH_PUBLIC_API int XXH128_isEqual(XXH128_hash_t h1, XXH128_hash_t h2); +XXH_PUBLIC_API XXH_PUREF int XXH128_isEqual(XXH128_hash_t h1, XXH128_hash_t h2); /*! - * XXH128_cmp(): + * @brief Compares two @ref XXH128_hash_t * * This comparator is compatible with stdlib's `qsort()`/`bsearch()`. * - * return: >0 if *h128_1 > *h128_2 - * =0 if *h128_1 == *h128_2 - * <0 if *h128_1 < *h128_2 + * @param h128_1 Left-hand side value + * @param h128_2 Right-hand side value + * + * @return >0 if @p h128_1 > @p h128_2 + * @return =0 if @p h128_1 == @p h128_2 + * @return <0 if @p h128_1 < @p h128_2 */ -XXH_PUBLIC_API int XXH128_cmp(const void* h128_1, const void* h128_2); +XXH_PUBLIC_API XXH_PUREF int XXH128_cmp(XXH_NOESCAPE const void* h128_1, XXH_NOESCAPE const void* h128_2); /******* Canonical representation *******/ typedef struct { unsigned char digest[sizeof(XXH128_hash_t)]; } XXH128_canonical_t; -XXH_PUBLIC_API void XXH128_canonicalFromHash(XXH128_canonical_t* dst, XXH128_hash_t hash); -XXH_PUBLIC_API XXH128_hash_t XXH128_hashFromCanonical(const XXH128_canonical_t* src); +/*! + * @brief Converts an @ref XXH128_hash_t to a big endian @ref XXH128_canonical_t. + * + * @param dst The @ref XXH128_canonical_t pointer to be stored to. + * @param hash The @ref XXH128_hash_t to be converted. + * + * @pre + * @p dst must not be `NULL`. + * @see @ref canonical_representation_example "Canonical Representation Example" + */ +XXH_PUBLIC_API void XXH128_canonicalFromHash(XXH_NOESCAPE XXH128_canonical_t* dst, XXH128_hash_t hash); + +/*! + * @brief Converts an @ref XXH128_canonical_t to a native @ref XXH128_hash_t. + * + * @param src The @ref XXH128_canonical_t to convert. + * + * @pre + * @p src must not be `NULL`. + * + * @return The converted hash. + * @see @ref canonical_representation_example "Canonical Representation Example" + */ +XXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH128_hashFromCanonical(XXH_NOESCAPE const XXH128_canonical_t* src); + + +#endif /* !XXH_NO_XXH3 */ #endif /* XXH_NO_LONG_LONG */ +/*! + * @} + */ #endif /* XXHASH_H_5627135585666179 */ @@ -612,35 +1657,57 @@ XXH_PUBLIC_API XXH128_hash_t XXH128_hashFromCanonical(const XXH128_canonical_t* * Never **ever** access their members directly. */ +/*! + * @internal + * @brief Structure for XXH32 streaming API. + * + * @note This is only defined when @ref XXH_STATIC_LINKING_ONLY, + * @ref XXH_INLINE_ALL, or @ref XXH_IMPLEMENTATION is defined. Otherwise it is + * an opaque type. This allows fields to safely be changed. + * + * Typedef'd to @ref XXH32_state_t. + * Do not access the members of this struct directly. + * @see XXH64_state_s, XXH3_state_s + */ struct XXH32_state_s { - XXH32_hash_t total_len_32; - XXH32_hash_t large_len; - XXH32_hash_t v1; - XXH32_hash_t v2; - XXH32_hash_t v3; - XXH32_hash_t v4; - XXH32_hash_t mem32[4]; - XXH32_hash_t memsize; - XXH32_hash_t reserved; /* never read nor write, might be removed in a future version */ + XXH32_hash_t total_len_32; /*!< Total length hashed, modulo 2^32 */ + XXH32_hash_t large_len; /*!< Whether the hash is >= 16 (handles @ref total_len_32 overflow) */ + XXH32_hash_t acc[4]; /*!< Accumulator lanes */ + unsigned char buffer[16]; /*!< Internal buffer for partial reads. */ + XXH32_hash_t bufferedSize; /*!< Amount of data in @ref buffer */ + XXH32_hash_t reserved; /*!< Reserved field. Do not read nor write to it. */ }; /* typedef'd to XXH32_state_t */ #ifndef XXH_NO_LONG_LONG /* defined when there is no 64-bit support */ +/*! + * @internal + * @brief Structure for XXH64 streaming API. + * + * @note This is only defined when @ref XXH_STATIC_LINKING_ONLY, + * @ref XXH_INLINE_ALL, or @ref XXH_IMPLEMENTATION is defined. Otherwise it is + * an opaque type. This allows fields to safely be changed. + * + * Typedef'd to @ref XXH64_state_t. + * Do not access the members of this struct directly. + * @see XXH32_state_s, XXH3_state_s + */ struct XXH64_state_s { - XXH64_hash_t total_len; - XXH64_hash_t v1; - XXH64_hash_t v2; - XXH64_hash_t v3; - XXH64_hash_t v4; - XXH64_hash_t mem64[4]; - XXH32_hash_t memsize; - XXH32_hash_t reserved32; /* required for padding anyway */ - XXH64_hash_t reserved64; /* never read nor write, might be removed in a future version */ + XXH64_hash_t total_len; /*!< Total length hashed. This is always 64-bit. */ + XXH64_hash_t acc[4]; /*!< Accumulator lanes */ + unsigned char buffer[32]; /*!< Internal buffer for partial reads.. */ + XXH32_hash_t bufferedSize; /*!< Amount of data in @ref buffer */ + XXH32_hash_t reserved32; /*!< Reserved field, needed for padding anyways*/ + XXH64_hash_t reserved64; /*!< Reserved field. Do not read or write to it. */ }; /* typedef'd to XXH64_state_t */ -#if defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) /* C11+ */ -# include +#ifndef XXH_NO_XXH3 + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) /* >= C11 */ +# define XXH_ALIGN(n) _Alignas(n) +#elif defined(__cplusplus) && (__cplusplus >= 201103L) /* >= C++11 */ +/* In C++ alignas() is a keyword */ # define XXH_ALIGN(n) alignas(n) #elif defined(__GNUC__) # define XXH_ALIGN(n) __attribute__ ((aligned(n))) @@ -652,35 +1719,89 @@ struct XXH64_state_s { /* Old GCC versions only accept the attribute after the type in structures. */ #if !(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) /* C11+ */ \ + && ! (defined(__cplusplus) && (__cplusplus >= 201103L)) /* >= C++11 */ \ && defined(__GNUC__) # define XXH_ALIGN_MEMBER(align, type) type XXH_ALIGN(align) #else # define XXH_ALIGN_MEMBER(align, type) XXH_ALIGN(align) type #endif +/*! + * @brief The size of the internal XXH3 buffer. + * + * This is the optimal update size for incremental hashing. + * + * @see XXH3_64b_update(), XXH3_128b_update(). + */ #define XXH3_INTERNALBUFFER_SIZE 256 + +/*! + * @internal + * @brief Default size of the secret buffer (and @ref XXH3_kSecret). + * + * This is the size used in @ref XXH3_kSecret and the seeded functions. + * + * Not to be confused with @ref XXH3_SECRET_SIZE_MIN. + */ #define XXH3_SECRET_DEFAULT_SIZE 192 + +/*! + * @internal + * @brief Structure for XXH3 streaming API. + * + * @note This is only defined when @ref XXH_STATIC_LINKING_ONLY, + * @ref XXH_INLINE_ALL, or @ref XXH_IMPLEMENTATION is defined. + * Otherwise it is an opaque type. + * Never use this definition in combination with dynamic library. + * This allows fields to safely be changed in the future. + * + * @note ** This structure has a strict alignment requirement of 64 bytes!! ** + * Do not allocate this with `malloc()` or `new`, + * it will not be sufficiently aligned. + * Use @ref XXH3_createState() and @ref XXH3_freeState(), or stack allocation. + * + * Typedef'd to @ref XXH3_state_t. + * Do never access the members of this struct directly. + * + * @see XXH3_INITSTATE() for stack initialization. + * @see XXH3_createState(), XXH3_freeState(). + * @see XXH32_state_s, XXH64_state_s + */ struct XXH3_state_s { XXH_ALIGN_MEMBER(64, XXH64_hash_t acc[8]); - /* used to store a custom secret generated from a seed */ + /*!< The 8 accumulators. See @ref XXH32_state_s::v and @ref XXH64_state_s::v */ XXH_ALIGN_MEMBER(64, unsigned char customSecret[XXH3_SECRET_DEFAULT_SIZE]); + /*!< Used to store a custom secret generated from a seed. */ XXH_ALIGN_MEMBER(64, unsigned char buffer[XXH3_INTERNALBUFFER_SIZE]); + /*!< The internal buffer. @see XXH32_state_s::mem32 */ XXH32_hash_t bufferedSize; - XXH32_hash_t reserved32; + /*!< The amount of memory in @ref buffer, @see XXH32_state_s::memsize */ + XXH32_hash_t useSeed; + /*!< Reserved field. Needed for padding on 64-bit. */ size_t nbStripesSoFar; + /*!< Number or stripes processed. */ XXH64_hash_t totalLen; + /*!< Total length hashed. 64-bit even on 32-bit targets. */ size_t nbStripesPerBlock; + /*!< Number of stripes per block. */ size_t secretLimit; + /*!< Size of @ref customSecret or @ref extSecret */ XXH64_hash_t seed; + /*!< Seed for _withSeed variants. Must be zero otherwise, @see XXH3_INITSTATE() */ XXH64_hash_t reserved64; - const unsigned char* extSecret; /* reference to external secret; - * if == NULL, use .customSecret instead */ + /*!< Reserved field. */ + const unsigned char* extSecret; + /*!< Reference to an external secret for the _withSecret variants, NULL + * for other variants. */ /* note: there may be some padding at the end due to alignment on 64 bytes */ }; /* typedef'd to XXH3_state_t */ #undef XXH_ALIGN_MEMBER -/* When the XXH3_state_t structure is merely emplaced on stack, +/*! + * @brief Initializes a stack-allocated `XXH3_state_s`. + * + * When the @ref XXH3_state_t structure is merely emplaced on stack, * it should be initialized with XXH3_INITSTATE() or a memset() * in case its first reset uses XXH3_NNbits_reset_withSeed(). * This init can be omitted if the first reset uses default or _withSecret mode. @@ -688,51 +1809,248 @@ struct XXH3_state_s { * Note that this doesn't prepare the state for a streaming operation, * it's still necessary to use XXH3_NNbits_reset*() afterwards. */ -#define XXH3_INITSTATE(XXH3_state_ptr) { (XXH3_state_ptr)->seed = 0; } +#define XXH3_INITSTATE(XXH3_state_ptr) \ + do { \ + XXH3_state_t* tmp_xxh3_state_ptr = (XXH3_state_ptr); \ + tmp_xxh3_state_ptr->seed = 0; \ + tmp_xxh3_state_ptr->extSecret = NULL; \ + } while(0) + + +/*! + * @brief Calculates the 128-bit hash of @p data using XXH3. + * + * @param data The block of data to be hashed, at least @p len bytes in size. + * @param len The length of @p data, in bytes. + * @param seed The 64-bit seed to alter the hash's output predictably. + * + * @pre + * The memory between @p data and @p data + @p len must be valid, + * readable, contiguous memory. However, if @p len is `0`, @p data may be + * `NULL`. In C++, this also must be *TriviallyCopyable*. + * + * @return The calculated 128-bit XXH3 value. + * + * @see @ref single_shot_example "Single Shot Example" for an example. + */ +XXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH128(XXH_NOESCAPE const void* data, size_t len, XXH64_hash_t seed); /* === Experimental API === */ /* Symbols defined below must be considered tied to a specific library version. */ -/* - * XXH3_generateSecret(): +/*! + * @brief Derive a high-entropy secret from any user-defined content, named customSeed. + * + * @param secretBuffer A writable buffer for derived high-entropy secret data. + * @param secretSize Size of secretBuffer, in bytes. Must be >= XXH3_SECRET_SIZE_MIN. + * @param customSeed A user-defined content. + * @param customSeedSize Size of customSeed, in bytes. + * + * @return @ref XXH_OK on success. + * @return @ref XXH_ERROR on failure. * - * Derive a high-entropy secret from any user-defined content, named customSeed. * The generated secret can be used in combination with `*_withSecret()` functions. - * The `_withSecret()` variants are useful to provide a higher level of protection than 64-bit seed, - * as it becomes much more difficult for an external actor to guess how to impact the calculation logic. + * The `_withSecret()` variants are useful to provide a higher level of protection + * than 64-bit seed, as it becomes much more difficult for an external actor to + * guess how to impact the calculation logic. * * The function accepts as input a custom seed of any length and any content, - * and derives from it a high-entropy secret of length XXH3_SECRET_DEFAULT_SIZE - * into an already allocated buffer secretBuffer. - * The generated secret is _always_ XXH_SECRET_DEFAULT_SIZE bytes long. + * and derives from it a high-entropy secret of length @p secretSize into an + * already allocated buffer @p secretBuffer. * * The generated secret can then be used with any `*_withSecret()` variant. - * Functions `XXH3_128bits_withSecret()`, `XXH3_64bits_withSecret()`, - * `XXH3_128bits_reset_withSecret()` and `XXH3_64bits_reset_withSecret()` + * The functions @ref XXH3_128bits_withSecret(), @ref XXH3_64bits_withSecret(), + * @ref XXH3_128bits_reset_withSecret() and @ref XXH3_64bits_reset_withSecret() * are part of this list. They all accept a `secret` parameter - * which must be very long for implementation reasons (>= XXH3_SECRET_SIZE_MIN) + * which must be large enough for implementation reasons (>= @ref XXH3_SECRET_SIZE_MIN) * _and_ feature very high entropy (consist of random-looking bytes). - * These conditions can be a high bar to meet, so - * this function can be used to generate a secret of proper quality. + * These conditions can be a high bar to meet, so @ref XXH3_generateSecret() can + * be employed to ensure proper quality. + * + * @p customSeed can be anything. It can have any size, even small ones, + * and its content can be anything, even "poor entropy" sources such as a bunch + * of zeroes. The resulting `secret` will nonetheless provide all required qualities. + * + * @pre + * - @p secretSize must be >= @ref XXH3_SECRET_SIZE_MIN + * - When @p customSeedSize > 0, supplying NULL as customSeed is undefined behavior. + * + * Example code: + * @code{.c} + * #include + * #include + * #include + * #define XXH_STATIC_LINKING_ONLY // expose unstable API + * #include "xxhash.h" + * // Hashes argv[2] using the entropy from argv[1]. + * int main(int argc, char* argv[]) + * { + * char secret[XXH3_SECRET_SIZE_MIN]; + * if (argv != 3) { return 1; } + * XXH3_generateSecret(secret, sizeof(secret), argv[1], strlen(argv[1])); + * XXH64_hash_t h = XXH3_64bits_withSecret( + * argv[2], strlen(argv[2]), + * secret, sizeof(secret) + * ); + * printf("%016llx\n", (unsigned long long) h); + * } + * @endcode + */ +XXH_PUBLIC_API XXH_errorcode XXH3_generateSecret(XXH_NOESCAPE void* secretBuffer, size_t secretSize, XXH_NOESCAPE const void* customSeed, size_t customSeedSize); + +/*! + * @brief Generate the same secret as the _withSeed() variants. + * + * @param secretBuffer A writable buffer of @ref XXH3_SECRET_DEFAULT_SIZE bytes + * @param seed The 64-bit seed to alter the hash result predictably. + * + * The generated secret can be used in combination with + *`*_withSecret()` and `_withSecretandSeed()` variants. + * + * Example C++ `std::string` hash class: + * @code{.cpp} + * #include + * #define XXH_STATIC_LINKING_ONLY // expose unstable API + * #include "xxhash.h" + * // Slow, seeds each time + * class HashSlow { + * XXH64_hash_t seed; + * public: + * HashSlow(XXH64_hash_t s) : seed{s} {} + * size_t operator()(const std::string& x) const { + * return size_t{XXH3_64bits_withSeed(x.c_str(), x.length(), seed)}; + * } + * }; + * // Fast, caches the seeded secret for future uses. + * class HashFast { + * unsigned char secret[XXH3_SECRET_DEFAULT_SIZE]; + * public: + * HashFast(XXH64_hash_t s) { + * XXH3_generateSecret_fromSeed(secret, seed); + * } + * size_t operator()(const std::string& x) const { + * return size_t{ + * XXH3_64bits_withSecret(x.c_str(), x.length(), secret, sizeof(secret)) + * }; + * } + * }; + * @endcode + */ +XXH_PUBLIC_API void XXH3_generateSecret_fromSeed(XXH_NOESCAPE void* secretBuffer, XXH64_hash_t seed); + +/*! + * @brief Maximum size of "short" key in bytes. + */ +#define XXH3_MIDSIZE_MAX 240 + +/*! + * @brief Calculates 64/128-bit seeded variant of XXH3 hash of @p data. + * + * @param data The block of data to be hashed, at least @p len bytes in size. + * @param len The length of @p data, in bytes. + * @param secret The secret data. + * @param secretSize The length of @p secret, in bytes. + * @param seed The 64-bit seed to alter the hash result predictably. + * + * These variants generate hash values using either: + * - @p seed for "short" keys (< @ref XXH3_MIDSIZE_MAX = 240 bytes) + * - @p secret for "large" keys (>= @ref XXH3_MIDSIZE_MAX). + * + * This generally benefits speed, compared to `_withSeed()` or `_withSecret()`. + * `_withSeed()` has to generate the secret on the fly for "large" keys. + * It's fast, but can be perceptible for "not so large" keys (< 1 KB). + * `_withSecret()` has to generate the masks on the fly for "small" keys, + * which requires more instructions than _withSeed() variants. + * Therefore, _withSecretandSeed variant combines the best of both worlds. + * + * When @p secret has been generated by XXH3_generateSecret_fromSeed(), + * this variant produces *exactly* the same results as `_withSeed()` variant, + * hence offering only a pure speed benefit on "large" input, + * by skipping the need to regenerate the secret for every large input. + * + * Another usage scenario is to hash the secret to a 64-bit hash value, + * for example with XXH3_64bits(), which then becomes the seed, + * and then employ both the seed and the secret in _withSecretandSeed(). + * On top of speed, an added benefit is that each bit in the secret + * has a 50% chance to swap each bit in the output, via its impact to the seed. + * + * This is not guaranteed when using the secret directly in "small data" scenarios, + * because only portions of the secret are employed for small data. + */ +XXH_PUBLIC_API XXH_PUREF XXH64_hash_t +XXH3_64bits_withSecretandSeed(XXH_NOESCAPE const void* data, size_t len, + XXH_NOESCAPE const void* secret, size_t secretSize, + XXH64_hash_t seed); + +/*! + * @brief Calculates 128-bit seeded variant of XXH3 hash of @p data. * - * customSeed can be anything. It can have any size, even small ones, - * and its content can be anything, even stupidly "low entropy" source such as a bunch of zeroes. - * The resulting `secret` will nonetheless provide all expected qualities. + * @param data The memory segment to be hashed, at least @p len bytes in size. + * @param length The length of @p data, in bytes. + * @param secret The secret used to alter hash result predictably. + * @param secretSize The length of @p secret, in bytes (must be >= XXH3_SECRET_SIZE_MIN) + * @param seed64 The 64-bit seed to alter the hash result predictably. * - * Supplying NULL as the customSeed copies the default secret into `secretBuffer`. - * When customSeedSize > 0, supplying NULL as customSeed is undefined behavior. + * @return @ref XXH_OK on success. + * @return @ref XXH_ERROR on failure. + * + * @see XXH3_64bits_withSecretandSeed(): contract is the same. */ -XXH_PUBLIC_API void XXH3_generateSecret(void* secretBuffer, const void* customSeed, size_t customSeedSize); +XXH_PUBLIC_API XXH_PUREF XXH128_hash_t +XXH3_128bits_withSecretandSeed(XXH_NOESCAPE const void* input, size_t length, + XXH_NOESCAPE const void* secret, size_t secretSize, + XXH64_hash_t seed64); +#ifndef XXH_NO_STREAM +/*! + * @brief Resets an @ref XXH3_state_t with secret data to begin a new hash. + * + * @param statePtr A pointer to an @ref XXH3_state_t allocated with @ref XXH3_createState(). + * @param secret The secret data. + * @param secretSize The length of @p secret, in bytes. + * @param seed64 The 64-bit seed to alter the hash result predictably. + * + * @return @ref XXH_OK on success. + * @return @ref XXH_ERROR on failure. + * + * @see XXH3_64bits_withSecretandSeed(). Contract is identical. + */ +XXH_PUBLIC_API XXH_errorcode +XXH3_64bits_reset_withSecretandSeed(XXH_NOESCAPE XXH3_state_t* statePtr, + XXH_NOESCAPE const void* secret, size_t secretSize, + XXH64_hash_t seed64); -/* simple short-cut to pre-selected XXH3_128bits variant */ -XXH_PUBLIC_API XXH128_hash_t XXH128(const void* data, size_t len, XXH64_hash_t seed); +/*! + * @brief Resets an @ref XXH3_state_t with secret data to begin a new hash. + * + * @param statePtr A pointer to an @ref XXH3_state_t allocated with @ref XXH3_createState(). + * @param secret The secret data. + * @param secretSize The length of @p secret, in bytes. + * @param seed64 The 64-bit seed to alter the hash result predictably. + * + * @return @ref XXH_OK on success. + * @return @ref XXH_ERROR on failure. + * + * @see XXH3_64bits_withSecretandSeed(). Contract is identical. + * + * Note: there was a bug in an earlier version of this function (<= v0.8.2) + * that would make it generate an incorrect hash value + * when @p seed == 0 and @p length < XXH3_MIDSIZE_MAX + * and @p secret is different from XXH3_generateSecret_fromSeed(). + * As stated in the contract, the correct hash result must be + * the same as XXH3_128bits_withSeed() when @p length <= XXH3_MIDSIZE_MAX. + * Results generated by this older version are wrong, hence not comparable. + */ +XXH_PUBLIC_API XXH_errorcode +XXH3_128bits_reset_withSecretandSeed(XXH_NOESCAPE XXH3_state_t* statePtr, + XXH_NOESCAPE const void* secret, size_t secretSize, + XXH64_hash_t seed64); +#endif /* !XXH_NO_STREAM */ +#endif /* !XXH_NO_XXH3 */ #endif /* XXH_NO_LONG_LONG */ - - #if defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API) # define XXH_IMPLEMENTATION #endif @@ -774,8 +2092,24 @@ XXH_PUBLIC_API XXH128_hash_t XXH128(const void* data, size_t len, XXH64_hash_t s /* ************************************* * Tuning parameters ***************************************/ + +/*! + * @defgroup tuning Tuning parameters + * @{ + * + * Various macros to control xxHash's behavior. + */ +#ifdef XXH_DOXYGEN +/*! + * @brief Define this to disable 64-bit code. + * + * Useful if only using the @ref XXH32_family and you have a strict C90 compiler. + */ +# define XXH_NO_LONG_LONG +# undef XXH_NO_LONG_LONG /* don't actually */ /*! - * XXH_FORCE_MEMORY_ACCESS: + * @brief Controls how unaligned memory is accessed. + * * By default, access to unaligned memory is controlled by `memcpy()`, which is * safe and portable. * @@ -784,77 +2118,108 @@ XXH_PUBLIC_API XXH128_hash_t XXH128(const void* data, size_t len, XXH64_hash_t s * * The below switch allow selection of a different access method * in the search for improved performance. - * Method 0 (default): - * Use `memcpy()`. Safe and portable. Default. - * Method 1: - * `__attribute__((packed))` statement. It depends on compiler extensions - * and is therefore not portable. - * This method is safe if your compiler supports it, and *generally* as - * fast or faster than `memcpy`. - * Method 2: - * Direct access via cast. This method doesn't depend on the compiler but - * violates the C standard. - * It can generate buggy code on targets which do not support unaligned - * memory accesses. - * But in some circumstances, it's the only known way to get the most - * performance (example: GCC + ARMv6) - * Method 3: - * Byteshift. This can generate the best code on old compilers which don't + * + * @par Possible options: + * + * - `XXH_FORCE_MEMORY_ACCESS=0` (default): `memcpy` + * @par + * Use `memcpy()`. Safe and portable. Note that most modern compilers will + * eliminate the function call and treat it as an unaligned access. + * + * - `XXH_FORCE_MEMORY_ACCESS=1`: `__attribute__((aligned(1)))` + * @par + * Depends on compiler extensions and is therefore not portable. + * This method is safe _if_ your compiler supports it, + * and *generally* as fast or faster than `memcpy`. + * + * - `XXH_FORCE_MEMORY_ACCESS=2`: Direct cast + * @par + * Casts directly and dereferences. This method doesn't depend on the + * compiler, but it violates the C standard as it directly dereferences an + * unaligned pointer. It can generate buggy code on targets which do not + * support unaligned memory accesses, but in some circumstances, it's the + * only known way to get the most performance. + * + * - `XXH_FORCE_MEMORY_ACCESS=3`: Byteshift + * @par + * Also portable. This can generate the best code on old compilers which don't * inline small `memcpy()` calls, and it might also be faster on big-endian - * systems which lack a native byteswap instruction. - * See https://stackoverflow.com/a/32095106/646947 for details. - * Prefer these methods in priority order (0 > 1 > 2 > 3) + * systems which lack a native byteswap instruction. However, some compilers + * will emit literal byteshifts even if the target supports unaligned access. + * + * + * @warning + * Methods 1 and 2 rely on implementation-defined behavior. Use these with + * care, as what works on one compiler/platform/optimization level may cause + * another to read garbage data or even crash. + * + * See https://fastcompression.blogspot.com/2015/08/accessing-unaligned-memory.html for details. + * + * Prefer these methods in priority order (0 > 3 > 1 > 2) */ -#ifndef XXH_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */ -# if !defined(__clang__) && defined(__GNUC__) && defined(__ARM_FEATURE_UNALIGNED) && defined(__ARM_ARCH) && (__ARM_ARCH == 6) -# define XXH_FORCE_MEMORY_ACCESS 2 -# elif !defined(__clang__) && ((defined(__INTEL_COMPILER) && !defined(_WIN32)) || \ - (defined(__GNUC__) && (defined(__ARM_ARCH) && __ARM_ARCH >= 7))) -# define XXH_FORCE_MEMORY_ACCESS 1 -# endif -#endif +# define XXH_FORCE_MEMORY_ACCESS 0 /*! - * XXH_ACCEPT_NULL_INPUT_POINTER: - * If the input pointer is NULL, xxHash's default behavior is to dereference it, - * triggering a segfault. - * When this macro is enabled, xxHash actively checks the input for a null pointer. - * If it is, the result for null input pointers is the same as a zero-length input. + * @def XXH_SIZE_OPT + * @brief Controls how much xxHash optimizes for size. + * + * xxHash, when compiled, tends to result in a rather large binary size. This + * is mostly due to heavy usage to forced inlining and constant folding of the + * @ref XXH3_family to increase performance. + * + * However, some developers prefer size over speed. This option can + * significantly reduce the size of the generated code. When using the `-Os` + * or `-Oz` options on GCC or Clang, this is defined to 1 by default, + * otherwise it is defined to 0. + * + * Most of these size optimizations can be controlled manually. + * + * This is a number from 0-2. + * - `XXH_SIZE_OPT` == 0: Default. xxHash makes no size optimizations. Speed + * comes first. + * - `XXH_SIZE_OPT` == 1: Default for `-Os` and `-Oz`. xxHash is more + * conservative and disables hacks that increase code size. It implies the + * options @ref XXH_NO_INLINE_HINTS == 1, @ref XXH_FORCE_ALIGN_CHECK == 0, + * and @ref XXH3_NEON_LANES == 8 if they are not already defined. + * - `XXH_SIZE_OPT` == 2: xxHash tries to make itself as small as possible. + * Performance may cry. For example, the single shot functions just use the + * streaming API. */ -#ifndef XXH_ACCEPT_NULL_INPUT_POINTER /* can be defined externally */ -# define XXH_ACCEPT_NULL_INPUT_POINTER 0 -#endif +# define XXH_SIZE_OPT 0 /*! - * XXH_FORCE_ALIGN_CHECK: - * This is an important performance trick - * for architectures without decent unaligned memory access performance. - * It checks for input alignment, and when conditions are met, - * uses a "fast path" employing direct 32-bit/64-bit read, - * resulting in _dramatically faster_ read speed. + * @def XXH_FORCE_ALIGN_CHECK + * @brief If defined to non-zero, adds a special path for aligned inputs (XXH32() + * and XXH64() only). * - * The check costs one initial branch per hash, which is generally negligible, but not zero. - * Moreover, it's not useful to generate binary for an additional code path - * if memory access uses same instruction for both aligned and unaligned adresses. + * This is an important performance trick for architectures without decent + * unaligned memory access performance. + * + * It checks for input alignment, and when conditions are met, uses a "fast + * path" employing direct 32-bit/64-bit reads, resulting in _dramatically + * faster_ read speed. + * + * The check costs one initial branch per hash, which is generally negligible, + * but not zero. + * + * Moreover, it's not useful to generate an additional code path if memory + * access uses the same instruction for both aligned and unaligned + * addresses (e.g. x86 and aarch64). * * In these cases, the alignment check can be removed by setting this macro to 0. * Then the code will always use unaligned memory access. - * Align check is automatically disabled on x86, x64 & arm64, + * Align check is automatically disabled on x86, x64, ARM64, and some ARM chips * which are platforms known to offer good unaligned memory accesses performance. * + * It is also disabled by default when @ref XXH_SIZE_OPT >= 1. + * * This option does not affect XXH3 (only XXH32 and XXH64). */ -#ifndef XXH_FORCE_ALIGN_CHECK /* can be defined externally */ -# if defined(__i386) || defined(__x86_64__) || defined(__aarch64__) \ - || defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM64) /* visual */ -# define XXH_FORCE_ALIGN_CHECK 0 -# else -# define XXH_FORCE_ALIGN_CHECK 1 -# endif -#endif +# define XXH_FORCE_ALIGN_CHECK 0 /*! - * XXH_NO_INLINE_HINTS: + * @def XXH_NO_INLINE_HINTS + * @brief When non-zero, sets all functions to `static`. * * By default, xxHash tries to force the compiler to inline almost all internal * functions. @@ -869,48 +2234,169 @@ XXH_PUBLIC_API XXH128_hash_t XXH128(const void* data, size_t len, XXH64_hash_t s * XXH_NO_INLINE_HINTS marks all internal functions as static, giving the * compiler full control on whether to inline or not. * - * When not optimizing (-O0), optimizing for size (-Os, -Oz), or using - * -fno-inline with GCC or Clang, this will automatically be defined. + * When not optimizing (-O0), using `-fno-inline` with GCC or Clang, or if + * @ref XXH_SIZE_OPT >= 1, this will automatically be defined. + */ +# define XXH_NO_INLINE_HINTS 0 + +/*! + * @def XXH3_INLINE_SECRET + * @brief Determines whether to inline the XXH3 withSecret code. + * + * When the secret size is known, the compiler can improve the performance + * of XXH3_64bits_withSecret() and XXH3_128bits_withSecret(). + * + * However, if the secret size is not known, it doesn't have any benefit. This + * happens when xxHash is compiled into a global symbol. Therefore, if + * @ref XXH_INLINE_ALL is *not* defined, this will be defined to 0. + * + * Additionally, this defaults to 0 on GCC 12+, which has an issue with function pointers + * that are *sometimes* force inline on -Og, and it is impossible to automatically + * detect this optimization level. + */ +# define XXH3_INLINE_SECRET 0 + +/*! + * @def XXH32_ENDJMP + * @brief Whether to use a jump for `XXH32_finalize`. + * + * For performance, `XXH32_finalize` uses multiple branches in the finalizer. + * This is generally preferable for performance, + * but depending on exact architecture, a jmp may be preferable. + * + * This setting is only possibly making a difference for very small inputs. + */ +# define XXH32_ENDJMP 0 + +/*! + * @internal + * @brief Redefines old internal names. + * + * For compatibility with code that uses xxHash's internals before the names + * were changed to improve namespacing. There is no other reason to use this. + */ +# define XXH_OLD_NAMES +# undef XXH_OLD_NAMES /* don't actually use, it is ugly. */ + +/*! + * @def XXH_NO_STREAM + * @brief Disables the streaming API. + * + * When xxHash is not inlined and the streaming functions are not used, disabling + * the streaming functions can improve code size significantly, especially with + * the @ref XXH3_family which tends to make constant folded copies of itself. + */ +# define XXH_NO_STREAM +# undef XXH_NO_STREAM /* don't actually */ +#endif /* XXH_DOXYGEN */ +/*! + * @} */ + +#ifndef XXH_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */ + /* prefer __packed__ structures (method 1) for GCC + * < ARMv7 with unaligned access (e.g. Raspbian armhf) still uses byte shifting, so we use memcpy + * which for some reason does unaligned loads. */ +# if defined(__GNUC__) && !(defined(__ARM_ARCH) && __ARM_ARCH < 7 && defined(__ARM_FEATURE_UNALIGNED)) +# define XXH_FORCE_MEMORY_ACCESS 1 +# endif +#endif + +#ifndef XXH_SIZE_OPT + /* default to 1 for -Os or -Oz */ +# if (defined(__GNUC__) || defined(__clang__)) && defined(__OPTIMIZE_SIZE__) +# define XXH_SIZE_OPT 1 +# else +# define XXH_SIZE_OPT 0 +# endif +#endif + +#ifndef XXH_FORCE_ALIGN_CHECK /* can be defined externally */ + /* don't check on sizeopt, x86, aarch64, or arm when unaligned access is available */ +# if XXH_SIZE_OPT >= 1 || \ + defined(__i386) || defined(__x86_64__) || defined(__aarch64__) || defined(__ARM_FEATURE_UNALIGNED) \ + || defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM64) || defined(_M_ARM) /* visual */ +# define XXH_FORCE_ALIGN_CHECK 0 +# else +# define XXH_FORCE_ALIGN_CHECK 1 +# endif +#endif + #ifndef XXH_NO_INLINE_HINTS -# if defined(__OPTIMIZE_SIZE__) /* -Os, -Oz */ \ - || defined(__NO_INLINE__) /* -O0, -fno-inline */ +# if XXH_SIZE_OPT >= 1 || defined(__NO_INLINE__) /* -O0, -fno-inline */ # define XXH_NO_INLINE_HINTS 1 # else # define XXH_NO_INLINE_HINTS 0 # endif #endif -/*! - * XXH_REROLL: - * Whether to reroll XXH32_finalize, and XXH64_finalize, - * instead of using an unrolled jump table/if statement loop. - * - * This is automatically defined on -Os/-Oz on GCC and Clang. - */ -#ifndef XXH_REROLL -# if defined(__OPTIMIZE_SIZE__) -# define XXH_REROLL 1 +#ifndef XXH3_INLINE_SECRET +# if (defined(__GNUC__) && !defined(__clang__) && __GNUC__ >= 12) \ + || !defined(XXH_INLINE_ALL) +# define XXH3_INLINE_SECRET 0 # else -# define XXH_REROLL 0 +# define XXH3_INLINE_SECRET 1 # endif #endif +#ifndef XXH32_ENDJMP +/* generally preferable for performance */ +# define XXH32_ENDJMP 0 +#endif + +/*! + * @defgroup impl Implementation + * @{ + */ + /* ************************************* * Includes & Memory related functions ***************************************/ -/*! +#if defined(XXH_NO_STREAM) +/* nothing */ +#elif defined(XXH_NO_STDLIB) + +/* When requesting to disable any mention of stdlib, + * the library loses the ability to invoked malloc / free. + * In practice, it means that functions like `XXH*_createState()` + * will always fail, and return NULL. + * This flag is useful in situations where + * xxhash.h is integrated into some kernel, embedded or limited environment + * without access to dynamic allocation. + */ + +static XXH_CONSTF void* XXH_malloc(size_t s) { (void)s; return NULL; } +static void XXH_free(void* p) { (void)p; } + +#else + +/* * Modify the local functions below should you wish to use * different memory routines for malloc() and free() */ #include -static void* XXH_malloc(size_t s) { return malloc(s); } +/*! + * @internal + * @brief Modify this function to use a different routine than malloc(). + */ +static XXH_MALLOCF void* XXH_malloc(size_t s) { return malloc(s); } + +/*! + * @internal + * @brief Modify this function to use a different routine than free(). + */ static void XXH_free(void* p) { free(p); } -/*! and for memcpy() */ +#endif /* XXH_NO_STDLIB */ + #include + +/*! + * @internal + * @brief Modify this function to use a different routine than memcpy(). + */ static void* XXH_memcpy(void* dest, const void* src, size_t size) { return memcpy(dest,src,size); @@ -927,19 +2413,19 @@ static void* XXH_memcpy(void* dest, const void* src, size_t size) #endif #if XXH_NO_INLINE_HINTS /* disable inlining hints */ -# if defined(__GNUC__) -# define XXH_FORCE_INLINE static __attribute__((unused)) +# if defined(__GNUC__) || defined(__clang__) +# define XXH_FORCE_INLINE static __attribute__((__unused__)) # else # define XXH_FORCE_INLINE static # endif # define XXH_NO_INLINE static /* enable inlining hints */ +#elif defined(__GNUC__) || defined(__clang__) +# define XXH_FORCE_INLINE static __inline__ __attribute__((__always_inline__, __unused__)) +# define XXH_NO_INLINE static __attribute__((__noinline__)) #elif defined(_MSC_VER) /* Visual Studio */ # define XXH_FORCE_INLINE static __forceinline # define XXH_NO_INLINE static __declspec(noinline) -#elif defined(__GNUC__) -# define XXH_FORCE_INLINE static __inline__ __attribute__((always_inline, unused)) -# define XXH_NO_INLINE static __attribute__((noinline)) #elif defined (__cplusplus) \ || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) /* C99 */ # define XXH_FORCE_INLINE static inline @@ -949,12 +2435,43 @@ static void* XXH_memcpy(void* dest, const void* src, size_t size) # define XXH_NO_INLINE static #endif +#if defined(XXH_INLINE_ALL) +# define XXH_STATIC XXH_FORCE_INLINE +#else +# define XXH_STATIC static +#endif + +#if XXH3_INLINE_SECRET +# define XXH3_WITH_SECRET_INLINE XXH_FORCE_INLINE +#else +# define XXH3_WITH_SECRET_INLINE XXH_NO_INLINE +#endif +#if ((defined(sun) || defined(__sun)) && __cplusplus) /* Solaris includes __STDC_VERSION__ with C++. Tested with GCC 5.5 */ +# define XXH_RESTRICT /* disable */ +#elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* >= C99 */ +# define XXH_RESTRICT restrict +#elif (defined (__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))) \ + || (defined (__clang__)) \ + || (defined (_MSC_VER) && (_MSC_VER >= 1400)) \ + || (defined (__INTEL_COMPILER) && (__INTEL_COMPILER >= 1300)) +/* + * There are a LOT more compilers that recognize __restrict but this + * covers the major ones. + */ +# define XXH_RESTRICT __restrict +#else +# define XXH_RESTRICT /* disable */ +#endif /* ************************************* * Debug ***************************************/ -/* +/*! + * @ingroup tuning + * @def XXH_DEBUGLEVEL + * @brief Sets the debugging level. + * * XXH_DEBUGLEVEL is expected to be defined externally, typically via the * compiler's command line options. The value must be a number. */ @@ -970,12 +2487,54 @@ static void* XXH_memcpy(void* dest, const void* src, size_t size) # include /* note: can still be disabled with NDEBUG */ # define XXH_ASSERT(c) assert(c) #else -# define XXH_ASSERT(c) ((void)0) +# if defined(__INTEL_COMPILER) +# define XXH_ASSERT(c) XXH_ASSUME((unsigned char) (c)) +# else +# define XXH_ASSERT(c) XXH_ASSUME(c) +# endif #endif /* note: use after variable declarations */ -#define XXH_STATIC_ASSERT(c) do { enum { XXH_sa = 1/(int)(!!(c)) }; } while (0) +#ifndef XXH_STATIC_ASSERT +# if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) /* C11 */ +# define XXH_STATIC_ASSERT_WITH_MESSAGE(c,m) do { _Static_assert((c),m); } while(0) +# elif defined(__cplusplus) && (__cplusplus >= 201103L) /* C++11 */ +# define XXH_STATIC_ASSERT_WITH_MESSAGE(c,m) do { static_assert((c),m); } while(0) +# else +# define XXH_STATIC_ASSERT_WITH_MESSAGE(c,m) do { struct xxh_sa { char x[(c) ? 1 : -1]; }; } while(0) +# endif +# define XXH_STATIC_ASSERT(c) XXH_STATIC_ASSERT_WITH_MESSAGE((c),#c) +#endif + +/*! + * @internal + * @def XXH_COMPILER_GUARD(var) + * @brief Used to prevent unwanted optimizations for @p var. + * + * It uses an empty GCC inline assembly statement with a register constraint + * which forces @p var into a general purpose register (eg eax, ebx, ecx + * on x86) and marks it as modified. + * + * This is used in a few places to avoid unwanted autovectorization (e.g. + * XXH32_round()). All vectorization we want is explicit via intrinsics, + * and _usually_ isn't wanted elsewhere. + * + * We also use it to prevent unwanted constant folding for AArch64 in + * XXH3_initCustomSecret_scalar(). + */ +#if defined(__GNUC__) || defined(__clang__) +# define XXH_COMPILER_GUARD(var) __asm__("" : "+r" (var)) +#else +# define XXH_COMPILER_GUARD(var) ((void)0) +#endif +/* Specifically for NEON vectors which use the "w" constraint, on + * Clang. */ +#if defined(__clang__) && defined(__ARM_ARCH) && !defined(__wasm__) +# define XXH_COMPILER_GUARD_CLANG_NEON(var) __asm__("" : "+w" (var)) +#else +# define XXH_COMPILER_GUARD_CLANG_NEON(var) ((void)0) +#endif /* ************************************* * Basic Types @@ -983,14 +2542,19 @@ static void* XXH_memcpy(void* dest, const void* src, size_t size) #if !defined (__VMS) \ && (defined (__cplusplus) \ || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) -# include - typedef uint8_t xxh_u8; +# ifdef _AIX +# include +# else +# include +# endif + typedef uint8_t xxh_u8; #else - typedef unsigned char xxh_u8; + typedef unsigned char xxh_u8; #endif typedef XXH32_hash_t xxh_u32; #ifdef XXH_OLD_NAMES +# warning "XXH_OLD_NAMES is planned to be removed starting v0.9. If the program depends on it, consider moving away from it by employing newer type names directly" # define BYTE xxh_u8 # define U8 xxh_u8 # define U32 xxh_u32 @@ -998,6 +2562,56 @@ typedef XXH32_hash_t xxh_u32; /* *** Memory access *** */ +/*! + * @internal + * @fn xxh_u32 XXH_read32(const void* ptr) + * @brief Reads an unaligned 32-bit integer from @p ptr in native endianness. + * + * Affected by @ref XXH_FORCE_MEMORY_ACCESS. + * + * @param ptr The pointer to read from. + * @return The 32-bit native endian integer from the bytes at @p ptr. + */ + +/*! + * @internal + * @fn xxh_u32 XXH_readLE32(const void* ptr) + * @brief Reads an unaligned 32-bit little endian integer from @p ptr. + * + * Affected by @ref XXH_FORCE_MEMORY_ACCESS. + * + * @param ptr The pointer to read from. + * @return The 32-bit little endian integer from the bytes at @p ptr. + */ + +/*! + * @internal + * @fn xxh_u32 XXH_readBE32(const void* ptr) + * @brief Reads an unaligned 32-bit big endian integer from @p ptr. + * + * Affected by @ref XXH_FORCE_MEMORY_ACCESS. + * + * @param ptr The pointer to read from. + * @return The 32-bit big endian integer from the bytes at @p ptr. + */ + +/*! + * @internal + * @fn xxh_u32 XXH_readLE32_align(const void* ptr, XXH_alignment align) + * @brief Like @ref XXH_readLE32(), but has an option for aligned reads. + * + * Affected by @ref XXH_FORCE_MEMORY_ACCESS. + * Note that when @ref XXH_FORCE_ALIGN_CHECK == 0, the @p align parameter is + * always @ref XXH_alignment::XXH_unaligned. + * + * @param ptr The pointer to read from. + * @param align Whether @p ptr is aligned. + * @pre + * If @p align == @ref XXH_alignment::XXH_aligned, @p ptr must be 4 byte + * aligned. + * @return The 32-bit little endian integer from the bytes at @p ptr. + */ + #if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3)) /* * Manual byteshift. Best for old compilers which don't inline memcpy. @@ -1014,46 +2628,54 @@ static xxh_u32 XXH_read32(const void* memPtr) { return *(const xxh_u32*) memPtr; #elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1)) /* - * __pack instructions are safer but compiler specific, hence potentially - * problematic for some compilers. - * - * Currently only defined for GCC and ICC. + * __attribute__((aligned(1))) is supported by gcc and clang. Originally the + * documentation claimed that it only increased the alignment, but actually it + * can decrease it on gcc, clang, and icc: + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69502, + * https://gcc.godbolt.org/z/xYez1j67Y. */ #ifdef XXH_OLD_NAMES -typedef union { xxh_u32 u32; } __attribute__((packed)) unalign; +typedef union { xxh_u32 u32; } __attribute__((__packed__)) unalign; #endif static xxh_u32 XXH_read32(const void* ptr) { - typedef union { xxh_u32 u32; } __attribute__((packed)) xxh_unalign; - return ((const xxh_unalign*)ptr)->u32; + typedef __attribute__((__aligned__(1))) xxh_u32 xxh_unalign32; + return *((const xxh_unalign32*)ptr); } #else /* * Portable and safe solution. Generally efficient. - * see: https://stackoverflow.com/a/32095106/646947 + * see: https://fastcompression.blogspot.com/2015/08/accessing-unaligned-memory.html */ static xxh_u32 XXH_read32(const void* memPtr) { xxh_u32 val; - memcpy(&val, memPtr, sizeof(val)); + XXH_memcpy(&val, memPtr, sizeof(val)); return val; } #endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */ -/* *** Endianess *** */ -typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess; +/* *** Endianness *** */ /*! - * XXH_CPU_LITTLE_ENDIAN: + * @ingroup tuning + * @def XXH_CPU_LITTLE_ENDIAN + * @brief Whether the target is little endian. + * * Defined to 1 if the target is little endian, or 0 if it is big endian. * It can be defined externally, for example on the compiler command line. * - * If it is not defined, a runtime check (which is usually constant folded) - * is used instead. + * If it is not defined, + * a runtime check (which is usually constant folded) is used instead. + * + * @note + * This is not necessarily defined to an integer constant. + * + * @see XXH_isLittleEndian() for the runtime check. */ #ifndef XXH_CPU_LITTLE_ENDIAN /* @@ -1068,8 +2690,11 @@ typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess; || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) # define XXH_CPU_LITTLE_ENDIAN 0 # else -/* - * runtime test, presumed to simplify to a constant by compiler +/*! + * @internal + * @brief Runtime check for @ref XXH_CPU_LITTLE_ENDIAN. + * + * Most compilers will constant fold this. */ static int XXH_isLittleEndian(void) { @@ -1098,10 +2723,71 @@ static int XXH_isLittleEndian(void) # define XXH_HAS_BUILTIN(x) 0 #endif + + +/* + * C23 and future versions have standard "unreachable()". + * Once it has been implemented reliably we can add it as an + * additional case: + * + * ``` + * #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= XXH_C23_VN) + * # include + * # ifdef unreachable + * # define XXH_UNREACHABLE() unreachable() + * # endif + * #endif + * ``` + * + * Note C++23 also has std::unreachable() which can be detected + * as follows: + * ``` + * #if defined(__cpp_lib_unreachable) && (__cpp_lib_unreachable >= 202202L) + * # include + * # define XXH_UNREACHABLE() std::unreachable() + * #endif + * ``` + * NB: `__cpp_lib_unreachable` is defined in the `` header. + * We don't use that as including `` in `extern "C"` blocks + * doesn't work on GCC12 + */ + +#if XXH_HAS_BUILTIN(__builtin_unreachable) +# define XXH_UNREACHABLE() __builtin_unreachable() + +#elif defined(_MSC_VER) +# define XXH_UNREACHABLE() __assume(0) + +#else +# define XXH_UNREACHABLE() +#endif + +#if XXH_HAS_BUILTIN(__builtin_assume) +# define XXH_ASSUME(c) __builtin_assume(c) +#else +# define XXH_ASSUME(c) if (!(c)) { XXH_UNREACHABLE(); } +#endif + +/*! + * @internal + * @def XXH_rotl32(x,r) + * @brief 32-bit rotate left. + * + * @param x The 32-bit integer to be rotated. + * @param r The number of bits to rotate. + * @pre + * @p r > 0 && @p r < 32 + * @note + * @p x and @p r may be evaluated multiple times. + * @return The rotated result. + */ #if !defined(NO_CLANG_BUILTIN) && XXH_HAS_BUILTIN(__builtin_rotateleft32) \ && XXH_HAS_BUILTIN(__builtin_rotateleft64) # define XXH_rotl32 __builtin_rotateleft32 # define XXH_rotl64 __builtin_rotateleft64 +#elif XXH_HAS_BUILTIN(__builtin_stdc_rotate_left) +# define XXH_rotl32 __builtin_stdc_rotate_left +# define XXH_rotl64 __builtin_stdc_rotate_left /* Note: although _rotl exists for minGW (GCC under windows), performance seems poor */ #elif defined(_MSC_VER) # define XXH_rotl32(x,r) _rotl(x,r) @@ -1111,6 +2797,14 @@ static int XXH_isLittleEndian(void) # define XXH_rotl64(x,r) (((x) << (r)) | ((x) >> (64 - (r)))) #endif +/*! + * @internal + * @fn xxh_u32 XXH_swap32(xxh_u32 x) + * @brief A 32-bit byteswap. + * + * @param x The 32-bit integer to byteswap. + * @return @p x, byteswapped. + */ #if defined(_MSC_VER) /* Visual Studio */ # define XXH_swap32 _byteswap_ulong #elif XXH_GCC_VERSION >= 403 @@ -1129,7 +2823,15 @@ static xxh_u32 XXH_swap32 (xxh_u32 x) /* *************************** * Memory reads *****************************/ -typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment; + +/*! + * @internal + * @brief Enum to indicate whether a pointer is aligned. + */ +typedef enum { + XXH_aligned, /*!< Aligned */ + XXH_unaligned /*!< Possibly unaligned */ +} XXH_alignment; /* * XXH_FORCE_MEMORY_ACCESS==3 is an endian-independent byteshift load. @@ -1182,17 +2884,27 @@ XXH_readLE32_align(const void* ptr, XXH_alignment align) /* ************************************* * Misc ***************************************/ +/*! @ingroup public */ XXH_PUBLIC_API unsigned XXH_versionNumber (void) { return XXH_VERSION_NUMBER; } /* ******************************************************************* * 32-bit hash functions *********************************************************************/ -static const xxh_u32 XXH_PRIME32_1 = 0x9E3779B1U; /* 0b10011110001101110111100110110001 */ -static const xxh_u32 XXH_PRIME32_2 = 0x85EBCA77U; /* 0b10000101111010111100101001110111 */ -static const xxh_u32 XXH_PRIME32_3 = 0xC2B2AE3DU; /* 0b11000010101100101010111000111101 */ -static const xxh_u32 XXH_PRIME32_4 = 0x27D4EB2FU; /* 0b00100111110101001110101100101111 */ -static const xxh_u32 XXH_PRIME32_5 = 0x165667B1U; /* 0b00010110010101100110011110110001 */ +/*! + * @} + * @defgroup XXH32_impl XXH32 implementation + * @ingroup impl + * + * Details on the XXH32 implementation. + * @{ + */ + /* #define instead of static const, to be used as initializers */ +#define XXH_PRIME32_1 0x9E3779B1U /*!< 0b10011110001101110111100110110001 */ +#define XXH_PRIME32_2 0x85EBCA77U /*!< 0b10000101111010111100101001110111 */ +#define XXH_PRIME32_3 0xC2B2AE3DU /*!< 0b11000010101100101010111000111101 */ +#define XXH_PRIME32_4 0x27D4EB2FU /*!< 0b00100111110101001110101100101111 */ +#define XXH_PRIME32_5 0x165667B1U /*!< 0b00010110010101100110011110110001 */ #ifdef XXH_OLD_NAMES # define PRIME32_1 XXH_PRIME32_1 @@ -1202,18 +2914,28 @@ static const xxh_u32 XXH_PRIME32_5 = 0x165667B1U; /* 0b00010110010101100110011 # define PRIME32_5 XXH_PRIME32_5 #endif +/*! + * @internal + * @brief Normal stripe processing routine. + * + * This shuffles the bits so that any bit from @p input impacts several bits in + * @p acc. + * + * @param acc The accumulator lane. + * @param input The stripe of input to mix. + * @return The mixed accumulator lane. + */ static xxh_u32 XXH32_round(xxh_u32 acc, xxh_u32 input) { acc += input * XXH_PRIME32_2; acc = XXH_rotl32(acc, 13); acc *= XXH_PRIME32_1; -#if defined(__GNUC__) && defined(__SSE4_1__) && !defined(XXH_ENABLE_AUTOVECTORIZE) +#if (defined(__SSE4_1__) || defined(__aarch64__) || defined(__wasm_simd128__)) && !defined(XXH_ENABLE_AUTOVECTORIZE) /* * UGLY HACK: - * This inline assembly hack forces acc into a normal register. This is the - * only thing that prevents GCC and Clang from autovectorizing the XXH32 - * loop (pragmas and attributes don't work for some resason) without globally - * disabling SSE4.1. + * A compiler fence is used to prevent GCC and Clang from + * autovectorizing the XXH32 loop (pragmas and attributes don't work for some + * reason) without globally disabling SSE4.1. * * The reason we want to avoid vectorization is because despite working on * 4 integers at a time, there are multiple factors slowing XXH32 down on @@ -1238,55 +2960,128 @@ static xxh_u32 XXH32_round(xxh_u32 acc, xxh_u32 input) * can load data, while v3 can multiply. SSE forces them to operate * together. * - * How this hack works: - * __asm__("" // Declare an assembly block but don't declare any instructions - * : // However, as an Input/Output Operand, - * "+r" // constrain a read/write operand (+) as a general purpose register (r). - * (acc) // and set acc as the operand - * ); + * This is also enabled on AArch64, as Clang is *very aggressive* in vectorizing + * the loop. NEON is only faster on the A53, and with the newer cores, it is less + * than half the speed. * - * Because of the 'r', the compiler has promised that seed will be in a - * general purpose register and the '+' says that it will be 'read/write', - * so it has to assume it has changed. It is like volatile without all the - * loads and stores. - * - * Since the argument has to be in a normal register (not an SSE register), - * each time XXH32_round is called, it is impossible to vectorize. + * Additionally, this is used on WASM SIMD128 because it JITs to the same + * SIMD instructions and has the same issue. */ - __asm__("" : "+r" (acc)); + XXH_COMPILER_GUARD(acc); #endif return acc; } -/* mix all bits */ -static xxh_u32 XXH32_avalanche(xxh_u32 h32) +/*! + * @internal + * @brief Mixes all bits to finalize the hash. + * + * The final mix ensures that all input bits have a chance to impact any bit in + * the output digest, resulting in an unbiased distribution. + * + * @param hash The hash to avalanche. + * @return The avalanched hash. + */ +static xxh_u32 XXH32_avalanche(xxh_u32 hash) { - h32 ^= h32 >> 15; - h32 *= XXH_PRIME32_2; - h32 ^= h32 >> 13; - h32 *= XXH_PRIME32_3; - h32 ^= h32 >> 16; - return(h32); + hash ^= hash >> 15; + hash *= XXH_PRIME32_2; + hash ^= hash >> 13; + hash *= XXH_PRIME32_3; + hash ^= hash >> 16; + return hash; } #define XXH_get32bits(p) XXH_readLE32_align(p, align) -static xxh_u32 -XXH32_finalize(xxh_u32 h32, const xxh_u8* ptr, size_t len, XXH_alignment align) +/*! + * @internal + * @brief Sets up the initial accumulator state for XXH32(). + */ +XXH_FORCE_INLINE void +XXH32_initAccs(xxh_u32 *acc, xxh_u32 seed) +{ + XXH_ASSERT(acc != NULL); + acc[0] = seed + XXH_PRIME32_1 + XXH_PRIME32_2; + acc[1] = seed + XXH_PRIME32_2; + acc[2] = seed + 0; + acc[3] = seed - XXH_PRIME32_1; +} + +/*! + * @internal + * @brief Consumes a block of data for XXH32(). + * + * @return the end input pointer. + */ +XXH_FORCE_INLINE const xxh_u8 * +XXH32_consumeLong( + xxh_u32 *XXH_RESTRICT acc, + xxh_u8 const *XXH_RESTRICT input, + size_t len, + XXH_alignment align +) +{ + const xxh_u8* const bEnd = input + len; + const xxh_u8* const limit = bEnd - 15; + XXH_ASSERT(acc != NULL); + XXH_ASSERT(input != NULL); + XXH_ASSERT(len >= 16); + do { + acc[0] = XXH32_round(acc[0], XXH_get32bits(input)); input += 4; + acc[1] = XXH32_round(acc[1], XXH_get32bits(input)); input += 4; + acc[2] = XXH32_round(acc[2], XXH_get32bits(input)); input += 4; + acc[3] = XXH32_round(acc[3], XXH_get32bits(input)); input += 4; + } while (input < limit); + + return input; +} + +/*! + * @internal + * @brief Merges the accumulator lanes together for XXH32() + */ +XXH_FORCE_INLINE XXH_PUREF xxh_u32 +XXH32_mergeAccs(const xxh_u32 *acc) +{ + XXH_ASSERT(acc != NULL); + return XXH_rotl32(acc[0], 1) + XXH_rotl32(acc[1], 7) + + XXH_rotl32(acc[2], 12) + XXH_rotl32(acc[3], 18); +} + +/*! + * @internal + * @brief Processes the last 0-15 bytes of @p ptr. + * + * There may be up to 15 bytes remaining to consume from the input. + * This final stage will digest them to ensure that all input bytes are present + * in the final mix. + * + * @param hash The hash to finalize. + * @param ptr The pointer to the remaining input. + * @param len The remaining length, modulo 16. + * @param align Whether @p ptr is aligned. + * @return The finalized hash. + * @see XXH64_finalize(). + */ +static XXH_PUREF xxh_u32 +XXH32_finalize(xxh_u32 hash, const xxh_u8* ptr, size_t len, XXH_alignment align) { -#define XXH_PROCESS1 do { \ - h32 += (*ptr++) * XXH_PRIME32_5; \ - h32 = XXH_rotl32(h32, 11) * XXH_PRIME32_1; \ +#define XXH_PROCESS1 do { \ + hash += (*ptr++) * XXH_PRIME32_5; \ + hash = XXH_rotl32(hash, 11) * XXH_PRIME32_1; \ } while (0) -#define XXH_PROCESS4 do { \ - h32 += XXH_get32bits(ptr) * XXH_PRIME32_3; \ - ptr += 4; \ - h32 = XXH_rotl32(h32, 17) * XXH_PRIME32_4; \ +#define XXH_PROCESS4 do { \ + hash += XXH_get32bits(ptr) * XXH_PRIME32_3; \ + ptr += 4; \ + hash = XXH_rotl32(hash, 17) * XXH_PRIME32_4; \ } while (0) - /* Compact rerolled version */ - if (XXH_REROLL) { + if (ptr==NULL) XXH_ASSERT(len == 0); + + /* Compact rerolled version; generally faster */ + if (!XXH32_ENDJMP) { len &= 15; while (len >= 4) { XXH_PROCESS4; @@ -1296,49 +3091,49 @@ XXH32_finalize(xxh_u32 h32, const xxh_u8* ptr, size_t len, XXH_alignment align) XXH_PROCESS1; --len; } - return XXH32_avalanche(h32); + return XXH32_avalanche(hash); } else { switch(len&15) /* or switch(bEnd - p) */ { case 12: XXH_PROCESS4; - /* fallthrough */ + XXH_FALLTHROUGH; /* fallthrough */ case 8: XXH_PROCESS4; - /* fallthrough */ + XXH_FALLTHROUGH; /* fallthrough */ case 4: XXH_PROCESS4; - return XXH32_avalanche(h32); + return XXH32_avalanche(hash); case 13: XXH_PROCESS4; - /* fallthrough */ + XXH_FALLTHROUGH; /* fallthrough */ case 9: XXH_PROCESS4; - /* fallthrough */ + XXH_FALLTHROUGH; /* fallthrough */ case 5: XXH_PROCESS4; XXH_PROCESS1; - return XXH32_avalanche(h32); + return XXH32_avalanche(hash); case 14: XXH_PROCESS4; - /* fallthrough */ + XXH_FALLTHROUGH; /* fallthrough */ case 10: XXH_PROCESS4; - /* fallthrough */ + XXH_FALLTHROUGH; /* fallthrough */ case 6: XXH_PROCESS4; XXH_PROCESS1; XXH_PROCESS1; - return XXH32_avalanche(h32); + return XXH32_avalanche(hash); case 15: XXH_PROCESS4; - /* fallthrough */ + XXH_FALLTHROUGH; /* fallthrough */ case 11: XXH_PROCESS4; - /* fallthrough */ + XXH_FALLTHROUGH; /* fallthrough */ case 7: XXH_PROCESS4; - /* fallthrough */ + XXH_FALLTHROUGH; /* fallthrough */ case 3: XXH_PROCESS1; - /* fallthrough */ + XXH_FALLTHROUGH; /* fallthrough */ case 2: XXH_PROCESS1; - /* fallthrough */ + XXH_FALLTHROUGH; /* fallthrough */ case 1: XXH_PROCESS1; - /* fallthrough */ - case 0: return XXH32_avalanche(h32); + XXH_FALLTHROUGH; /* fallthrough */ + case 0: return XXH32_avalanche(hash); } XXH_ASSERT(0); - return h32; /* reaching this point is deemed impossible */ + return hash; /* reaching this point is deemed impossible */ } } @@ -1350,35 +3145,28 @@ XXH32_finalize(xxh_u32 h32, const xxh_u8* ptr, size_t len, XXH_alignment align) # undef XXH_PROCESS4 #endif -XXH_FORCE_INLINE xxh_u32 +/*! + * @internal + * @brief The implementation for @ref XXH32(). + * + * @param input , len , seed Directly passed from @ref XXH32(). + * @param align Whether @p input is aligned. + * @return The calculated hash. + */ +XXH_FORCE_INLINE XXH_PUREF xxh_u32 XXH32_endian_align(const xxh_u8* input, size_t len, xxh_u32 seed, XXH_alignment align) { - const xxh_u8* bEnd = input + len; xxh_u32 h32; -#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) - if (input==NULL) { - len=0; - bEnd=input=(const xxh_u8*)(size_t)16; - } -#endif + if (input==NULL) XXH_ASSERT(len == 0); if (len>=16) { - const xxh_u8* const limit = bEnd - 15; - xxh_u32 v1 = seed + XXH_PRIME32_1 + XXH_PRIME32_2; - xxh_u32 v2 = seed + XXH_PRIME32_2; - xxh_u32 v3 = seed + 0; - xxh_u32 v4 = seed - XXH_PRIME32_1; + xxh_u32 acc[4]; + XXH32_initAccs(acc, seed); - do { - v1 = XXH32_round(v1, XXH_get32bits(input)); input += 4; - v2 = XXH32_round(v2, XXH_get32bits(input)); input += 4; - v3 = XXH32_round(v3, XXH_get32bits(input)); input += 4; - v4 = XXH32_round(v4, XXH_get32bits(input)); input += 4; - } while (input < limit); - - h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) - + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18); + input = XXH32_consumeLong(acc, input, len, align); + + h32 = XXH32_mergeAccs(acc); } else { h32 = seed + XXH_PRIME32_5; } @@ -1388,18 +3176,16 @@ XXH32_endian_align(const xxh_u8* input, size_t len, xxh_u32 seed, XXH_alignment return XXH32_finalize(h32, input, len&15, align); } - +/*! @ingroup XXH32_family */ XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t len, XXH32_hash_t seed) { -#if 0 +#if !defined(XXH_NO_STREAM) && XXH_SIZE_OPT >= 2 /* Simple version, good for code maintenance, but unfortunately slow for small inputs */ XXH32_state_t state; XXH32_reset(&state, seed); XXH32_update(&state, (const xxh_u8*)input, len); return XXH32_digest(&state); - #else - if (XXH_FORCE_ALIGN_CHECK) { if ((((size_t)input) & 3) == 0) { /* Input is 4-bytes aligned, leverage the speed benefit */ return XXH32_endian_align((const xxh_u8*)input, len, seed, XXH_aligned); @@ -1412,93 +3198,75 @@ XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t len, XXH32_hash_t s /******* Hash streaming *******/ - +#ifndef XXH_NO_STREAM +/*! @ingroup XXH32_family */ XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void) { return (XXH32_state_t*)XXH_malloc(sizeof(XXH32_state_t)); } +/*! @ingroup XXH32_family */ XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr) { XXH_free(statePtr); return XXH_OK; } +/*! @ingroup XXH32_family */ XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dstState, const XXH32_state_t* srcState) { - memcpy(dstState, srcState, sizeof(*dstState)); + XXH_memcpy(dstState, srcState, sizeof(*dstState)); } +/*! @ingroup XXH32_family */ XXH_PUBLIC_API XXH_errorcode XXH32_reset(XXH32_state_t* statePtr, XXH32_hash_t seed) { - XXH32_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */ - memset(&state, 0, sizeof(state)); - state.v1 = seed + XXH_PRIME32_1 + XXH_PRIME32_2; - state.v2 = seed + XXH_PRIME32_2; - state.v3 = seed + 0; - state.v4 = seed - XXH_PRIME32_1; - /* do not write into reserved, planned to be removed in a future version */ - memcpy(statePtr, &state, sizeof(state) - sizeof(state.reserved)); + XXH_ASSERT(statePtr != NULL); + memset(statePtr, 0, sizeof(*statePtr)); + XXH32_initAccs(statePtr->acc, seed); return XXH_OK; } +/*! @ingroup XXH32_family */ XXH_PUBLIC_API XXH_errorcode XXH32_update(XXH32_state_t* state, const void* input, size_t len) { - if (input==NULL) -#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + if (input==NULL) { + XXH_ASSERT(len == 0); return XXH_OK; -#else - return XXH_ERROR; -#endif + } - { const xxh_u8* p = (const xxh_u8*)input; - const xxh_u8* const bEnd = p + len; + state->total_len_32 += (XXH32_hash_t)len; + state->large_len |= (XXH32_hash_t)((len>=16) | (state->total_len_32>=16)); - state->total_len_32 += (XXH32_hash_t)len; - state->large_len |= (XXH32_hash_t)((len>=16) | (state->total_len_32>=16)); + XXH_ASSERT(state->bufferedSize < sizeof(state->buffer)); + if (len < sizeof(state->buffer) - state->bufferedSize) { /* fill in tmp buffer */ + XXH_memcpy(state->buffer + state->bufferedSize, input, len); + state->bufferedSize += (XXH32_hash_t)len; + return XXH_OK; + } - if (state->memsize + len < 16) { /* fill in tmp buffer */ - XXH_memcpy((xxh_u8*)(state->mem32) + state->memsize, input, len); - state->memsize += (XXH32_hash_t)len; - return XXH_OK; - } + { const xxh_u8* xinput = (const xxh_u8*)input; + const xxh_u8* const bEnd = xinput + len; - if (state->memsize) { /* some data left from previous update */ - XXH_memcpy((xxh_u8*)(state->mem32) + state->memsize, input, 16-state->memsize); - { const xxh_u32* p32 = state->mem32; - state->v1 = XXH32_round(state->v1, XXH_readLE32(p32)); p32++; - state->v2 = XXH32_round(state->v2, XXH_readLE32(p32)); p32++; - state->v3 = XXH32_round(state->v3, XXH_readLE32(p32)); p32++; - state->v4 = XXH32_round(state->v4, XXH_readLE32(p32)); - } - p += 16-state->memsize; - state->memsize = 0; + if (state->bufferedSize) { /* non-empty buffer: complete first */ + XXH_memcpy(state->buffer + state->bufferedSize, xinput, sizeof(state->buffer) - state->bufferedSize); + xinput += sizeof(state->buffer) - state->bufferedSize; + /* then process one round */ + (void)XXH32_consumeLong(state->acc, state->buffer, sizeof(state->buffer), XXH_aligned); + state->bufferedSize = 0; } - if (p <= bEnd-16) { - const xxh_u8* const limit = bEnd - 16; - xxh_u32 v1 = state->v1; - xxh_u32 v2 = state->v2; - xxh_u32 v3 = state->v3; - xxh_u32 v4 = state->v4; - - do { - v1 = XXH32_round(v1, XXH_readLE32(p)); p+=4; - v2 = XXH32_round(v2, XXH_readLE32(p)); p+=4; - v3 = XXH32_round(v3, XXH_readLE32(p)); p+=4; - v4 = XXH32_round(v4, XXH_readLE32(p)); p+=4; - } while (p<=limit); - - state->v1 = v1; - state->v2 = v2; - state->v3 = v3; - state->v4 = v4; + XXH_ASSERT(xinput <= bEnd); + if ((size_t)(bEnd - xinput) >= sizeof(state->buffer)) { + /* Process the remaining data */ + xinput = XXH32_consumeLong(state->acc, xinput, (size_t)(bEnd - xinput), XXH_unaligned); } - if (p < bEnd) { - XXH_memcpy(state->mem32, p, (size_t)(bEnd-p)); - state->memsize = (unsigned)(bEnd-p); + if (xinput < bEnd) { + /* Copy the leftover to the tmp buffer */ + XXH_memcpy(state->buffer, xinput, (size_t)(bEnd-xinput)); + state->bufferedSize = (unsigned)(bEnd-xinput); } } @@ -1506,47 +3274,33 @@ XXH32_update(XXH32_state_t* state, const void* input, size_t len) } -XXH_PUBLIC_API XXH32_hash_t XXH32_digest (const XXH32_state_t* state) +/*! @ingroup XXH32_family */ +XXH_PUBLIC_API XXH32_hash_t XXH32_digest(const XXH32_state_t* state) { xxh_u32 h32; if (state->large_len) { - h32 = XXH_rotl32(state->v1, 1) - + XXH_rotl32(state->v2, 7) - + XXH_rotl32(state->v3, 12) - + XXH_rotl32(state->v4, 18); + h32 = XXH32_mergeAccs(state->acc); } else { - h32 = state->v3 /* == seed */ + XXH_PRIME32_5; + h32 = state->acc[2] /* == seed */ + XXH_PRIME32_5; } h32 += state->total_len_32; - return XXH32_finalize(h32, (const xxh_u8*)state->mem32, state->memsize, XXH_aligned); + return XXH32_finalize(h32, state->buffer, state->bufferedSize, XXH_aligned); } - +#endif /* !XXH_NO_STREAM */ /******* Canonical representation *******/ -/* - * The default return values from XXH functions are unsigned 32 and 64 bit - * integers. - * - * The canonical representation uses big endian convention, the same convention - * as human-readable numbers (large digits first). - * - * This way, hash values can be written into a file or buffer, remaining - * comparable across different systems. - * - * The following functions allow transformation of hash values to and from their - * canonical format. - */ +/*! @ingroup XXH32_family */ XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash) { XXH_STATIC_ASSERT(sizeof(XXH32_canonical_t) == sizeof(XXH32_hash_t)); if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap32(hash); - memcpy(dst, &hash, sizeof(*dst)); + XXH_memcpy(dst, &hash, sizeof(*dst)); } - +/*! @ingroup XXH32_family */ XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src) { return XXH_readBE32(src); @@ -1558,7 +3312,11 @@ XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src /* ******************************************************************* * 64-bit hash functions *********************************************************************/ - +/*! + * @} + * @ingroup impl + * @{ + */ /******* Memory access *******/ typedef XXH64_hash_t xxh_u64; @@ -1567,35 +3325,6 @@ typedef XXH64_hash_t xxh_u64; # define U64 xxh_u64 #endif -/*! - * XXH_REROLL_XXH64: - * Whether to reroll the XXH64_finalize() loop. - * - * Just like XXH32, we can unroll the XXH64_finalize() loop. This can be a - * performance gain on 64-bit hosts, as only one jump is required. - * - * However, on 32-bit hosts, because arithmetic needs to be done with two 32-bit - * registers, and 64-bit arithmetic needs to be simulated, it isn't beneficial - * to unroll. The code becomes ridiculously large (the largest function in the - * binary on i386!), and rerolling it saves anywhere from 3kB to 20kB. It is - * also slightly faster because it fits into cache better and is more likely - * to be inlined by the compiler. - * - * If XXH_REROLL is defined, this is ignored and the loop is always rerolled. - */ -#ifndef XXH_REROLL_XXH64 -# if (defined(__ILP32__) || defined(_ILP32)) /* ILP32 is often defined on 32-bit GCC family */ \ - || !(defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64) /* x86-64 */ \ - || defined(_M_ARM64) || defined(__aarch64__) || defined(__arm64__) /* aarch64 */ \ - || defined(__PPC64__) || defined(__PPC64LE__) || defined(__ppc64__) || defined(__powerpc64__) /* ppc64 */ \ - || defined(__mips64__) || defined(__mips64)) /* mips64 */ \ - || (!defined(SIZE_MAX) || SIZE_MAX < ULLONG_MAX) /* check limits */ -# define XXH_REROLL_XXH64 1 -# else -# define XXH_REROLL_XXH64 0 -# endif -#endif /* !defined(XXH_REROLL_XXH64) */ - #if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3)) /* * Manual byteshift. Best for old compilers which don't inline memcpy. @@ -1604,35 +3333,39 @@ typedef XXH64_hash_t xxh_u64; #elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2)) /* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */ -static xxh_u64 XXH_read64(const void* memPtr) { return *(const xxh_u64*) memPtr; } +static xxh_u64 XXH_read64(const void* memPtr) +{ + return *(const xxh_u64*) memPtr; +} #elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1)) /* - * __pack instructions are safer, but compiler specific, hence potentially - * problematic for some compilers. - * - * Currently only defined for GCC and ICC. + * __attribute__((aligned(1))) is supported by gcc and clang. Originally the + * documentation claimed that it only increased the alignment, but actually it + * can decrease it on gcc, clang, and icc: + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69502, + * https://gcc.godbolt.org/z/xYez1j67Y. */ #ifdef XXH_OLD_NAMES -typedef union { xxh_u32 u32; xxh_u64 u64; } __attribute__((packed)) unalign64; +typedef union { xxh_u32 u32; xxh_u64 u64; } __attribute__((__packed__)) unalign64; #endif static xxh_u64 XXH_read64(const void* ptr) { - typedef union { xxh_u32 u32; xxh_u64 u64; } __attribute__((packed)) xxh_unalign64; - return ((const xxh_unalign64*)ptr)->u64; + typedef __attribute__((__aligned__(1))) xxh_u64 xxh_unalign64; + return *((const xxh_unalign64*)ptr); } #else /* * Portable and safe solution. Generally efficient. - * see: https://stackoverflow.com/a/32095106/646947 + * see: https://fastcompression.blogspot.com/2015/08/accessing-unaligned-memory.html */ static xxh_u64 XXH_read64(const void* memPtr) { xxh_u64 val; - memcpy(&val, memPtr, sizeof(val)); + XXH_memcpy(&val, memPtr, sizeof(val)); return val; } @@ -1643,7 +3376,7 @@ static xxh_u64 XXH_read64(const void* memPtr) #elif XXH_GCC_VERSION >= 403 # define XXH_swap64 __builtin_bswap64 #else -static xxh_u64 XXH_swap64 (xxh_u64 x) +static xxh_u64 XXH_swap64(xxh_u64 x) { return ((x << 56) & 0xff00000000000000ULL) | ((x << 40) & 0x00ff000000000000ULL) | @@ -1709,12 +3442,20 @@ XXH_readLE64_align(const void* ptr, XXH_alignment align) /******* xxh64 *******/ - -static const xxh_u64 XXH_PRIME64_1 = 0x9E3779B185EBCA87ULL; /* 0b1001111000110111011110011011000110000101111010111100101010000111 */ -static const xxh_u64 XXH_PRIME64_2 = 0xC2B2AE3D27D4EB4FULL; /* 0b1100001010110010101011100011110100100111110101001110101101001111 */ -static const xxh_u64 XXH_PRIME64_3 = 0x165667B19E3779F9ULL; /* 0b0001011001010110011001111011000110011110001101110111100111111001 */ -static const xxh_u64 XXH_PRIME64_4 = 0x85EBCA77C2B2AE63ULL; /* 0b1000010111101011110010100111011111000010101100101010111001100011 */ -static const xxh_u64 XXH_PRIME64_5 = 0x27D4EB2F165667C5ULL; /* 0b0010011111010100111010110010111100010110010101100110011111000101 */ +/*! + * @} + * @defgroup XXH64_impl XXH64 implementation + * @ingroup impl + * + * Details on the XXH64 implementation. + * @{ + */ +/* #define rather that static const, to be used as initializers */ +#define XXH_PRIME64_1 0x9E3779B185EBCA87ULL /*!< 0b1001111000110111011110011011000110000101111010111100101010000111 */ +#define XXH_PRIME64_2 0xC2B2AE3D27D4EB4FULL /*!< 0b1100001010110010101011100011110100100111110101001110101101001111 */ +#define XXH_PRIME64_3 0x165667B19E3779F9ULL /*!< 0b0001011001010110011001111011000110011110001101110111100111111001 */ +#define XXH_PRIME64_4 0x85EBCA77C2B2AE63ULL /*!< 0b1000010111101011110010100111011111000010101100101010111001100011 */ +#define XXH_PRIME64_5 0x27D4EB2F165667C5ULL /*!< 0b0010011111010100111010110010111100010110010101100110011111000101 */ #ifdef XXH_OLD_NAMES # define PRIME64_1 XXH_PRIME64_1 @@ -1724,11 +3465,29 @@ static const xxh_u64 XXH_PRIME64_5 = 0x27D4EB2F165667C5ULL; /* 0b0010011111010 # define PRIME64_5 XXH_PRIME64_5 #endif +/*! @copydoc XXH32_round */ static xxh_u64 XXH64_round(xxh_u64 acc, xxh_u64 input) { acc += input * XXH_PRIME64_2; acc = XXH_rotl64(acc, 31); acc *= XXH_PRIME64_1; +#if (defined(__AVX512F__)) && !defined(XXH_ENABLE_AUTOVECTORIZE) + /* + * DISABLE AUTOVECTORIZATION: + * A compiler fence is used to prevent GCC and Clang from + * autovectorizing the XXH64 loop (pragmas and attributes don't work for some + * reason) without globally disabling AVX512. + * + * Autovectorization of XXH64 tends to be detrimental, + * though the exact outcome may change depending on exact cpu and compiler version. + * For information, it has been reported as detrimental for Skylake-X, + * but possibly beneficial for Zen4. + * + * The default is to disable auto-vectorization, + * but you can select to enable it instead using `XXH_ENABLE_AUTOVECTORIZE` build variable. + */ + XXH_COMPILER_GUARD(acc); +#endif return acc; } @@ -1740,142 +3499,138 @@ static xxh_u64 XXH64_mergeRound(xxh_u64 acc, xxh_u64 val) return acc; } -static xxh_u64 XXH64_avalanche(xxh_u64 h64) +/*! @copydoc XXH32_avalanche */ +static xxh_u64 XXH64_avalanche(xxh_u64 hash) { - h64 ^= h64 >> 33; - h64 *= XXH_PRIME64_2; - h64 ^= h64 >> 29; - h64 *= XXH_PRIME64_3; - h64 ^= h64 >> 32; - return h64; + hash ^= hash >> 33; + hash *= XXH_PRIME64_2; + hash ^= hash >> 29; + hash *= XXH_PRIME64_3; + hash ^= hash >> 32; + return hash; } #define XXH_get64bits(p) XXH_readLE64_align(p, align) -static xxh_u64 -XXH64_finalize(xxh_u64 h64, const xxh_u8* ptr, size_t len, XXH_alignment align) +/*! + * @internal + * @brief Sets up the initial accumulator state for XXH64(). + */ +XXH_FORCE_INLINE void +XXH64_initAccs(xxh_u64 *acc, xxh_u64 seed) { -#define XXH_PROCESS1_64 do { \ - h64 ^= (*ptr++) * XXH_PRIME64_5; \ - h64 = XXH_rotl64(h64, 11) * XXH_PRIME64_1; \ -} while (0) + XXH_ASSERT(acc != NULL); + acc[0] = seed + XXH_PRIME64_1 + XXH_PRIME64_2; + acc[1] = seed + XXH_PRIME64_2; + acc[2] = seed + 0; + acc[3] = seed - XXH_PRIME64_1; +} -#define XXH_PROCESS4_64 do { \ - h64 ^= (xxh_u64)(XXH_get32bits(ptr)) * XXH_PRIME64_1; \ - ptr += 4; \ - h64 = XXH_rotl64(h64, 23) * XXH_PRIME64_2 + XXH_PRIME64_3; \ -} while (0) +/*! + * @internal + * @brief Consumes a block of data for XXH64(). + * + * @return the end input pointer. + */ +XXH_FORCE_INLINE const xxh_u8 * +XXH64_consumeLong( + xxh_u64 *XXH_RESTRICT acc, + xxh_u8 const *XXH_RESTRICT input, + size_t len, + XXH_alignment align +) +{ + const xxh_u8* const bEnd = input + len; + const xxh_u8* const limit = bEnd - 31; + XXH_ASSERT(acc != NULL); + XXH_ASSERT(input != NULL); + XXH_ASSERT(len >= 32); + do { + /* reroll on 32-bit */ + if (sizeof(void *) < sizeof(xxh_u64)) { + size_t i; + for (i = 0; i < 4; i++) { + acc[i] = XXH64_round(acc[i], XXH_get64bits(input)); + input += 8; + } + } else { + acc[0] = XXH64_round(acc[0], XXH_get64bits(input)); input += 8; + acc[1] = XXH64_round(acc[1], XXH_get64bits(input)); input += 8; + acc[2] = XXH64_round(acc[2], XXH_get64bits(input)); input += 8; + acc[3] = XXH64_round(acc[3], XXH_get64bits(input)); input += 8; + } + } while (input < limit); -#define XXH_PROCESS8_64 do { \ - xxh_u64 const k1 = XXH64_round(0, XXH_get64bits(ptr)); \ - ptr += 8; \ - h64 ^= k1; \ - h64 = XXH_rotl64(h64,27) * XXH_PRIME64_1 + XXH_PRIME64_4; \ -} while (0) + return input; +} - /* Rerolled version for 32-bit targets is faster and much smaller. */ - if (XXH_REROLL || XXH_REROLL_XXH64) { - len &= 31; - while (len >= 8) { - XXH_PROCESS8_64; - len -= 8; - } - if (len >= 4) { - XXH_PROCESS4_64; - len -= 4; - } - while (len > 0) { - XXH_PROCESS1_64; - --len; - } - return XXH64_avalanche(h64); - } else { - switch(len & 31) { - case 24: XXH_PROCESS8_64; - /* fallthrough */ - case 16: XXH_PROCESS8_64; - /* fallthrough */ - case 8: XXH_PROCESS8_64; - return XXH64_avalanche(h64); - - case 28: XXH_PROCESS8_64; - /* fallthrough */ - case 20: XXH_PROCESS8_64; - /* fallthrough */ - case 12: XXH_PROCESS8_64; - /* fallthrough */ - case 4: XXH_PROCESS4_64; - return XXH64_avalanche(h64); - - case 25: XXH_PROCESS8_64; - /* fallthrough */ - case 17: XXH_PROCESS8_64; - /* fallthrough */ - case 9: XXH_PROCESS8_64; - XXH_PROCESS1_64; - return XXH64_avalanche(h64); - - case 29: XXH_PROCESS8_64; - /* fallthrough */ - case 21: XXH_PROCESS8_64; - /* fallthrough */ - case 13: XXH_PROCESS8_64; - /* fallthrough */ - case 5: XXH_PROCESS4_64; - XXH_PROCESS1_64; - return XXH64_avalanche(h64); - - case 26: XXH_PROCESS8_64; - /* fallthrough */ - case 18: XXH_PROCESS8_64; - /* fallthrough */ - case 10: XXH_PROCESS8_64; - XXH_PROCESS1_64; - XXH_PROCESS1_64; - return XXH64_avalanche(h64); - - case 30: XXH_PROCESS8_64; - /* fallthrough */ - case 22: XXH_PROCESS8_64; - /* fallthrough */ - case 14: XXH_PROCESS8_64; - /* fallthrough */ - case 6: XXH_PROCESS4_64; - XXH_PROCESS1_64; - XXH_PROCESS1_64; - return XXH64_avalanche(h64); - - case 27: XXH_PROCESS8_64; - /* fallthrough */ - case 19: XXH_PROCESS8_64; - /* fallthrough */ - case 11: XXH_PROCESS8_64; - XXH_PROCESS1_64; - XXH_PROCESS1_64; - XXH_PROCESS1_64; - return XXH64_avalanche(h64); - - case 31: XXH_PROCESS8_64; - /* fallthrough */ - case 23: XXH_PROCESS8_64; - /* fallthrough */ - case 15: XXH_PROCESS8_64; - /* fallthrough */ - case 7: XXH_PROCESS4_64; - /* fallthrough */ - case 3: XXH_PROCESS1_64; - /* fallthrough */ - case 2: XXH_PROCESS1_64; - /* fallthrough */ - case 1: XXH_PROCESS1_64; - /* fallthrough */ - case 0: return XXH64_avalanche(h64); +/*! + * @internal + * @brief Merges the accumulator lanes together for XXH64() + */ +XXH_FORCE_INLINE XXH_PUREF xxh_u64 +XXH64_mergeAccs(const xxh_u64 *acc) +{ + XXH_ASSERT(acc != NULL); + { + xxh_u64 h64 = XXH_rotl64(acc[0], 1) + XXH_rotl64(acc[1], 7) + + XXH_rotl64(acc[2], 12) + XXH_rotl64(acc[3], 18); + /* reroll on 32-bit */ + if (sizeof(void *) < sizeof(xxh_u64)) { + size_t i; + for (i = 0; i < 4; i++) { + h64 = XXH64_mergeRound(h64, acc[i]); + } + } else { + h64 = XXH64_mergeRound(h64, acc[0]); + h64 = XXH64_mergeRound(h64, acc[1]); + h64 = XXH64_mergeRound(h64, acc[2]); + h64 = XXH64_mergeRound(h64, acc[3]); } + return h64; + } +} + +/*! + * @internal + * @brief Processes the last 0-31 bytes of @p ptr. + * + * There may be up to 31 bytes remaining to consume from the input. + * This final stage will digest them to ensure that all input bytes are present + * in the final mix. + * + * @param hash The hash to finalize. + * @param ptr The pointer to the remaining input. + * @param len The remaining length, modulo 32. + * @param align Whether @p ptr is aligned. + * @return The finalized hash + * @see XXH32_finalize(). + */ +XXH_STATIC XXH_PUREF xxh_u64 +XXH64_finalize(xxh_u64 hash, const xxh_u8* ptr, size_t len, XXH_alignment align) +{ + if (ptr==NULL) XXH_ASSERT(len == 0); + len &= 31; + while (len >= 8) { + xxh_u64 const k1 = XXH64_round(0, XXH_get64bits(ptr)); + ptr += 8; + hash ^= k1; + hash = XXH_rotl64(hash,27) * XXH_PRIME64_1 + XXH_PRIME64_4; + len -= 8; } - /* impossible to reach */ - XXH_ASSERT(0); - return 0; /* unreachable, but some compilers complain without it */ + if (len >= 4) { + hash ^= (xxh_u64)(XXH_get32bits(ptr)) * XXH_PRIME64_1; + ptr += 4; + hash = XXH_rotl64(hash, 23) * XXH_PRIME64_2 + XXH_PRIME64_3; + len -= 4; + } + while (len > 0) { + hash ^= (*ptr++) * XXH_PRIME64_5; + hash = XXH_rotl64(hash, 11) * XXH_PRIME64_1; + --len; + } + return XXH64_avalanche(hash); } #ifdef XXH_OLD_NAMES @@ -1888,39 +3643,27 @@ XXH64_finalize(xxh_u64 h64, const xxh_u8* ptr, size_t len, XXH_alignment align) # undef XXH_PROCESS8_64 #endif -XXH_FORCE_INLINE xxh_u64 +/*! + * @internal + * @brief The implementation for @ref XXH64(). + * + * @param input , len , seed Directly passed from @ref XXH64(). + * @param align Whether @p input is aligned. + * @return The calculated hash. + */ +XXH_FORCE_INLINE XXH_PUREF xxh_u64 XXH64_endian_align(const xxh_u8* input, size_t len, xxh_u64 seed, XXH_alignment align) { - const xxh_u8* bEnd = input + len; xxh_u64 h64; + if (input==NULL) XXH_ASSERT(len == 0); -#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) - if (input==NULL) { - len=0; - bEnd=input=(const xxh_u8*)(size_t)32; - } -#endif - - if (len>=32) { - const xxh_u8* const limit = bEnd - 32; - xxh_u64 v1 = seed + XXH_PRIME64_1 + XXH_PRIME64_2; - xxh_u64 v2 = seed + XXH_PRIME64_2; - xxh_u64 v3 = seed + 0; - xxh_u64 v4 = seed - XXH_PRIME64_1; + if (len>=32) { /* Process a large block of data */ + xxh_u64 acc[4]; + XXH64_initAccs(acc, seed); - do { - v1 = XXH64_round(v1, XXH_get64bits(input)); input+=8; - v2 = XXH64_round(v2, XXH_get64bits(input)); input+=8; - v3 = XXH64_round(v3, XXH_get64bits(input)); input+=8; - v4 = XXH64_round(v4, XXH_get64bits(input)); input+=8; - } while (input<=limit); - - h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18); - h64 = XXH64_mergeRound(h64, v1); - h64 = XXH64_mergeRound(h64, v2); - h64 = XXH64_mergeRound(h64, v3); - h64 = XXH64_mergeRound(h64, v4); + input = XXH64_consumeLong(acc, input, len, align); + h64 = XXH64_mergeAccs(acc); } else { h64 = seed + XXH_PRIME64_5; } @@ -1931,17 +3674,16 @@ XXH64_endian_align(const xxh_u8* input, size_t len, xxh_u64 seed, XXH_alignment } -XXH_PUBLIC_API XXH64_hash_t XXH64 (const void* input, size_t len, XXH64_hash_t seed) +/*! @ingroup XXH64_family */ +XXH_PUBLIC_API XXH64_hash_t XXH64 (XXH_NOESCAPE const void* input, size_t len, XXH64_hash_t seed) { -#if 0 +#if !defined(XXH_NO_STREAM) && XXH_SIZE_OPT >= 2 /* Simple version, good for code maintenance, but unfortunately slow for small inputs */ XXH64_state_t state; XXH64_reset(&state, seed); XXH64_update(&state, (const xxh_u8*)input, len); return XXH64_digest(&state); - #else - if (XXH_FORCE_ALIGN_CHECK) { if ((((size_t)input) & 7)==0) { /* Input is aligned, let's leverage the speed advantage */ return XXH64_endian_align((const xxh_u8*)input, len, seed, XXH_aligned); @@ -1953,89 +3695,73 @@ XXH_PUBLIC_API XXH64_hash_t XXH64 (const void* input, size_t len, XXH64_hash_t s } /******* Hash Streaming *******/ - +#ifndef XXH_NO_STREAM +/*! @ingroup XXH64_family*/ XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void) { return (XXH64_state_t*)XXH_malloc(sizeof(XXH64_state_t)); } +/*! @ingroup XXH64_family */ XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr) { XXH_free(statePtr); return XXH_OK; } -XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dstState, const XXH64_state_t* srcState) +/*! @ingroup XXH64_family */ +XXH_PUBLIC_API void XXH64_copyState(XXH_NOESCAPE XXH64_state_t* dstState, const XXH64_state_t* srcState) { - memcpy(dstState, srcState, sizeof(*dstState)); + XXH_memcpy(dstState, srcState, sizeof(*dstState)); } -XXH_PUBLIC_API XXH_errorcode XXH64_reset(XXH64_state_t* statePtr, XXH64_hash_t seed) +/*! @ingroup XXH64_family */ +XXH_PUBLIC_API XXH_errorcode XXH64_reset(XXH_NOESCAPE XXH64_state_t* statePtr, XXH64_hash_t seed) { - XXH64_state_t state; /* use a local state to memcpy() in order to avoid strict-aliasing warnings */ - memset(&state, 0, sizeof(state)); - state.v1 = seed + XXH_PRIME64_1 + XXH_PRIME64_2; - state.v2 = seed + XXH_PRIME64_2; - state.v3 = seed + 0; - state.v4 = seed - XXH_PRIME64_1; - /* do not write into reserved64, might be removed in a future version */ - memcpy(statePtr, &state, sizeof(state) - sizeof(state.reserved64)); + XXH_ASSERT(statePtr != NULL); + memset(statePtr, 0, sizeof(*statePtr)); + XXH64_initAccs(statePtr->acc, seed); return XXH_OK; } +/*! @ingroup XXH64_family */ XXH_PUBLIC_API XXH_errorcode -XXH64_update (XXH64_state_t* state, const void* input, size_t len) +XXH64_update (XXH_NOESCAPE XXH64_state_t* state, XXH_NOESCAPE const void* input, size_t len) { - if (input==NULL) -#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + if (input==NULL) { + XXH_ASSERT(len == 0); return XXH_OK; -#else - return XXH_ERROR; -#endif + } - { const xxh_u8* p = (const xxh_u8*)input; - const xxh_u8* const bEnd = p + len; + state->total_len += len; - state->total_len += len; + XXH_ASSERT(state->bufferedSize <= sizeof(state->buffer)); + if (len < sizeof(state->buffer) - state->bufferedSize) { /* fill in tmp buffer */ + XXH_memcpy(state->buffer + state->bufferedSize, input, len); + state->bufferedSize += (XXH32_hash_t)len; + return XXH_OK; + } - if (state->memsize + len < 32) { /* fill in tmp buffer */ - XXH_memcpy(((xxh_u8*)state->mem64) + state->memsize, input, len); - state->memsize += (xxh_u32)len; - return XXH_OK; - } + { const xxh_u8* xinput = (const xxh_u8*)input; + const xxh_u8* const bEnd = xinput + len; - if (state->memsize) { /* tmp buffer is full */ - XXH_memcpy(((xxh_u8*)state->mem64) + state->memsize, input, 32-state->memsize); - state->v1 = XXH64_round(state->v1, XXH_readLE64(state->mem64+0)); - state->v2 = XXH64_round(state->v2, XXH_readLE64(state->mem64+1)); - state->v3 = XXH64_round(state->v3, XXH_readLE64(state->mem64+2)); - state->v4 = XXH64_round(state->v4, XXH_readLE64(state->mem64+3)); - p += 32-state->memsize; - state->memsize = 0; + if (state->bufferedSize) { /* non-empty buffer => complete first */ + XXH_memcpy(state->buffer + state->bufferedSize, xinput, sizeof(state->buffer) - state->bufferedSize); + xinput += sizeof(state->buffer) - state->bufferedSize; + /* and process one round */ + (void)XXH64_consumeLong(state->acc, state->buffer, sizeof(state->buffer), XXH_aligned); + state->bufferedSize = 0; } - if (p+32 <= bEnd) { - const xxh_u8* const limit = bEnd - 32; - xxh_u64 v1 = state->v1; - xxh_u64 v2 = state->v2; - xxh_u64 v3 = state->v3; - xxh_u64 v4 = state->v4; - - do { - v1 = XXH64_round(v1, XXH_readLE64(p)); p+=8; - v2 = XXH64_round(v2, XXH_readLE64(p)); p+=8; - v3 = XXH64_round(v3, XXH_readLE64(p)); p+=8; - v4 = XXH64_round(v4, XXH_readLE64(p)); p+=8; - } while (p<=limit); - - state->v1 = v1; - state->v2 = v2; - state->v3 = v3; - state->v4 = v4; + XXH_ASSERT(xinput <= bEnd); + if ((size_t)(bEnd - xinput) >= sizeof(state->buffer)) { + /* Process the remaining data */ + xinput = XXH64_consumeLong(state->acc, xinput, (size_t)(bEnd - xinput), XXH_unaligned); } - if (p < bEnd) { - XXH_memcpy(state->mem64, p, (size_t)(bEnd-p)); - state->memsize = (unsigned)(bEnd-p); + if (xinput < bEnd) { + /* Copy the leftover to the tmp buffer */ + XXH_memcpy(state->buffer, xinput, (size_t)(bEnd-xinput)); + state->bufferedSize = (unsigned)(bEnd-xinput); } } @@ -2043,60 +3769,54 @@ XXH64_update (XXH64_state_t* state, const void* input, size_t len) } -XXH_PUBLIC_API XXH64_hash_t XXH64_digest (const XXH64_state_t* state) +/*! @ingroup XXH64_family */ +XXH_PUBLIC_API XXH64_hash_t XXH64_digest(XXH_NOESCAPE const XXH64_state_t* state) { xxh_u64 h64; if (state->total_len >= 32) { - xxh_u64 const v1 = state->v1; - xxh_u64 const v2 = state->v2; - xxh_u64 const v3 = state->v3; - xxh_u64 const v4 = state->v4; - - h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18); - h64 = XXH64_mergeRound(h64, v1); - h64 = XXH64_mergeRound(h64, v2); - h64 = XXH64_mergeRound(h64, v3); - h64 = XXH64_mergeRound(h64, v4); + h64 = XXH64_mergeAccs(state->acc); } else { - h64 = state->v3 /*seed*/ + XXH_PRIME64_5; + h64 = state->acc[2] /*seed*/ + XXH_PRIME64_5; } h64 += (xxh_u64) state->total_len; - return XXH64_finalize(h64, (const xxh_u8*)state->mem64, (size_t)state->total_len, XXH_aligned); + return XXH64_finalize(h64, state->buffer, (size_t)state->total_len, XXH_aligned); } - +#endif /* !XXH_NO_STREAM */ /******* Canonical representation *******/ -XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash) +/*! @ingroup XXH64_family */ +XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH_NOESCAPE XXH64_canonical_t* dst, XXH64_hash_t hash) { XXH_STATIC_ASSERT(sizeof(XXH64_canonical_t) == sizeof(XXH64_hash_t)); if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap64(hash); - memcpy(dst, &hash, sizeof(*dst)); + XXH_memcpy(dst, &hash, sizeof(*dst)); } -XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src) +/*! @ingroup XXH64_family */ +XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(XXH_NOESCAPE const XXH64_canonical_t* src) { return XXH_readBE64(src); } - +#ifndef XXH_NO_XXH3 /* ********************************************************************* * XXH3 * New generation hash designed for speed on small keys and vectorization ************************************************************************ */ +/*! + * @} + * @defgroup XXH3_impl XXH3 implementation + * @ingroup impl + * @{ + */ /* === Compiler specifics === */ -#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* >= C99 */ -# define XXH_RESTRICT restrict -#else -/* Note: it might be useful to define __restrict or __restrict__ for some C++ compilers */ -# define XXH_RESTRICT /* disable */ -#endif #if (defined(__GNUC__) && (__GNUC__ >= 3)) \ || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) \ @@ -2108,17 +3828,39 @@ XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src # define XXH_unlikely(x) (x) #endif -#if defined(__GNUC__) -# if defined(__AVX2__) -# include -# elif defined(__SSE2__) -# include -# elif defined(__ARM_NEON__) || defined(__ARM_NEON) +#ifndef XXH_HAS_INCLUDE +# ifdef __has_include +/* + * Not defined as XXH_HAS_INCLUDE(x) (function-like) because + * this causes segfaults in Apple Clang 4.2 (on Mac OS X 10.7 Lion) + */ +# define XXH_HAS_INCLUDE __has_include +# else +# define XXH_HAS_INCLUDE(x) 0 +# endif +#endif + +#if defined(__GNUC__) || defined(__clang__) +# if defined(__ARM_FEATURE_SVE) +# include +# endif +# if defined(__ARM_NEON__) || defined(__ARM_NEON) \ + || (defined(_M_ARM) && _M_ARM >= 7) \ + || defined(_M_ARM64) || defined(_M_ARM64EC) \ + || (defined(__wasm_simd128__) && XXH_HAS_INCLUDE()) /* WASM SIMD128 via SIMDe */ # define inline __inline__ /* circumvent a clang bug */ # include # undef inline +# elif defined(__AVX2__) +# include +# elif defined(__SSE2__) +# include +# elif defined(__loongarch_sx) +# include # endif -#elif defined(_MSC_VER) +#endif + +#if defined(_MSC_VER) # include #endif @@ -2198,34 +3940,75 @@ XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src /* ========================================== * Vectorization detection * ========================================== */ -#define XXH_SCALAR 0 /* Portable scalar version */ -#define XXH_SSE2 1 /* SSE2 for Pentium 4 and all x86_64 */ -#define XXH_AVX2 2 /* AVX2 for Haswell and Bulldozer */ -#define XXH_AVX512 3 /* AVX512 for Skylake and Icelake */ -#define XXH_NEON 4 /* NEON for most ARMv7-A and all AArch64 */ -#define XXH_VSX 5 /* VSX and ZVector for POWER8/z13 */ + +#ifdef XXH_DOXYGEN +/*! + * @ingroup tuning + * @brief Overrides the vectorization implementation chosen for XXH3. + * + * Can be defined to 0 to disable SIMD or any of the values mentioned in + * @ref XXH_VECTOR_TYPE. + * + * If this is not defined, it uses predefined macros to determine the best + * implementation. + */ +# define XXH_VECTOR XXH_SCALAR +/*! + * @ingroup tuning + * @brief Selects the minimum alignment for XXH3's accumulators. + * + * When using SIMD, this should match the alignment required for said vector + * type, so, for example, 32 for AVX2. + * + * Default: Auto detected. + */ +# define XXH_ACC_ALIGN 8 +#endif + +/* Actual definition */ +#ifndef XXH_DOXYGEN +#endif #ifndef XXH_VECTOR /* can be defined on command line */ -# if defined(__AVX512F__) +# if defined(__ARM_FEATURE_SVE) +# define XXH_VECTOR XXH_SVE +# elif ( \ + defined(__ARM_NEON__) || defined(__ARM_NEON) /* gcc */ \ + || defined(_M_ARM) || defined(_M_ARM64) || defined(_M_ARM64EC) /* msvc */ \ + || (defined(__wasm_simd128__) && XXH_HAS_INCLUDE()) /* wasm simd128 via SIMDe */ \ + ) && ( \ + defined(_WIN32) || defined(__LITTLE_ENDIAN__) /* little endian only */ \ + || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) \ + ) +# define XXH_VECTOR XXH_NEON +# elif defined(__AVX512F__) # define XXH_VECTOR XXH_AVX512 # elif defined(__AVX2__) # define XXH_VECTOR XXH_AVX2 # elif defined(__SSE2__) || defined(_M_AMD64) || defined(_M_X64) || (defined(_M_IX86_FP) && (_M_IX86_FP == 2)) # define XXH_VECTOR XXH_SSE2 -# elif defined(__GNUC__) /* msvc support maybe later */ \ - && (defined(__ARM_NEON__) || defined(__ARM_NEON)) \ - && (defined(__LITTLE_ENDIAN__) /* We only support little endian NEON */ \ - || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) -# define XXH_VECTOR XXH_NEON # elif (defined(__PPC64__) && defined(__POWER8_VECTOR__)) \ || (defined(__s390x__) && defined(__VEC__)) \ && defined(__GNUC__) /* TODO: IBM XL */ # define XXH_VECTOR XXH_VSX +# elif defined(__loongarch_sx) +# define XXH_VECTOR XXH_LSX # else # define XXH_VECTOR XXH_SCALAR # endif #endif +/* __ARM_FEATURE_SVE is only supported by GCC & Clang. */ +#if (XXH_VECTOR == XXH_SVE) && !defined(__ARM_FEATURE_SVE) +# ifdef _MSC_VER +# pragma warning(once : 4606) +# else +# warning "__ARM_FEATURE_SVE isn't supported. Use SCALAR instead." +# endif +# undef XXH_VECTOR +# define XXH_VECTOR XXH_SCALAR +#endif + /* * Controls the alignment of the accumulator, * for compatibility with aligned vector loads, which are usually faster. @@ -2245,16 +4028,28 @@ XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src # define XXH_ACC_ALIGN 16 # elif XXH_VECTOR == XXH_AVX512 /* avx512 */ # define XXH_ACC_ALIGN 64 +# elif XXH_VECTOR == XXH_SVE /* sve */ +# define XXH_ACC_ALIGN 64 +# elif XXH_VECTOR == XXH_LSX /* lsx */ +# define XXH_ACC_ALIGN 64 # endif #endif #if defined(XXH_X86DISPATCH) || XXH_VECTOR == XXH_SSE2 \ || XXH_VECTOR == XXH_AVX2 || XXH_VECTOR == XXH_AVX512 # define XXH_SEC_ALIGN XXH_ACC_ALIGN +#elif XXH_VECTOR == XXH_SVE +# define XXH_SEC_ALIGN XXH_ACC_ALIGN #else # define XXH_SEC_ALIGN 8 #endif +#if defined(__GNUC__) || defined(__clang__) +# define XXH_ALIASING __attribute__((__may_alias__)) +#else +# define XXH_ALIASING /* nothing */ +#endif + /* * UGLY HACK: * GCC usually generates the best code with -O3 for xxHash. @@ -2278,111 +4073,130 @@ XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src */ #if XXH_VECTOR == XXH_AVX2 /* AVX2 */ \ && defined(__GNUC__) && !defined(__clang__) /* GCC, not Clang */ \ - && defined(__OPTIMIZE__) && !defined(__OPTIMIZE_SIZE__) /* respect -O0 and -Os */ + && defined(__OPTIMIZE__) && XXH_SIZE_OPT <= 0 /* respect -O0 and -Os */ # pragma GCC push_options # pragma GCC optimize("-O2") #endif - #if XXH_VECTOR == XXH_NEON + /* - * NEON's setup for vmlal_u32 is a little more complicated than it is on - * SSE2, AVX2, and VSX. - * - * While PMULUDQ and VMULEUW both perform a mask, VMLAL.U32 performs an upcast. - * - * To do the same operation, the 128-bit 'Q' register needs to be split into - * two 64-bit 'D' registers, performing this operation:: - * - * [ a | b ] - * | '---------. .--------' | - * | x | - * | .---------' '--------. | - * [ a & 0xFFFFFFFF | b & 0xFFFFFFFF ],[ a >> 32 | b >> 32 ] + * UGLY HACK: While AArch64 GCC on Linux does not seem to care, on macOS, GCC -O3 + * optimizes out the entire hashLong loop because of the aliasing violation. * - * Due to significant changes in aarch64, the fastest method for aarch64 is - * completely different than the fastest method for ARMv7-A. - * - * ARMv7-A treats D registers as unions overlaying Q registers, so modifying - * D11 will modify the high half of Q5. This is similar to how modifying AH - * will only affect bits 8-15 of AX on x86. - * - * VZIP takes two registers, and puts even lanes in one register and odd lanes - * in the other. + * However, GCC is also inefficient at load-store optimization with vld1q/vst1q, + * so the only option is to mark it as aliasing. + */ +typedef uint64x2_t xxh_aliasing_uint64x2_t XXH_ALIASING; + +/*! + * @internal + * @brief `vld1q_u64` but faster and alignment-safe. * - * On ARMv7-A, this strangely modifies both parameters in place instead of - * taking the usual 3-operand form. + * On AArch64, unaligned access is always safe, but on ARMv7-a, it is only + * *conditionally* safe (`vld1` has an alignment bit like `movdq[ua]` in x86). * - * Therefore, if we want to do this, we can simply use a D-form VZIP.32 on the - * lower and upper halves of the Q register to end up with the high and low - * halves where we want - all in one instruction. + * GCC for AArch64 sees `vld1q_u8` as an intrinsic instead of a load, so it + * prohibits load-store optimizations. Therefore, a direct dereference is used. * - * vzip.32 d10, d11 @ d10 = { d10[0], d11[0] }; d11 = { d10[1], d11[1] } + * Otherwise, `vld1q_u8` is used with `vreinterpretq_u8_u64` to do a safe + * unaligned load. + */ +#if defined(__aarch64__) && defined(__GNUC__) && !defined(__clang__) +XXH_FORCE_INLINE uint64x2_t XXH_vld1q_u64(void const* ptr) /* silence -Wcast-align */ +{ + return *(xxh_aliasing_uint64x2_t const *)ptr; +} +#else +XXH_FORCE_INLINE uint64x2_t XXH_vld1q_u64(void const* ptr) +{ + return vreinterpretq_u64_u8(vld1q_u8((uint8_t const*)ptr)); +} +#endif + +/*! + * @internal + * @brief `vmlal_u32` on low and high halves of a vector. * - * Unfortunately we need inline assembly for this: Instructions modifying two - * registers at once is not possible in GCC or Clang's IR, and they have to - * create a copy. + * This is a workaround for AArch64 GCC < 11 which implemented arm_neon.h with + * inline assembly and were therefore incapable of merging the `vget_{low, high}_u32` + * with `vmlal_u32`. + */ +#if defined(__aarch64__) && defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 11 +XXH_FORCE_INLINE uint64x2_t +XXH_vmlal_low_u32(uint64x2_t acc, uint32x4_t lhs, uint32x4_t rhs) +{ + /* Inline assembly is the only way */ + __asm__("umlal %0.2d, %1.2s, %2.2s" : "+w" (acc) : "w" (lhs), "w" (rhs)); + return acc; +} +XXH_FORCE_INLINE uint64x2_t +XXH_vmlal_high_u32(uint64x2_t acc, uint32x4_t lhs, uint32x4_t rhs) +{ + /* This intrinsic works as expected */ + return vmlal_high_u32(acc, lhs, rhs); +} +#else +/* Portable intrinsic versions */ +XXH_FORCE_INLINE uint64x2_t +XXH_vmlal_low_u32(uint64x2_t acc, uint32x4_t lhs, uint32x4_t rhs) +{ + return vmlal_u32(acc, vget_low_u32(lhs), vget_low_u32(rhs)); +} +/*! @copydoc XXH_vmlal_low_u32 + * Assume the compiler converts this to vmlal_high_u32 on aarch64 */ +XXH_FORCE_INLINE uint64x2_t +XXH_vmlal_high_u32(uint64x2_t acc, uint32x4_t lhs, uint32x4_t rhs) +{ + return vmlal_u32(acc, vget_high_u32(lhs), vget_high_u32(rhs)); +} +#endif + +/*! + * @ingroup tuning + * @brief Controls the NEON to scalar ratio for XXH3 * - * aarch64 requires a different approach. + * This can be set to 2, 4, 6, or 8. * - * In order to make it easier to write a decent compiler for aarch64, many - * quirks were removed, such as conditional execution. + * ARM Cortex CPUs are _very_ sensitive to how their pipelines are used. * - * NEON was also affected by this. + * For example, the Cortex-A73 can dispatch 3 micro-ops per cycle, but only 2 of those + * can be NEON. If you are only using NEON instructions, you are only using 2/3 of the CPU + * bandwidth. * - * aarch64 cannot access the high bits of a Q-form register, and writes to a - * D-form register zero the high bits, similar to how writes to W-form scalar - * registers (or DWORD registers on x86_64) work. + * This is even more noticeable on the more advanced cores like the Cortex-A76 which + * can dispatch 8 micro-ops per cycle, but still only 2 NEON micro-ops at once. * - * The formerly free vget_high intrinsics now require a vext (with a few - * exceptions) + * Therefore, to make the most out of the pipeline, it is beneficial to run 6 NEON lanes + * and 2 scalar lanes, which is chosen by default. * - * Additionally, VZIP was replaced by ZIP1 and ZIP2, which are the equivalent - * of PUNPCKL* and PUNPCKH* in SSE, respectively, in order to only modify one - * operand. + * This does not apply to Apple processors or 32-bit processors, which run better with + * full NEON. These will default to 8. Additionally, size-optimized builds run 8 lanes. * - * The equivalent of the VZIP.32 on the lower and upper halves would be this - * mess: + * This change benefits CPUs with large micro-op buffers without negatively affecting + * most other CPUs: * - * ext v2.4s, v0.4s, v0.4s, #2 // v2 = { v0[2], v0[3], v0[0], v0[1] } - * zip1 v1.2s, v0.2s, v2.2s // v1 = { v0[0], v2[0] } - * zip2 v0.2s, v0.2s, v1.2s // v0 = { v0[1], v2[1] } + * | Chipset | Dispatch type | NEON only | 6:2 hybrid | Diff. | + * |:----------------------|:--------------------|----------:|-----------:|------:| + * | Snapdragon 730 (A76) | 2 NEON/8 micro-ops | 8.8 GB/s | 10.1 GB/s | ~16% | + * | Snapdragon 835 (A73) | 2 NEON/3 micro-ops | 5.1 GB/s | 5.3 GB/s | ~5% | + * | Marvell PXA1928 (A53) | In-order dual-issue | 1.9 GB/s | 1.9 GB/s | 0% | + * | Apple M1 | 4 NEON/8 micro-ops | 37.3 GB/s | 36.1 GB/s | ~-3% | * - * Instead, we use a literal downcast, vmovn_u64 (XTN), and vshrn_n_u64 (SHRN): + * It also seems to fix some bad codegen on GCC, making it almost as fast as clang. * - * shrn v1.2s, v0.2d, #32 // v1 = (uint32x2_t)(v0 >> 32); - * xtn v0.2s, v0.2d // v0 = (uint32x2_t)(v0 & 0xFFFFFFFF); + * When using WASM SIMD128, if this is 2 or 6, SIMDe will scalarize 2 of the lanes meaning + * it effectively becomes worse 4. * - * This is available on ARMv7-A, but is less efficient than a single VZIP.32. + * @see XXH3_accumulate_512_neon() */ - -/* - * Function-like macro: - * void XXH_SPLIT_IN_PLACE(uint64x2_t &in, uint32x2_t &outLo, uint32x2_t &outHi) - * { - * outLo = (uint32x2_t)(in & 0xFFFFFFFF); - * outHi = (uint32x2_t)(in >> 32); - * in = UNDEFINED; - * } - */ -# if !defined(XXH_NO_VZIP_HACK) /* define to disable */ \ - && defined(__GNUC__) \ - && !defined(__aarch64__) && !defined(__arm64__) -# define XXH_SPLIT_IN_PLACE(in, outLo, outHi) \ - do { \ - /* Undocumented GCC/Clang operand modifier: %e0 = lower D half, %f0 = upper D half */ \ - /* https://github.com/gcc-mirror/gcc/blob/38cf91e5/gcc/config/arm/arm.c#L22486 */ \ - /* https://github.com/llvm-mirror/llvm/blob/2c4ca683/lib/Target/ARM/ARMAsmPrinter.cpp#L399 */ \ - __asm__("vzip.32 %e0, %f0" : "+w" (in)); \ - (outLo) = vget_low_u32 (vreinterpretq_u32_u64(in)); \ - (outHi) = vget_high_u32(vreinterpretq_u32_u64(in)); \ - } while (0) -# else -# define XXH_SPLIT_IN_PLACE(in, outLo, outHi) \ - do { \ - (outLo) = vmovn_u64 (in); \ - (outHi) = vshrn_n_u64 ((in), 32); \ - } while (0) +# ifndef XXH3_NEON_LANES +# if (defined(__aarch64__) || defined(__arm64__) || defined(_M_ARM64) || defined(_M_ARM64EC)) \ + && !defined(__APPLE__) && XXH_SIZE_OPT <= 0 +# define XXH3_NEON_LANES 6 +# else +# define XXH3_NEON_LANES XXH_ACC_NB +# endif # endif #endif /* XXH_VECTOR == XXH_NEON */ @@ -2395,27 +4209,42 @@ XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src * inconsistent intrinsics, spotty coverage, and multiple endiannesses. */ #if XXH_VECTOR == XXH_VSX +/* Annoyingly, these headers _may_ define three macros: `bool`, `vector`, + * and `pixel`. This is a problem for obvious reasons. + * + * These keywords are unnecessary; the spec literally says they are + * equivalent to `__bool`, `__vector`, and `__pixel` and may be undef'd + * after including the header. + * + * We use pragma push_macro/pop_macro to keep the namespace clean. */ +# pragma push_macro("bool") +# pragma push_macro("vector") +# pragma push_macro("pixel") +/* silence potential macro redefined warnings */ +# undef bool +# undef vector +# undef pixel + # if defined(__s390x__) # include # else -/* gcc's altivec.h can have the unwanted consequence to unconditionally - * #define bool, vector, and pixel keywords, - * with bad consequences for programs already using these keywords for other purposes. - * The paragraph defining these macros is skipped when __APPLE_ALTIVEC__ is defined. - * __APPLE_ALTIVEC__ is _generally_ defined automatically by the compiler, - * but it seems that, in some cases, it isn't. - * Force the build macro to be defined, so that keywords are not altered. - */ -# if defined(__GNUC__) && !defined(__APPLE_ALTIVEC__) -# define __APPLE_ALTIVEC__ -# endif # include # endif +/* Restore the original macro values, if applicable. */ +# pragma pop_macro("pixel") +# pragma pop_macro("vector") +# pragma pop_macro("bool") + typedef __vector unsigned long long xxh_u64x2; typedef __vector unsigned char xxh_u8x16; typedef __vector unsigned xxh_u32x4; +/* + * UGLY HACK: Similar to aarch64 macOS GCC, s390x GCC has the same aliasing issue. + */ +typedef xxh_u64x2 xxh_aliasing_u64x2 XXH_ALIASING; + # ifndef XXH_VSX_BE # if defined(__BIG_ENDIAN__) \ || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) @@ -2429,10 +4258,12 @@ typedef __vector unsigned xxh_u32x4; # endif /* !defined(XXH_VSX_BE) */ # if XXH_VSX_BE -/* A wrapper for POWER9's vec_revb. */ # if defined(__POWER9_VECTOR__) || (defined(__clang__) && defined(__s390x__)) # define XXH_vec_revb vec_revb # else +/*! + * A polyfill for POWER9's vec_revb(). + */ XXH_FORCE_INLINE xxh_u64x2 XXH_vec_revb(xxh_u64x2 val) { xxh_u8x16 const vByteSwap = { 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, @@ -2442,13 +4273,13 @@ XXH_FORCE_INLINE xxh_u64x2 XXH_vec_revb(xxh_u64x2 val) # endif # endif /* XXH_VSX_BE */ -/* - * Performs an unaligned load and byte swaps it on big endian. +/*! + * Performs an unaligned vector load and byte swaps it on big endian. */ XXH_FORCE_INLINE xxh_u64x2 XXH_vec_loadu(const void *ptr) { xxh_u64x2 ret; - memcpy(&ret, ptr, sizeof(xxh_u64x2)); + XXH_memcpy(&ret, ptr, sizeof(xxh_u64x2)); # if XXH_VSX_BE ret = XXH_vec_revb(ret); # endif @@ -2465,8 +4296,9 @@ XXH_FORCE_INLINE xxh_u64x2 XXH_vec_loadu(const void *ptr) /* s390x is always big endian, no issue on this platform */ # define XXH_vec_mulo vec_mulo # define XXH_vec_mule vec_mule -# elif defined(__clang__) && XXH_HAS_BUILTIN(__builtin_altivec_vmuleuw) +# elif defined(__clang__) && XXH_HAS_BUILTIN(__builtin_altivec_vmuleuw) && !defined(__ibmxl__) /* Clang has a better way to control this, we can just use the builtin which doesn't swap. */ + /* The IBM XL Compiler (which defined __clang__) only implements the vec_* operations */ # define XXH_vec_mulo __builtin_altivec_vmulouw # define XXH_vec_mule __builtin_altivec_vmuleuw # else @@ -2487,13 +4319,28 @@ XXH_FORCE_INLINE xxh_u64x2 XXH_vec_mule(xxh_u32x4 a, xxh_u32x4 b) # endif /* XXH_vec_mulo, XXH_vec_mule */ #endif /* XXH_VECTOR == XXH_VSX */ +#if XXH_VECTOR == XXH_SVE +#define ACCRND(acc, offset) \ +do { \ + svuint64_t input_vec = svld1_u64(mask, xinput + offset); \ + svuint64_t secret_vec = svld1_u64(mask, xsecret + offset); \ + svuint64_t mixed = sveor_u64_x(mask, secret_vec, input_vec); \ + svuint64_t swapped = svtbl_u64(input_vec, kSwap); \ + svuint64_t mixed_lo = svextw_u64_x(mask, mixed); \ + svuint64_t mixed_hi = svlsr_n_u64_x(mask, mixed, 32); \ + svuint64_t mul = svmad_u64_x(mask, mixed_lo, mixed_hi, swapped); \ + acc = svadd_u64_x(mask, acc, mul); \ +} while (0) +#endif /* XXH_VECTOR == XXH_SVE */ /* prefetch * can be disabled, by declaring XXH_NO_PREFETCH build macro */ #if defined(XXH_NO_PREFETCH) # define XXH_PREFETCH(ptr) (void)(ptr) /* disabled */ #else -# if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_I86)) /* _mm_prefetch() is not defined outside of x86/x64 */ +# if XXH_SIZE_OPT >= 1 +# define XXH_PREFETCH(ptr) (void)(ptr) +# elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_IX86)) /* _mm_prefetch() not defined outside of x86/x64 */ # include /* https://msdn.microsoft.com/fr-fr/library/84szxsww(v=vs.90).aspx */ # define XXH_PREFETCH(ptr) _mm_prefetch((const char*)(ptr), _MM_HINT_T0) # elif defined(__GNUC__) && ( (__GNUC__ >= 4) || ( (__GNUC__ == 3) && (__GNUC_MINOR__ >= 1) ) ) @@ -2514,7 +4361,7 @@ XXH_FORCE_INLINE xxh_u64x2 XXH_vec_mule(xxh_u32x4 a, xxh_u32x4 b) # error "default keyset is not large enough" #endif -/* Pseudorandom secret taken directly from FARSH */ +/*! Pseudorandom secret taken directly from FARSH. */ XXH_ALIGN(64) static const xxh_u8 XXH3_kSecret[XXH_SECRET_DEFAULT_SIZE] = { 0xb8, 0xfe, 0x6c, 0x39, 0x23, 0xa4, 0x4b, 0xbe, 0x7c, 0x01, 0x81, 0x2c, 0xf7, 0x21, 0xad, 0x1c, 0xde, 0xd4, 0x6d, 0xe9, 0x83, 0x90, 0x97, 0xdb, 0x72, 0x40, 0xa4, 0xa4, 0xb7, 0xb3, 0x67, 0x1f, @@ -2530,29 +4377,36 @@ XXH_ALIGN(64) static const xxh_u8 XXH3_kSecret[XXH_SECRET_DEFAULT_SIZE] = { 0x45, 0xcb, 0x3a, 0x8f, 0x95, 0x16, 0x04, 0x28, 0xaf, 0xd7, 0xfb, 0xca, 0xbb, 0x4b, 0x40, 0x7e, }; +static const xxh_u64 PRIME_MX1 = 0x165667919E3779F9ULL; /*!< 0b0001011001010110011001111001000110011110001101110111100111111001 */ +static const xxh_u64 PRIME_MX2 = 0x9FB21C651E98DF25ULL; /*!< 0b1001111110110010000111000110010100011110100110001101111100100101 */ #ifdef XXH_OLD_NAMES # define kSecret XXH3_kSecret #endif -/* - * Calculates a 32-bit to 64-bit long multiply. +#ifdef XXH_DOXYGEN +/*! + * @brief Calculates a 32-bit to 64-bit long multiply. * - * Wraps __emulu on MSVC x86 because it tends to call __allmul when it doesn't + * Implemented as a macro. + * + * Wraps `__emulu` on MSVC x86 because it tends to call `__allmul` when it doesn't * need to (but it shouldn't need to anyways, it is about 7 instructions to do - * a 64x64 multiply...). Since we know that this will _always_ emit MULL, we + * a 64x64 multiply...). Since we know that this will _always_ emit `MULL`, we * use that instead of the normal method. * * If you are compiling for platforms like Thumb-1 and don't have a better option, * you may also want to write your own long multiply routine here. * - * XXH_FORCE_INLINE xxh_u64 XXH_mult32to64(xxh_u64 x, xxh_u64 y) - * { - * return (x & 0xFFFFFFFF) * (y & 0xFFFFFFFF); - * } + * @param x, y Numbers to be multiplied + * @return 64-bit product of the low 32 bits of @p x and @p y. */ -#if defined(_MSC_VER) && defined(_M_IX86) -# include +XXH_FORCE_INLINE xxh_u64 +XXH_mult32to64(xxh_u64 x, xxh_u64 y) +{ + return (x & 0xFFFFFFFF) * (y & 0xFFFFFFFF); +} +#elif defined(_MSC_VER) && defined(_M_IX86) # define XXH_mult32to64(x, y) __emulu((unsigned)(x), (unsigned)(y)) #else /* @@ -2565,10 +4419,14 @@ XXH_ALIGN(64) static const xxh_u8 XXH3_kSecret[XXH_SECRET_DEFAULT_SIZE] = { # define XXH_mult32to64(x, y) ((xxh_u64)(xxh_u32)(x) * (xxh_u64)(xxh_u32)(y)) #endif -/* - * Calculates a 64->128-bit long multiply. +/*! + * @brief Calculates a 64->128-bit long multiply. * - * Uses __uint128_t and _umul128 if available, otherwise uses a scalar version. + * Uses `__uint128_t` and `_umul128` if available, otherwise uses a scalar + * version. + * + * @param lhs , rhs The 64-bit integers to be multiplied + * @return The 128-bit result represented in an @ref XXH128_hash_t. */ static XXH128_hash_t XXH_mult64to128(xxh_u64 lhs, xxh_u64 rhs) @@ -2588,7 +4446,7 @@ XXH_mult64to128(xxh_u64 lhs, xxh_u64 rhs) * In that case it is best to use the portable one. * https://github.com/Cyan4973/xxHash/issues/211#issuecomment-515575677 */ -#if defined(__GNUC__) && !defined(__wasm__) \ +#if (defined(__GNUC__) || defined(__clang__)) && !defined(__wasm__) \ && defined(__SIZEOF_INT128__) \ || (defined(_INTEGRAL_MAX_BITS) && _INTEGRAL_MAX_BITS >= 128) @@ -2605,7 +4463,7 @@ XXH_mult64to128(xxh_u64 lhs, xxh_u64 rhs) * * This compiles to single operand MUL on x64. */ -#elif defined(_M_X64) || defined(_M_IA64) +#elif (defined(_M_X64) || defined(_M_IA64)) && !defined(_M_ARM64EC) #ifndef _MSC_VER # pragma intrinsic(_umul128) @@ -2617,6 +4475,21 @@ XXH_mult64to128(xxh_u64 lhs, xxh_u64 rhs) r128.high64 = product_high; return r128; + /* + * MSVC for ARM64's __umulh method. + * + * This compiles to the same MUL + UMULH as GCC/Clang's __uint128_t method. + */ +#elif defined(_M_ARM64) || defined(_M_ARM64EC) + +#ifndef _MSC_VER +# pragma intrinsic(__umulh) +#endif + XXH128_hash_t r128; + r128.low64 = lhs * rhs; + r128.high64 = __umulh(lhs, rhs); + return r128; + #else /* * Portable scalar method. Optimized for 32-bit and 64-bit ALUs. @@ -2679,11 +4552,15 @@ XXH_mult64to128(xxh_u64 lhs, xxh_u64 rhs) #endif } -/* - * Does a 64-bit to 128-bit multiply, then XOR folds it. +/*! + * @brief Calculates a 64-bit to 128-bit multiply, then XOR folds it. * * The reason for the separate function is to prevent passing too many structs * around by value. This will hopefully inline the multiply, but we don't force it. + * + * @param lhs , rhs The 64-bit integers to multiply + * @return The low 64 bits of the product XOR'd by the high 64 bits. + * @see XXH_mult64to128() */ static xxh_u64 XXH3_mul128_fold64(xxh_u64 lhs, xxh_u64 rhs) @@ -2692,8 +4569,8 @@ XXH3_mul128_fold64(xxh_u64 lhs, xxh_u64 rhs) return product.low64 ^ product.high64; } -/* Seems to produce slightly better code on GCC for some reason. */ -XXH_FORCE_INLINE xxh_u64 XXH_xorshift64(xxh_u64 v64, int shift) +/*! Seems to produce slightly better code on GCC for some reason. */ +XXH_FORCE_INLINE XXH_CONSTF xxh_u64 XXH_xorshift64(xxh_u64 v64, int shift) { XXH_ASSERT(0 <= shift && shift < 64); return v64 ^ (v64 >> shift); @@ -2706,7 +4583,7 @@ XXH_FORCE_INLINE xxh_u64 XXH_xorshift64(xxh_u64 v64, int shift) static XXH64_hash_t XXH3_avalanche(xxh_u64 h64) { h64 = XXH_xorshift64(h64, 37); - h64 *= 0x165667919E3779F9ULL; + h64 *= PRIME_MX1; h64 = XXH_xorshift64(h64, 32); return h64; } @@ -2720,9 +4597,9 @@ static XXH64_hash_t XXH3_rrmxmx(xxh_u64 h64, xxh_u64 len) { /* this mix is inspired by Pelle Evensen's rrmxmx */ h64 ^= XXH_rotl64(h64, 49) ^ XXH_rotl64(h64, 24); - h64 *= 0x9FB21C651E98DF25ULL; + h64 *= PRIME_MX2; h64 ^= (h64 >> 35) + len ; - h64 *= 0x9FB21C651E98DF25ULL; + h64 *= PRIME_MX2; return XXH_xorshift64(h64, 28); } @@ -2760,7 +4637,7 @@ static XXH64_hash_t XXH3_rrmxmx(xxh_u64 h64, xxh_u64 len) * * This adds an extra layer of strength for custom secrets. */ -XXH_FORCE_INLINE XXH64_hash_t +XXH_FORCE_INLINE XXH_PUREF XXH64_hash_t XXH3_len_1to3_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) { XXH_ASSERT(input != NULL); @@ -2782,12 +4659,12 @@ XXH3_len_1to3_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_h } } -XXH_FORCE_INLINE XXH64_hash_t +XXH_FORCE_INLINE XXH_PUREF XXH64_hash_t XXH3_len_4to8_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) { XXH_ASSERT(input != NULL); XXH_ASSERT(secret != NULL); - XXH_ASSERT(4 <= len && len < 8); + XXH_ASSERT(4 <= len && len <= 8); seed ^= (xxh_u64)XXH_swap32((xxh_u32)seed) << 32; { xxh_u32 const input1 = XXH_readLE32(input); xxh_u32 const input2 = XXH_readLE32(input + len - 4); @@ -2798,12 +4675,12 @@ XXH3_len_4to8_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_h } } -XXH_FORCE_INLINE XXH64_hash_t +XXH_FORCE_INLINE XXH_PUREF XXH64_hash_t XXH3_len_9to16_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) { XXH_ASSERT(input != NULL); XXH_ASSERT(secret != NULL); - XXH_ASSERT(8 <= len && len <= 16); + XXH_ASSERT(9 <= len && len <= 16); { xxh_u64 const bitflip1 = (XXH_readLE64(secret+24) ^ XXH_readLE64(secret+32)) + seed; xxh_u64 const bitflip2 = (XXH_readLE64(secret+40) ^ XXH_readLE64(secret+48)) - seed; xxh_u64 const input_lo = XXH_readLE64(input) ^ bitflip1; @@ -2815,7 +4692,7 @@ XXH3_len_9to16_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_ } } -XXH_FORCE_INLINE XXH64_hash_t +XXH_FORCE_INLINE XXH_PUREF XXH64_hash_t XXH3_len_0to16_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) { XXH_ASSERT(len <= 16); @@ -2873,7 +4750,7 @@ XXH_FORCE_INLINE xxh_u64 XXH3_mix16B(const xxh_u8* XXH_RESTRICT input, * GCC generates much better scalar code than Clang for the rest of XXH3, * which is why finding a more optimal codepath is an interest. */ - __asm__ ("" : "+r" (seed64)); + XXH_COMPILER_GUARD(seed64); #endif { xxh_u64 const input_lo = XXH_readLE64(input); xxh_u64 const input_hi = XXH_readLE64(input+8); @@ -2885,7 +4762,7 @@ XXH_FORCE_INLINE xxh_u64 XXH3_mix16B(const xxh_u8* XXH_RESTRICT input, } /* For mid range keys, XXH3 uses a Mum-hash variant. */ -XXH_FORCE_INLINE XXH64_hash_t +XXH_FORCE_INLINE XXH_PUREF XXH64_hash_t XXH3_len_17to128_64b(const xxh_u8* XXH_RESTRICT input, size_t len, const xxh_u8* XXH_RESTRICT secret, size_t secretSize, XXH64_hash_t seed) @@ -2894,6 +4771,14 @@ XXH3_len_17to128_64b(const xxh_u8* XXH_RESTRICT input, size_t len, XXH_ASSERT(16 < len && len <= 128); { xxh_u64 acc = len * XXH_PRIME64_1; +#if XXH_SIZE_OPT >= 1 + /* Smaller and cleaner, but slightly slower. */ + unsigned int i = (unsigned int)(len - 1) / 32; + do { + acc += XXH3_mix16B(input+16 * i, secret+32*i, seed); + acc += XXH3_mix16B(input+len-16*(i+1), secret+32*i+16, seed); + } while (i-- != 0); +#else if (len > 32) { if (len > 64) { if (len > 96) { @@ -2908,14 +4793,12 @@ XXH3_len_17to128_64b(const xxh_u8* XXH_RESTRICT input, size_t len, } acc += XXH3_mix16B(input+0, secret+0, seed); acc += XXH3_mix16B(input+len-16, secret+16, seed); - +#endif return XXH3_avalanche(acc); } } -#define XXH3_MIDSIZE_MAX 240 - -XXH_NO_INLINE XXH64_hash_t +XXH_NO_INLINE XXH_PUREF XXH64_hash_t XXH3_len_129to240_64b(const xxh_u8* XXH_RESTRICT input, size_t len, const xxh_u8* XXH_RESTRICT secret, size_t secretSize, XXH64_hash_t seed) @@ -2927,13 +4810,17 @@ XXH3_len_129to240_64b(const xxh_u8* XXH_RESTRICT input, size_t len, #define XXH3_MIDSIZE_LASTOFFSET 17 { xxh_u64 acc = len * XXH_PRIME64_1; - int const nbRounds = (int)len / 16; - int i; + xxh_u64 acc_end; + unsigned int const nbRounds = (unsigned int)len / 16; + unsigned int i; + XXH_ASSERT(128 < len && len <= XXH3_MIDSIZE_MAX); for (i=0; i<8; i++) { acc += XXH3_mix16B(input+(16*i), secret+(16*i), seed); } - acc = XXH3_avalanche(acc); + /* last bytes */ + acc_end = XXH3_mix16B(input + len - 16, secret + XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET, seed); XXH_ASSERT(nbRounds >= 8); + acc = XXH3_avalanche(acc); #if defined(__clang__) /* Clang */ \ && (defined(__ARM_NEON) || defined(__ARM_NEON__)) /* NEON */ \ && !defined(XXH_ENABLE_AUTOVECTORIZE) /* Define to disable */ @@ -2960,11 +4847,13 @@ XXH3_len_129to240_64b(const xxh_u8* XXH_RESTRICT input, size_t len, #pragma clang loop vectorize(disable) #endif for (i=8 ; i < nbRounds; i++) { - acc += XXH3_mix16B(input+(16*i), secret+(16*(i-8)) + XXH3_MIDSIZE_STARTOFFSET, seed); + /* + * Prevents clang for unrolling the acc loop and interleaving with this one. + */ + XXH_COMPILER_GUARD(acc); + acc_end += XXH3_mix16B(input+(16*i), secret+(16*(i-8)) + XXH3_MIDSIZE_STARTOFFSET, seed); } - /* last bytes */ - acc += XXH3_mix16B(input + len - 16, secret + XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET, seed); - return XXH3_avalanche(acc); + return XXH3_avalanche(acc + acc_end); } } @@ -2980,10 +4869,51 @@ XXH3_len_129to240_64b(const xxh_u8* XXH_RESTRICT input, size_t len, # define ACC_NB XXH_ACC_NB #endif +#ifndef XXH_PREFETCH_DIST +# ifdef __clang__ +# define XXH_PREFETCH_DIST 320 +# else +# if (XXH_VECTOR == XXH_AVX512) +# define XXH_PREFETCH_DIST 512 +# else +# define XXH_PREFETCH_DIST 384 +# endif +# endif /* __clang__ */ +#endif /* XXH_PREFETCH_DIST */ + +/* + * These macros are to generate an XXH3_accumulate() function. + * The two arguments select the name suffix and target attribute. + * + * The name of this symbol is XXH3_accumulate_() and it calls + * XXH3_accumulate_512_(). + * + * It may be useful to hand implement this function if the compiler fails to + * optimize the inline function. + */ +#define XXH3_ACCUMULATE_TEMPLATE(name) \ +void \ +XXH3_accumulate_##name(xxh_u64* XXH_RESTRICT acc, \ + const xxh_u8* XXH_RESTRICT input, \ + const xxh_u8* XXH_RESTRICT secret, \ + size_t nbStripes) \ +{ \ + size_t n; \ + for (n = 0; n < nbStripes; n++ ) { \ + const xxh_u8* const in = input + n*XXH_STRIPE_LEN; \ + XXH_PREFETCH(in + XXH_PREFETCH_DIST); \ + XXH3_accumulate_512_##name( \ + acc, \ + in, \ + secret + n*XXH_SECRET_CONSUME_RATE); \ + } \ +} + + XXH_FORCE_INLINE void XXH_writeLE64(void* dst, xxh_u64 v64) { if (!XXH_CPU_LITTLE_ENDIAN) v64 = XXH_swap64(v64); - memcpy(dst, &v64, sizeof(v64)); + XXH_memcpy(dst, &v64, sizeof(v64)); } /* Several intrinsic functions below are supposed to accept __int64 as argument, @@ -3000,6 +4930,7 @@ XXH_FORCE_INLINE void XXH_writeLE64(void* dst, xxh_u64 v64) typedef long long xxh_i64; #endif + /* * XXH3_accumulate_512 is the tightest loop for long inputs, and it is the most optimized. * @@ -3023,7 +4954,8 @@ XXH_FORCE_INLINE void XXH_writeLE64(void* dst, xxh_u64 v64) * Both XXH3_64bits and XXH3_128bits use this subroutine. */ -#if (XXH_VECTOR == XXH_AVX512) || defined(XXH_X86DISPATCH) +#if (XXH_VECTOR == XXH_AVX512) \ + || (defined(XXH_DISPATCH_AVX512) && XXH_DISPATCH_AVX512 != 0) #ifndef XXH_TARGET_AVX512 # define XXH_TARGET_AVX512 /* disable attribute target */ @@ -3034,7 +4966,7 @@ XXH3_accumulate_512_avx512(void* XXH_RESTRICT acc, const void* XXH_RESTRICT input, const void* XXH_RESTRICT secret) { - XXH_ALIGN(64) __m512i* const xacc = (__m512i *) acc; + __m512i* const xacc = (__m512i *) acc; XXH_ASSERT((((size_t)acc) & 63) == 0); XXH_STATIC_ASSERT(XXH_STRIPE_LEN == sizeof(__m512i)); @@ -3046,7 +4978,7 @@ XXH3_accumulate_512_avx512(void* XXH_RESTRICT acc, /* data_key = data_vec ^ key_vec; */ __m512i const data_key = _mm512_xor_si512 (data_vec, key_vec); /* data_key_lo = data_key >> 32; */ - __m512i const data_key_lo = _mm512_shuffle_epi32 (data_key, (_MM_PERM_ENUM)_MM_SHUFFLE(0, 3, 0, 1)); + __m512i const data_key_lo = _mm512_srli_epi64 (data_key, 32); /* product = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */ __m512i const product = _mm512_mul_epu32 (data_key, data_key_lo); /* xacc[0] += swap(data_vec); */ @@ -3056,6 +4988,7 @@ XXH3_accumulate_512_avx512(void* XXH_RESTRICT acc, *xacc = _mm512_add_epi64(product, sum); } } +XXH_FORCE_INLINE XXH_TARGET_AVX512 XXH3_ACCUMULATE_TEMPLATE(avx512) /* * XXH3_scrambleAcc: Scrambles the accumulators to improve mixing. @@ -3083,19 +5016,18 @@ XXH3_scrambleAcc_avx512(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) { XXH_ASSERT((((size_t)acc) & 63) == 0); XXH_STATIC_ASSERT(XXH_STRIPE_LEN == sizeof(__m512i)); - { XXH_ALIGN(64) __m512i* const xacc = (__m512i*) acc; + { __m512i* const xacc = (__m512i*) acc; const __m512i prime32 = _mm512_set1_epi32((int)XXH_PRIME32_1); /* xacc[0] ^= (xacc[0] >> 47) */ __m512i const acc_vec = *xacc; __m512i const shifted = _mm512_srli_epi64 (acc_vec, 47); - __m512i const data_vec = _mm512_xor_si512 (acc_vec, shifted); /* xacc[0] ^= secret; */ __m512i const key_vec = _mm512_loadu_si512 (secret); - __m512i const data_key = _mm512_xor_si512 (data_vec, key_vec); + __m512i const data_key = _mm512_ternarylogic_epi32(key_vec, acc_vec, shifted, 0x96 /* key_vec ^ acc_vec ^ shifted */); /* xacc[0] *= XXH_PRIME32_1; */ - __m512i const data_key_hi = _mm512_shuffle_epi32 (data_key, (_MM_PERM_ENUM)_MM_SHUFFLE(0, 3, 0, 1)); + __m512i const data_key_hi = _mm512_srli_epi64 (data_key, 32); __m512i const prod_lo = _mm512_mul_epu32 (data_key, prime32); __m512i const prod_hi = _mm512_mul_epu32 (data_key_hi, prime32); *xacc = _mm512_add_epi64(prod_lo, _mm512_slli_epi64(prod_hi, 32)); @@ -3110,26 +5042,23 @@ XXH3_initCustomSecret_avx512(void* XXH_RESTRICT customSecret, xxh_u64 seed64) XXH_ASSERT(((size_t)customSecret & 63) == 0); (void)(&XXH_writeLE64); { int const nbRounds = XXH_SECRET_DEFAULT_SIZE / sizeof(__m512i); - __m512i const seed = _mm512_mask_set1_epi64(_mm512_set1_epi64((xxh_i64)seed64), 0xAA, -(xxh_i64)seed64); + __m512i const seed_pos = _mm512_set1_epi64((xxh_i64)seed64); + __m512i const seed = _mm512_mask_sub_epi64(seed_pos, 0xAA, _mm512_set1_epi8(0), seed_pos); - XXH_ALIGN(64) const __m512i* const src = (const __m512i*) XXH3_kSecret; - XXH_ALIGN(64) __m512i* const dest = ( __m512i*) customSecret; + const __m512i* const src = (const __m512i*) ((const void*) XXH3_kSecret); + __m512i* const dest = ( __m512i*) customSecret; int i; + XXH_ASSERT(((size_t)src & 63) == 0); /* control alignment */ + XXH_ASSERT(((size_t)dest & 63) == 0); for (i=0; i < nbRounds; ++i) { - /* GCC has a bug, _mm512_stream_load_si512 accepts 'void*', not 'void const*', - * this will warn "discards ‘const’ qualifier". */ - union { - XXH_ALIGN(64) const __m512i* cp; - XXH_ALIGN(64) void* p; - } remote_const_void; - remote_const_void.cp = src + i; - dest[i] = _mm512_add_epi64(_mm512_stream_load_si512(remote_const_void.p), seed); + dest[i] = _mm512_add_epi64(_mm512_load_si512(src + i), seed); } } } #endif -#if (XXH_VECTOR == XXH_AVX2) || defined(XXH_X86DISPATCH) +#if (XXH_VECTOR == XXH_AVX2) \ + || (defined(XXH_DISPATCH_AVX2) && XXH_DISPATCH_AVX2 != 0) #ifndef XXH_TARGET_AVX2 # define XXH_TARGET_AVX2 /* disable attribute target */ @@ -3141,7 +5070,7 @@ XXH3_accumulate_512_avx2( void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) { XXH_ASSERT((((size_t)acc) & 31) == 0); - { XXH_ALIGN(32) __m256i* const xacc = (__m256i *) acc; + { __m256i* const xacc = (__m256i *) acc; /* Unaligned. This is mainly for pointer arithmetic, and because * _mm256_loadu_si256 requires a const __m256i * pointer for some reason. */ const __m256i* const xinput = (const __m256i *) input; @@ -3158,7 +5087,7 @@ XXH3_accumulate_512_avx2( void* XXH_RESTRICT acc, /* data_key = data_vec ^ key_vec; */ __m256i const data_key = _mm256_xor_si256 (data_vec, key_vec); /* data_key_lo = data_key >> 32; */ - __m256i const data_key_lo = _mm256_shuffle_epi32 (data_key, _MM_SHUFFLE(0, 3, 0, 1)); + __m256i const data_key_lo = _mm256_srli_epi64 (data_key, 32); /* product = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */ __m256i const product = _mm256_mul_epu32 (data_key, data_key_lo); /* xacc[i] += swap(data_vec); */ @@ -3168,12 +5097,13 @@ XXH3_accumulate_512_avx2( void* XXH_RESTRICT acc, xacc[i] = _mm256_add_epi64(product, sum); } } } +XXH_FORCE_INLINE XXH_TARGET_AVX2 XXH3_ACCUMULATE_TEMPLATE(avx2) XXH_FORCE_INLINE XXH_TARGET_AVX2 void XXH3_scrambleAcc_avx2(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) { XXH_ASSERT((((size_t)acc) & 31) == 0); - { XXH_ALIGN(32) __m256i* const xacc = (__m256i*) acc; + { __m256i* const xacc = (__m256i*) acc; /* Unaligned. This is mainly for pointer arithmetic, and because * _mm256_loadu_si256 requires a const __m256i * pointer for some reason. */ const __m256i* const xsecret = (const __m256i *) secret; @@ -3190,7 +5120,7 @@ XXH3_scrambleAcc_avx2(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) __m256i const data_key = _mm256_xor_si256 (data_vec, key_vec); /* xacc[i] *= XXH_PRIME32_1; */ - __m256i const data_key_hi = _mm256_shuffle_epi32 (data_key, _MM_SHUFFLE(0, 3, 0, 1)); + __m256i const data_key_hi = _mm256_srli_epi64 (data_key, 32); __m256i const prod_lo = _mm256_mul_epu32 (data_key, prime32); __m256i const prod_hi = _mm256_mul_epu32 (data_key_hi, prime32); xacc[i] = _mm256_add_epi64(prod_lo, _mm256_slli_epi64(prod_hi, 32)); @@ -3205,36 +5135,35 @@ XXH_FORCE_INLINE XXH_TARGET_AVX2 void XXH3_initCustomSecret_avx2(void* XXH_RESTR XXH_STATIC_ASSERT(XXH_SEC_ALIGN <= 64); (void)(&XXH_writeLE64); XXH_PREFETCH(customSecret); - { __m256i const seed = _mm256_set_epi64x(-(xxh_i64)seed64, (xxh_i64)seed64, -(xxh_i64)seed64, (xxh_i64)seed64); + { __m256i const seed = _mm256_set_epi64x((xxh_i64)(0U - seed64), (xxh_i64)seed64, (xxh_i64)(0U - seed64), (xxh_i64)seed64); - XXH_ALIGN(64) const __m256i* const src = (const __m256i*) XXH3_kSecret; - XXH_ALIGN(64) __m256i* dest = ( __m256i*) customSecret; + const __m256i* const src = (const __m256i*) ((const void*) XXH3_kSecret); + __m256i* dest = ( __m256i*) customSecret; # if defined(__GNUC__) || defined(__clang__) /* * On GCC & Clang, marking 'dest' as modified will cause the compiler: * - do not extract the secret from sse registers in the internal loop * - use less common registers, and avoid pushing these reg into stack - * The asm hack causes Clang to assume that XXH3_kSecretPtr aliases with - * customSecret, and on aarch64, this prevented LDP from merging two - * loads together for free. Putting the loads together before the stores - * properly generates LDP. */ - __asm__("" : "+r" (dest)); + XXH_COMPILER_GUARD(dest); # endif + XXH_ASSERT(((size_t)src & 31) == 0); /* control alignment */ + XXH_ASSERT(((size_t)dest & 31) == 0); /* GCC -O2 need unroll loop manually */ - dest[0] = _mm256_add_epi64(_mm256_stream_load_si256(src+0), seed); - dest[1] = _mm256_add_epi64(_mm256_stream_load_si256(src+1), seed); - dest[2] = _mm256_add_epi64(_mm256_stream_load_si256(src+2), seed); - dest[3] = _mm256_add_epi64(_mm256_stream_load_si256(src+3), seed); - dest[4] = _mm256_add_epi64(_mm256_stream_load_si256(src+4), seed); - dest[5] = _mm256_add_epi64(_mm256_stream_load_si256(src+5), seed); + dest[0] = _mm256_add_epi64(_mm256_load_si256(src+0), seed); + dest[1] = _mm256_add_epi64(_mm256_load_si256(src+1), seed); + dest[2] = _mm256_add_epi64(_mm256_load_si256(src+2), seed); + dest[3] = _mm256_add_epi64(_mm256_load_si256(src+3), seed); + dest[4] = _mm256_add_epi64(_mm256_load_si256(src+4), seed); + dest[5] = _mm256_add_epi64(_mm256_load_si256(src+5), seed); } } #endif +/* x86dispatch always generates SSE2 */ #if (XXH_VECTOR == XXH_SSE2) || defined(XXH_X86DISPATCH) #ifndef XXH_TARGET_SSE2 @@ -3248,7 +5177,7 @@ XXH3_accumulate_512_sse2( void* XXH_RESTRICT acc, { /* SSE2 is just a half-scale version of the AVX2 version. */ XXH_ASSERT((((size_t)acc) & 15) == 0); - { XXH_ALIGN(16) __m128i* const xacc = (__m128i *) acc; + { __m128i* const xacc = (__m128i *) acc; /* Unaligned. This is mainly for pointer arithmetic, and because * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */ const __m128i* const xinput = (const __m128i *) input; @@ -3275,12 +5204,13 @@ XXH3_accumulate_512_sse2( void* XXH_RESTRICT acc, xacc[i] = _mm_add_epi64(product, sum); } } } +XXH_FORCE_INLINE XXH_TARGET_SSE2 XXH3_ACCUMULATE_TEMPLATE(sse2) XXH_FORCE_INLINE XXH_TARGET_SSE2 void XXH3_scrambleAcc_sse2(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) { XXH_ASSERT((((size_t)acc) & 15) == 0); - { XXH_ALIGN(16) __m128i* const xacc = (__m128i*) acc; + { __m128i* const xacc = (__m128i*) acc; /* Unaligned. This is mainly for pointer arithmetic, and because * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */ const __m128i* const xsecret = (const __m128i *) secret; @@ -3312,27 +5242,29 @@ XXH_FORCE_INLINE XXH_TARGET_SSE2 void XXH3_initCustomSecret_sse2(void* XXH_RESTR { int const nbRounds = XXH_SECRET_DEFAULT_SIZE / sizeof(__m128i); # if defined(_MSC_VER) && defined(_M_IX86) && _MSC_VER < 1900 - // MSVC 32bit mode does not support _mm_set_epi64x before 2015 - XXH_ALIGN(16) const xxh_i64 seed64x2[2] = { (xxh_i64)seed64, -(xxh_i64)seed64 }; + /* MSVC 32bit mode does not support _mm_set_epi64x before 2015 */ + XXH_ALIGN(16) const xxh_i64 seed64x2[2] = { (xxh_i64)seed64, (xxh_i64)(0U - seed64) }; __m128i const seed = _mm_load_si128((__m128i const*)seed64x2); # else - __m128i const seed = _mm_set_epi64x(-(xxh_i64)seed64, (xxh_i64)seed64); + __m128i const seed = _mm_set_epi64x((xxh_i64)(0U - seed64), (xxh_i64)seed64); # endif int i; - XXH_ALIGN(64) const float* const src = (float const*) XXH3_kSecret; - XXH_ALIGN(XXH_SEC_ALIGN) __m128i* dest = (__m128i*) customSecret; + const void* const src16 = XXH3_kSecret; + __m128i* dst16 = (__m128i*) customSecret; # if defined(__GNUC__) || defined(__clang__) /* * On GCC & Clang, marking 'dest' as modified will cause the compiler: * - do not extract the secret from sse registers in the internal loop * - use less common registers, and avoid pushing these reg into stack */ - __asm__("" : "+r" (dest)); + XXH_COMPILER_GUARD(dst16); # endif + XXH_ASSERT(((size_t)src16 & 15) == 0); /* control alignment */ + XXH_ASSERT(((size_t)dst16 & 15) == 0); for (i=0; i < nbRounds; ++i) { - dest[i] = _mm_add_epi64(_mm_castps_si128(_mm_load_ps(src+i*4)), seed); + dst16[i] = _mm_add_epi64(_mm_load_si128((const __m128i *)src16+i), seed); } } } @@ -3340,96 +5272,222 @@ XXH_FORCE_INLINE XXH_TARGET_SSE2 void XXH3_initCustomSecret_sse2(void* XXH_RESTR #if (XXH_VECTOR == XXH_NEON) +/* forward declarations for the scalar routines */ +XXH_FORCE_INLINE void +XXH3_scalarRound(void* XXH_RESTRICT acc, void const* XXH_RESTRICT input, + void const* XXH_RESTRICT secret, size_t lane); + +XXH_FORCE_INLINE void +XXH3_scalarScrambleRound(void* XXH_RESTRICT acc, + void const* XXH_RESTRICT secret, size_t lane); + +/*! + * @internal + * @brief The bulk processing loop for NEON and WASM SIMD128. + * + * The NEON code path is actually partially scalar when running on AArch64. This + * is to optimize the pipelining and can have up to 15% speedup depending on the + * CPU, and it also mitigates some GCC codegen issues. + * + * @see XXH3_NEON_LANES for configuring this and details about this optimization. + * + * NEON's 32-bit to 64-bit long multiply takes a half vector of 32-bit + * integers instead of the other platforms which mask full 64-bit vectors, + * so the setup is more complicated than just shifting right. + * + * Additionally, there is an optimization for 4 lanes at once noted below. + * + * Since, as stated, the most optimal amount of lanes for Cortexes is 6, + * there needs to be *three* versions of the accumulate operation used + * for the remaining 2 lanes. + * + * WASM's SIMD128 uses SIMDe's arm_neon.h polyfill because the intrinsics overlap + * nearly perfectly. + */ + XXH_FORCE_INLINE void XXH3_accumulate_512_neon( void* XXH_RESTRICT acc, const void* XXH_RESTRICT input, const void* XXH_RESTRICT secret) { XXH_ASSERT((((size_t)acc) & 15) == 0); - { - XXH_ALIGN(16) uint64x2_t* const xacc = (uint64x2_t *) acc; + XXH_STATIC_ASSERT(XXH3_NEON_LANES > 0 && XXH3_NEON_LANES <= XXH_ACC_NB && XXH3_NEON_LANES % 2 == 0); + { /* GCC for darwin arm64 does not like aliasing here */ + xxh_aliasing_uint64x2_t* const xacc = (xxh_aliasing_uint64x2_t*) acc; /* We don't use a uint32x4_t pointer because it causes bus errors on ARMv7. */ - uint8_t const* const xinput = (const uint8_t *) input; - uint8_t const* const xsecret = (const uint8_t *) secret; + uint8_t const* xinput = (const uint8_t *) input; + uint8_t const* xsecret = (const uint8_t *) secret; size_t i; - for (i=0; i < XXH_STRIPE_LEN / sizeof(uint64x2_t); i++) { +#ifdef __wasm_simd128__ + /* + * On WASM SIMD128, Clang emits direct address loads when XXH3_kSecret + * is constant propagated, which results in it converting it to this + * inside the loop: + * + * a = v128.load(XXH3_kSecret + 0 + $secret_offset, offset = 0) + * b = v128.load(XXH3_kSecret + 16 + $secret_offset, offset = 0) + * ... + * + * This requires a full 32-bit address immediate (and therefore a 6 byte + * instruction) as well as an add for each offset. + * + * Putting an asm guard prevents it from folding (at the cost of losing + * the alignment hint), and uses the free offset in `v128.load` instead + * of adding secret_offset each time which overall reduces code size by + * about a kilobyte and improves performance. + */ + XXH_COMPILER_GUARD(xsecret); +#endif + /* Scalar lanes use the normal scalarRound routine */ + for (i = XXH3_NEON_LANES; i < XXH_ACC_NB; i++) { + XXH3_scalarRound(acc, input, secret, i); + } + i = 0; + /* 4 NEON lanes at a time. */ + for (; i+1 < XXH3_NEON_LANES / 2; i+=2) { /* data_vec = xinput[i]; */ - uint8x16_t data_vec = vld1q_u8(xinput + (i * 16)); + uint64x2_t data_vec_1 = XXH_vld1q_u64(xinput + (i * 16)); + uint64x2_t data_vec_2 = XXH_vld1q_u64(xinput + ((i+1) * 16)); /* key_vec = xsecret[i]; */ - uint8x16_t key_vec = vld1q_u8(xsecret + (i * 16)); - uint64x2_t data_key; - uint32x2_t data_key_lo, data_key_hi; - /* xacc[i] += swap(data_vec); */ - uint64x2_t const data64 = vreinterpretq_u64_u8(data_vec); - uint64x2_t const swapped = vextq_u64(data64, data64, 1); - xacc[i] = vaddq_u64 (xacc[i], swapped); + uint64x2_t key_vec_1 = XXH_vld1q_u64(xsecret + (i * 16)); + uint64x2_t key_vec_2 = XXH_vld1q_u64(xsecret + ((i+1) * 16)); + /* data_swap = swap(data_vec) */ + uint64x2_t data_swap_1 = vextq_u64(data_vec_1, data_vec_1, 1); + uint64x2_t data_swap_2 = vextq_u64(data_vec_2, data_vec_2, 1); /* data_key = data_vec ^ key_vec; */ - data_key = vreinterpretq_u64_u8(veorq_u8(data_vec, key_vec)); - /* data_key_lo = (uint32x2_t) (data_key & 0xFFFFFFFF); - * data_key_hi = (uint32x2_t) (data_key >> 32); - * data_key = UNDEFINED; */ - XXH_SPLIT_IN_PLACE(data_key, data_key_lo, data_key_hi); - /* xacc[i] += (uint64x2_t) data_key_lo * (uint64x2_t) data_key_hi; */ - xacc[i] = vmlal_u32 (xacc[i], data_key_lo, data_key_hi); + uint64x2_t data_key_1 = veorq_u64(data_vec_1, key_vec_1); + uint64x2_t data_key_2 = veorq_u64(data_vec_2, key_vec_2); + /* + * If we reinterpret the 64x2 vectors as 32x4 vectors, we can use a + * de-interleave operation for 4 lanes in 1 step with `vuzpq_u32` to + * get one vector with the low 32 bits of each lane, and one vector + * with the high 32 bits of each lane. + * + * The intrinsic returns a double vector because the original ARMv7-a + * instruction modified both arguments in place. AArch64 and SIMD128 emit + * two instructions from this intrinsic. + * + * [ dk11L | dk11H | dk12L | dk12H ] -> [ dk11L | dk12L | dk21L | dk22L ] + * [ dk21L | dk21H | dk22L | dk22H ] -> [ dk11H | dk12H | dk21H | dk22H ] + */ + uint32x4x2_t unzipped = vuzpq_u32( + vreinterpretq_u32_u64(data_key_1), + vreinterpretq_u32_u64(data_key_2) + ); + /* data_key_lo = data_key & 0xFFFFFFFF */ + uint32x4_t data_key_lo = unzipped.val[0]; + /* data_key_hi = data_key >> 32 */ + uint32x4_t data_key_hi = unzipped.val[1]; + /* + * Then, we can split the vectors horizontally and multiply which, as for most + * widening intrinsics, have a variant that works on both high half vectors + * for free on AArch64. A similar instruction is available on SIMD128. + * + * sum = data_swap + (u64x2) data_key_lo * (u64x2) data_key_hi + */ + uint64x2_t sum_1 = XXH_vmlal_low_u32(data_swap_1, data_key_lo, data_key_hi); + uint64x2_t sum_2 = XXH_vmlal_high_u32(data_swap_2, data_key_lo, data_key_hi); + /* + * Clang reorders + * a += b * c; // umlal swap.2d, dkl.2s, dkh.2s + * c += a; // add acc.2d, acc.2d, swap.2d + * to + * c += a; // add acc.2d, acc.2d, swap.2d + * c += b * c; // umlal acc.2d, dkl.2s, dkh.2s + * + * While it would make sense in theory since the addition is faster, + * for reasons likely related to umlal being limited to certain NEON + * pipelines, this is worse. A compiler guard fixes this. + */ + XXH_COMPILER_GUARD_CLANG_NEON(sum_1); + XXH_COMPILER_GUARD_CLANG_NEON(sum_2); + /* xacc[i] = acc_vec + sum; */ + xacc[i] = vaddq_u64(xacc[i], sum_1); + xacc[i+1] = vaddq_u64(xacc[i+1], sum_2); + } + /* Operate on the remaining NEON lanes 2 at a time. */ + for (; i < XXH3_NEON_LANES / 2; i++) { + /* data_vec = xinput[i]; */ + uint64x2_t data_vec = XXH_vld1q_u64(xinput + (i * 16)); + /* key_vec = xsecret[i]; */ + uint64x2_t key_vec = XXH_vld1q_u64(xsecret + (i * 16)); + /* acc_vec_2 = swap(data_vec) */ + uint64x2_t data_swap = vextq_u64(data_vec, data_vec, 1); + /* data_key = data_vec ^ key_vec; */ + uint64x2_t data_key = veorq_u64(data_vec, key_vec); + /* For two lanes, just use VMOVN and VSHRN. */ + /* data_key_lo = data_key & 0xFFFFFFFF; */ + uint32x2_t data_key_lo = vmovn_u64(data_key); + /* data_key_hi = data_key >> 32; */ + uint32x2_t data_key_hi = vshrn_n_u64(data_key, 32); + /* sum = data_swap + (u64x2) data_key_lo * (u64x2) data_key_hi; */ + uint64x2_t sum = vmlal_u32(data_swap, data_key_lo, data_key_hi); + /* Same Clang workaround as before */ + XXH_COMPILER_GUARD_CLANG_NEON(sum); + /* xacc[i] = acc_vec + sum; */ + xacc[i] = vaddq_u64 (xacc[i], sum); } } } +XXH_FORCE_INLINE XXH3_ACCUMULATE_TEMPLATE(neon) XXH_FORCE_INLINE void XXH3_scrambleAcc_neon(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) { XXH_ASSERT((((size_t)acc) & 15) == 0); - { uint64x2_t* xacc = (uint64x2_t*) acc; + { xxh_aliasing_uint64x2_t* xacc = (xxh_aliasing_uint64x2_t*) acc; uint8_t const* xsecret = (uint8_t const*) secret; - uint32x2_t prime = vdup_n_u32 (XXH_PRIME32_1); size_t i; - for (i=0; i < XXH_STRIPE_LEN/sizeof(uint64x2_t); i++) { + /* WASM uses operator overloads and doesn't need these. */ +#ifndef __wasm_simd128__ + /* { prime32_1, prime32_1 } */ + uint32x2_t const kPrimeLo = vdup_n_u32(XXH_PRIME32_1); + /* { 0, prime32_1, 0, prime32_1 } */ + uint32x4_t const kPrimeHi = vreinterpretq_u32_u64(vdupq_n_u64((xxh_u64)XXH_PRIME32_1 << 32)); +#endif + + /* AArch64 uses both scalar and neon at the same time */ + for (i = XXH3_NEON_LANES; i < XXH_ACC_NB; i++) { + XXH3_scalarScrambleRound(acc, secret, i); + } + for (i=0; i < XXH3_NEON_LANES / 2; i++) { /* xacc[i] ^= (xacc[i] >> 47); */ uint64x2_t acc_vec = xacc[i]; - uint64x2_t shifted = vshrq_n_u64 (acc_vec, 47); - uint64x2_t data_vec = veorq_u64 (acc_vec, shifted); + uint64x2_t shifted = vshrq_n_u64(acc_vec, 47); + uint64x2_t data_vec = veorq_u64(acc_vec, shifted); /* xacc[i] ^= xsecret[i]; */ - uint8x16_t key_vec = vld1q_u8(xsecret + (i * 16)); - uint64x2_t data_key = veorq_u64(data_vec, vreinterpretq_u64_u8(key_vec)); - + uint64x2_t key_vec = XXH_vld1q_u64(xsecret + (i * 16)); + uint64x2_t data_key = veorq_u64(data_vec, key_vec); /* xacc[i] *= XXH_PRIME32_1 */ - uint32x2_t data_key_lo, data_key_hi; - /* data_key_lo = (uint32x2_t) (xacc[i] & 0xFFFFFFFF); - * data_key_hi = (uint32x2_t) (xacc[i] >> 32); - * xacc[i] = UNDEFINED; */ - XXH_SPLIT_IN_PLACE(data_key, data_key_lo, data_key_hi); - { /* - * prod_hi = (data_key >> 32) * XXH_PRIME32_1; - * - * Avoid vmul_u32 + vshll_n_u32 since Clang 6 and 7 will - * incorrectly "optimize" this: - * tmp = vmul_u32(vmovn_u64(a), vmovn_u64(b)); - * shifted = vshll_n_u32(tmp, 32); - * to this: - * tmp = "vmulq_u64"(a, b); // no such thing! - * shifted = vshlq_n_u64(tmp, 32); - * - * However, unlike SSE, Clang lacks a 64-bit multiply routine - * for NEON, and it scalarizes two 64-bit multiplies instead. - * - * vmull_u32 has the same timing as vmul_u32, and it avoids - * this bug completely. - * See https://bugs.llvm.org/show_bug.cgi?id=39967 - */ - uint64x2_t prod_hi = vmull_u32 (data_key_hi, prime); - /* xacc[i] = prod_hi << 32; */ - xacc[i] = vshlq_n_u64(prod_hi, 32); - /* xacc[i] += (prod_hi & 0xFFFFFFFF) * XXH_PRIME32_1; */ - xacc[i] = vmlal_u32(xacc[i], data_key_lo, prime); - } - } } +#ifdef __wasm_simd128__ + /* SIMD128 has multiply by u64x2, use it instead of expanding and scalarizing */ + xacc[i] = data_key * XXH_PRIME32_1; +#else + /* + * Expanded version with portable NEON intrinsics + * + * lo(x) * lo(y) + (hi(x) * lo(y) << 32) + * + * prod_hi = hi(data_key) * lo(prime) << 32 + * + * Since we only need 32 bits of this multiply a trick can be used, reinterpreting the vector + * as a uint32x4_t and multiplying by { 0, prime, 0, prime } to cancel out the unwanted bits + * and avoid the shift. + */ + uint32x4_t prod_hi = vmulq_u32 (vreinterpretq_u32_u64(data_key), kPrimeHi); + /* Extract low bits for vmlal_u32 */ + uint32x2_t data_key_lo = vmovn_u64(data_key); + /* xacc[i] = prod_hi + lo(data_key) * XXH_PRIME32_1; */ + xacc[i] = vmlal_u32(vreinterpretq_u64_u32(prod_hi), data_key_lo, kPrimeLo); +#endif + } + } } - #endif #if (XXH_VECTOR == XXH_VSX) @@ -3439,39 +5497,44 @@ XXH3_accumulate_512_vsx( void* XXH_RESTRICT acc, const void* XXH_RESTRICT input, const void* XXH_RESTRICT secret) { - xxh_u64x2* const xacc = (xxh_u64x2*) acc; /* presumed aligned */ - xxh_u64x2 const* const xinput = (xxh_u64x2 const*) input; /* no alignment restriction */ - xxh_u64x2 const* const xsecret = (xxh_u64x2 const*) secret; /* no alignment restriction */ + /* presumed aligned */ + xxh_aliasing_u64x2* const xacc = (xxh_aliasing_u64x2*) acc; + xxh_u8 const* const xinput = (xxh_u8 const*) input; /* no alignment restriction */ + xxh_u8 const* const xsecret = (xxh_u8 const*) secret; /* no alignment restriction */ xxh_u64x2 const v32 = { 32, 32 }; size_t i; for (i = 0; i < XXH_STRIPE_LEN / sizeof(xxh_u64x2); i++) { /* data_vec = xinput[i]; */ - xxh_u64x2 const data_vec = XXH_vec_loadu(xinput + i); + xxh_u64x2 const data_vec = XXH_vec_loadu(xinput + 16*i); /* key_vec = xsecret[i]; */ - xxh_u64x2 const key_vec = XXH_vec_loadu(xsecret + i); + xxh_u64x2 const key_vec = XXH_vec_loadu(xsecret + 16*i); xxh_u64x2 const data_key = data_vec ^ key_vec; /* shuffled = (data_key << 32) | (data_key >> 32); */ xxh_u32x4 const shuffled = (xxh_u32x4)vec_rl(data_key, v32); /* product = ((xxh_u64x2)data_key & 0xFFFFFFFF) * ((xxh_u64x2)shuffled & 0xFFFFFFFF); */ xxh_u64x2 const product = XXH_vec_mulo((xxh_u32x4)data_key, shuffled); - xacc[i] += product; + /* acc_vec = xacc[i]; */ + xxh_u64x2 acc_vec = xacc[i]; + acc_vec += product; /* swap high and low halves */ #ifdef __s390x__ - xacc[i] += vec_permi(data_vec, data_vec, 2); + acc_vec += vec_permi(data_vec, data_vec, 2); #else - xacc[i] += vec_xxpermdi(data_vec, data_vec, 2); + acc_vec += vec_xxpermdi(data_vec, data_vec, 2); #endif + xacc[i] = acc_vec; } } +XXH_FORCE_INLINE XXH3_ACCUMULATE_TEMPLATE(vsx) XXH_FORCE_INLINE void XXH3_scrambleAcc_vsx(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) { XXH_ASSERT((((size_t)acc) & 15) == 0); - { xxh_u64x2* const xacc = (xxh_u64x2*) acc; - const xxh_u64x2* const xsecret = (const xxh_u64x2*) secret; + { xxh_aliasing_u64x2* const xacc = (xxh_aliasing_u64x2*) acc; + const xxh_u8* const xsecret = (const xxh_u8*) secret; /* constants */ xxh_u64x2 const v32 = { 32, 32 }; xxh_u64x2 const v47 = { 47, 47 }; @@ -3483,54 +5546,312 @@ XXH3_scrambleAcc_vsx(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) xxh_u64x2 const data_vec = acc_vec ^ (acc_vec >> v47); /* xacc[i] ^= xsecret[i]; */ - xxh_u64x2 const key_vec = XXH_vec_loadu(xsecret + i); + xxh_u64x2 const key_vec = XXH_vec_loadu(xsecret + 16*i); xxh_u64x2 const data_key = data_vec ^ key_vec; - /* xacc[i] *= XXH_PRIME32_1 */ - /* prod_lo = ((xxh_u64x2)data_key & 0xFFFFFFFF) * ((xxh_u64x2)prime & 0xFFFFFFFF); */ - xxh_u64x2 const prod_even = XXH_vec_mule((xxh_u32x4)data_key, prime); - /* prod_hi = ((xxh_u64x2)data_key >> 32) * ((xxh_u64x2)prime >> 32); */ - xxh_u64x2 const prod_odd = XXH_vec_mulo((xxh_u32x4)data_key, prime); - xacc[i] = prod_odd + (prod_even << v32); - } } + /* xacc[i] *= XXH_PRIME32_1 */ + /* prod_lo = ((xxh_u64x2)data_key & 0xFFFFFFFF) * ((xxh_u64x2)prime & 0xFFFFFFFF); */ + xxh_u64x2 const prod_even = XXH_vec_mule((xxh_u32x4)data_key, prime); + /* prod_hi = ((xxh_u64x2)data_key >> 32) * ((xxh_u64x2)prime >> 32); */ + xxh_u64x2 const prod_odd = XXH_vec_mulo((xxh_u32x4)data_key, prime); + xacc[i] = prod_odd + (prod_even << v32); + } } +} + +#endif + +#if (XXH_VECTOR == XXH_SVE) + +XXH_FORCE_INLINE void +XXH3_accumulate_512_sve( void* XXH_RESTRICT acc, + const void* XXH_RESTRICT input, + const void* XXH_RESTRICT secret) +{ + uint64_t *xacc = (uint64_t *)acc; + const uint64_t *xinput = (const uint64_t *)(const void *)input; + const uint64_t *xsecret = (const uint64_t *)(const void *)secret; + svuint64_t kSwap = sveor_n_u64_z(svptrue_b64(), svindex_u64(0, 1), 1); + uint64_t element_count = svcntd(); + if (element_count >= 8) { + svbool_t mask = svptrue_pat_b64(SV_VL8); + svuint64_t vacc = svld1_u64(mask, xacc); + ACCRND(vacc, 0); + svst1_u64(mask, xacc, vacc); + } else if (element_count == 2) { /* sve128 */ + svbool_t mask = svptrue_pat_b64(SV_VL2); + svuint64_t acc0 = svld1_u64(mask, xacc + 0); + svuint64_t acc1 = svld1_u64(mask, xacc + 2); + svuint64_t acc2 = svld1_u64(mask, xacc + 4); + svuint64_t acc3 = svld1_u64(mask, xacc + 6); + ACCRND(acc0, 0); + ACCRND(acc1, 2); + ACCRND(acc2, 4); + ACCRND(acc3, 6); + svst1_u64(mask, xacc + 0, acc0); + svst1_u64(mask, xacc + 2, acc1); + svst1_u64(mask, xacc + 4, acc2); + svst1_u64(mask, xacc + 6, acc3); + } else { + svbool_t mask = svptrue_pat_b64(SV_VL4); + svuint64_t acc0 = svld1_u64(mask, xacc + 0); + svuint64_t acc1 = svld1_u64(mask, xacc + 4); + ACCRND(acc0, 0); + ACCRND(acc1, 4); + svst1_u64(mask, xacc + 0, acc0); + svst1_u64(mask, xacc + 4, acc1); + } +} + +XXH_FORCE_INLINE void +XXH3_accumulate_sve(xxh_u64* XXH_RESTRICT acc, + const xxh_u8* XXH_RESTRICT input, + const xxh_u8* XXH_RESTRICT secret, + size_t nbStripes) +{ + if (nbStripes != 0) { + uint64_t *xacc = (uint64_t *)acc; + const uint64_t *xinput = (const uint64_t *)(const void *)input; + const uint64_t *xsecret = (const uint64_t *)(const void *)secret; + svuint64_t kSwap = sveor_n_u64_z(svptrue_b64(), svindex_u64(0, 1), 1); + uint64_t element_count = svcntd(); + if (element_count >= 8) { + svbool_t mask = svptrue_pat_b64(SV_VL8); + svuint64_t vacc = svld1_u64(mask, xacc + 0); + do { + /* svprfd(svbool_t, void *, enum svfprop); */ + svprfd(mask, xinput + 128, SV_PLDL1STRM); + ACCRND(vacc, 0); + xinput += 8; + xsecret += 1; + nbStripes--; + } while (nbStripes != 0); + + svst1_u64(mask, xacc + 0, vacc); + } else if (element_count == 2) { /* sve128 */ + svbool_t mask = svptrue_pat_b64(SV_VL2); + svuint64_t acc0 = svld1_u64(mask, xacc + 0); + svuint64_t acc1 = svld1_u64(mask, xacc + 2); + svuint64_t acc2 = svld1_u64(mask, xacc + 4); + svuint64_t acc3 = svld1_u64(mask, xacc + 6); + do { + svprfd(mask, xinput + 128, SV_PLDL1STRM); + ACCRND(acc0, 0); + ACCRND(acc1, 2); + ACCRND(acc2, 4); + ACCRND(acc3, 6); + xinput += 8; + xsecret += 1; + nbStripes--; + } while (nbStripes != 0); + + svst1_u64(mask, xacc + 0, acc0); + svst1_u64(mask, xacc + 2, acc1); + svst1_u64(mask, xacc + 4, acc2); + svst1_u64(mask, xacc + 6, acc3); + } else { + svbool_t mask = svptrue_pat_b64(SV_VL4); + svuint64_t acc0 = svld1_u64(mask, xacc + 0); + svuint64_t acc1 = svld1_u64(mask, xacc + 4); + do { + svprfd(mask, xinput + 128, SV_PLDL1STRM); + ACCRND(acc0, 0); + ACCRND(acc1, 4); + xinput += 8; + xsecret += 1; + nbStripes--; + } while (nbStripes != 0); + + svst1_u64(mask, xacc + 0, acc0); + svst1_u64(mask, xacc + 4, acc1); + } + } +} + +#endif + +#if (XXH_VECTOR == XXH_LSX) +#define _LSX_SHUFFLE(z, y, x, w) (((z) << 6) | ((y) << 4) | ((x) << 2) | (w)) + +XXH_FORCE_INLINE void +XXH3_accumulate_512_lsx( void* XXH_RESTRICT acc, + const void* XXH_RESTRICT input, + const void* XXH_RESTRICT secret) +{ + XXH_ASSERT((((size_t)acc) & 15) == 0); + { + __m128i* const xacc = (__m128i *) acc; + const __m128i* const xinput = (const __m128i *) input; + const __m128i* const xsecret = (const __m128i *) secret; + + for (size_t i = 0; i < XXH_STRIPE_LEN / sizeof(__m128i); i++) { + /* data_vec = xinput[i]; */ + __m128i const data_vec = __lsx_vld(xinput + i, 0); + /* key_vec = xsecret[i]; */ + __m128i const key_vec = __lsx_vld(xsecret + i, 0); + /* data_key = data_vec ^ key_vec; */ + __m128i const data_key = __lsx_vxor_v(data_vec, key_vec); + /* data_key_lo = data_key >> 32; */ + __m128i const data_key_lo = __lsx_vsrli_d(data_key, 32); + // __m128i const data_key_lo = __lsx_vsrli_d(data_key, 32); + /* product = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */ + __m128i const product = __lsx_vmulwev_d_wu(data_key, data_key_lo); + /* xacc[i] += swap(data_vec); */ + __m128i const data_swap = __lsx_vshuf4i_w(data_vec, _LSX_SHUFFLE(1, 0, 3, 2)); + __m128i const sum = __lsx_vadd_d(xacc[i], data_swap); + /* xacc[i] += product; */ + xacc[i] = __lsx_vadd_d(product, sum); + } + } +} +XXH_FORCE_INLINE XXH3_ACCUMULATE_TEMPLATE(lsx) + +XXH_FORCE_INLINE void +XXH3_scrambleAcc_lsx(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) +{ + XXH_ASSERT((((size_t)acc) & 15) == 0); + { + __m128i* const xacc = (__m128i*) acc; + const __m128i* const xsecret = (const __m128i *) secret; + const __m128i prime32 = __lsx_vreplgr2vr_w((int)XXH_PRIME32_1); + + for (size_t i = 0; i < XXH_STRIPE_LEN / sizeof(__m128i); i++) { + /* xacc[i] ^= (xacc[i] >> 47) */ + __m128i const acc_vec = xacc[i]; + __m128i const shifted = __lsx_vsrli_d(acc_vec, 47); + __m128i const data_vec = __lsx_vxor_v(acc_vec, shifted); + /* xacc[i] ^= xsecret[i]; */ + __m128i const key_vec = __lsx_vld(xsecret + i, 0); + __m128i const data_key = __lsx_vxor_v(data_vec, key_vec); + + /* xacc[i] *= XXH_PRIME32_1; */ + __m128i const data_key_hi = __lsx_vsrli_d(data_key, 32); + __m128i const prod_lo = __lsx_vmulwev_d_wu(data_key, prime32); + __m128i const prod_hi = __lsx_vmulwev_d_wu(data_key_hi, prime32); + xacc[i] = __lsx_vadd_d(prod_lo, __lsx_vslli_d(prod_hi, 32)); + } + } } #endif /* scalar variants - universal */ +#if defined(__aarch64__) && (defined(__GNUC__) || defined(__clang__)) +/* + * In XXH3_scalarRound(), GCC and Clang have a similar codegen issue, where they + * emit an excess mask and a full 64-bit multiply-add (MADD X-form). + * + * While this might not seem like much, as AArch64 is a 64-bit architecture, only + * big Cortex designs have a full 64-bit multiplier. + * + * On the little cores, the smaller 32-bit multiplier is used, and full 64-bit + * multiplies expand to 2-3 multiplies in microcode. This has a major penalty + * of up to 4 latency cycles and 2 stall cycles in the multiply pipeline. + * + * Thankfully, AArch64 still provides the 32-bit long multiply-add (UMADDL) which does + * not have this penalty and does the mask automatically. + */ +XXH_FORCE_INLINE xxh_u64 +XXH_mult32to64_add64(xxh_u64 lhs, xxh_u64 rhs, xxh_u64 acc) +{ + xxh_u64 ret; + /* note: %x = 64-bit register, %w = 32-bit register */ + __asm__("umaddl %x0, %w1, %w2, %x3" : "=r" (ret) : "r" (lhs), "r" (rhs), "r" (acc)); + return ret; +} +#else +XXH_FORCE_INLINE xxh_u64 +XXH_mult32to64_add64(xxh_u64 lhs, xxh_u64 rhs, xxh_u64 acc) +{ + return XXH_mult32to64((xxh_u32)lhs, (xxh_u32)rhs) + acc; +} +#endif + +/*! + * @internal + * @brief Scalar round for @ref XXH3_accumulate_512_scalar(). + * + * This is extracted to its own function because the NEON path uses a combination + * of NEON and scalar. + */ +XXH_FORCE_INLINE void +XXH3_scalarRound(void* XXH_RESTRICT acc, + void const* XXH_RESTRICT input, + void const* XXH_RESTRICT secret, + size_t lane) +{ + xxh_u64* xacc = (xxh_u64*) acc; + xxh_u8 const* xinput = (xxh_u8 const*) input; + xxh_u8 const* xsecret = (xxh_u8 const*) secret; + XXH_ASSERT(lane < XXH_ACC_NB); + XXH_ASSERT(((size_t)acc & (XXH_ACC_ALIGN-1)) == 0); + { + xxh_u64 const data_val = XXH_readLE64(xinput + lane * 8); + xxh_u64 const data_key = data_val ^ XXH_readLE64(xsecret + lane * 8); + xacc[lane ^ 1] += data_val; /* swap adjacent lanes */ + xacc[lane] = XXH_mult32to64_add64(data_key /* & 0xFFFFFFFF */, data_key >> 32, xacc[lane]); + } +} + +/*! + * @internal + * @brief Processes a 64 byte block of data using the scalar path. + */ XXH_FORCE_INLINE void XXH3_accumulate_512_scalar(void* XXH_RESTRICT acc, const void* XXH_RESTRICT input, const void* XXH_RESTRICT secret) { - XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64* const xacc = (xxh_u64*) acc; /* presumed aligned */ - const xxh_u8* const xinput = (const xxh_u8*) input; /* no alignment restriction */ - const xxh_u8* const xsecret = (const xxh_u8*) secret; /* no alignment restriction */ size_t i; - XXH_ASSERT(((size_t)acc & (XXH_ACC_ALIGN-1)) == 0); + /* ARM GCC refuses to unroll this loop, resulting in a 24% slowdown on ARMv6. */ +#if defined(__GNUC__) && !defined(__clang__) \ + && (defined(__arm__) || defined(__thumb2__)) \ + && defined(__ARM_FEATURE_UNALIGNED) /* no unaligned access just wastes bytes */ \ + && XXH_SIZE_OPT <= 0 +# pragma GCC unroll 8 +#endif for (i=0; i < XXH_ACC_NB; i++) { - xxh_u64 const data_val = XXH_readLE64(xinput + 8*i); - xxh_u64 const data_key = data_val ^ XXH_readLE64(xsecret + i*8); - xacc[i ^ 1] += data_val; /* swap adjacent lanes */ - xacc[i] += XXH_mult32to64(data_key & 0xFFFFFFFF, data_key >> 32); + XXH3_scalarRound(acc, input, secret, i); } } +XXH_FORCE_INLINE XXH3_ACCUMULATE_TEMPLATE(scalar) +/*! + * @internal + * @brief Scalar scramble step for @ref XXH3_scrambleAcc_scalar(). + * + * This is extracted to its own function because the NEON path uses a combination + * of NEON and scalar. + */ XXH_FORCE_INLINE void -XXH3_scrambleAcc_scalar(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) +XXH3_scalarScrambleRound(void* XXH_RESTRICT acc, + void const* XXH_RESTRICT secret, + size_t lane) { - XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64* const xacc = (xxh_u64*) acc; /* presumed aligned */ + xxh_u64* const xacc = (xxh_u64*) acc; /* presumed aligned */ const xxh_u8* const xsecret = (const xxh_u8*) secret; /* no alignment restriction */ - size_t i; XXH_ASSERT((((size_t)acc) & (XXH_ACC_ALIGN-1)) == 0); - for (i=0; i < XXH_ACC_NB; i++) { - xxh_u64 const key64 = XXH_readLE64(xsecret + 8*i); - xxh_u64 acc64 = xacc[i]; + XXH_ASSERT(lane < XXH_ACC_NB); + { + xxh_u64 const key64 = XXH_readLE64(xsecret + lane * 8); + xxh_u64 acc64 = xacc[lane]; acc64 = XXH_xorshift64(acc64, 47); acc64 ^= key64; acc64 *= XXH_PRIME32_1; - xacc[i] = acc64; + xacc[lane] = acc64; + } +} + +/*! + * @internal + * @brief Scrambles the accumulators after a large chunk has been read + */ +XXH_FORCE_INLINE void +XXH3_scrambleAcc_scalar(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) +{ + size_t i; + for (i=0; i < XXH_ACC_NB; i++) { + XXH3_scalarScrambleRound(acc, secret, i); } } @@ -3545,15 +5866,16 @@ XXH3_initCustomSecret_scalar(void* XXH_RESTRICT customSecret, xxh_u64 seed64) const xxh_u8* kSecretPtr = XXH3_kSecret; XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 15) == 0); -#if defined(__clang__) && defined(__aarch64__) +#if defined(__GNUC__) && defined(__aarch64__) /* * UGLY HACK: - * Clang generates a bunch of MOV/MOVK pairs for aarch64, and they are + * GCC and Clang generate a bunch of MOV/MOVK pairs for aarch64, and they are * placed sequentially, in order, at the top of the unrolled loop. * * While MOVK is great for generating constants (2 cycles for a 64-bit - * constant compared to 4 cycles for LDR), long MOVK chains stall the - * integer pipelines: + * constant compared to 4 cycles for LDR), it fights for bandwidth with + * the arithmetic instructions. + * * I L S * MOVK * MOVK @@ -3562,7 +5884,7 @@ XXH3_initCustomSecret_scalar(void* XXH_RESTRICT customSecret, xxh_u64 seed64) * ADD * SUB STR * STR - * By forcing loads from memory (as the asm line causes Clang to assume + * By forcing loads from memory (as the asm line causes the compiler to assume * that XXH3_kSecretPtr has been changed), the pipelines are used more * efficiently: * I L S @@ -3570,23 +5892,20 @@ XXH3_initCustomSecret_scalar(void* XXH_RESTRICT customSecret, xxh_u64 seed64) * ADD LDR * SUB STR * STR + * + * See XXH3_NEON_LANES for details on the pipsline. + * * XXH3_64bits_withSeed, len == 256, Snapdragon 835 * without hack: 2654.4 MB/s * with hack: 3202.9 MB/s */ - __asm__("" : "+r" (kSecretPtr)); + XXH_COMPILER_GUARD(kSecretPtr); #endif - /* - * Note: in debug mode, this overrides the asm optimization - * and Clang will emit MOVK chains again. - */ - XXH_ASSERT(kSecretPtr == XXH3_kSecret); - { int const nbRounds = XXH_SECRET_DEFAULT_SIZE / 16; int i; for (i=0; i < nbRounds; i++) { /* - * The asm hack causes Clang to assume that kSecretPtr aliases with + * The asm hack causes the compiler to assume that kSecretPtr aliases with * customSecret, and on aarch64, this prevented LDP from merging two * loads together for free. Putting the loads together before the stores * properly generates LDP. @@ -3599,7 +5918,7 @@ XXH3_initCustomSecret_scalar(void* XXH_RESTRICT customSecret, xxh_u64 seed64) } -typedef void (*XXH3_f_accumulate_512)(void* XXH_RESTRICT, const void*, const void*); +typedef void (*XXH3_f_accumulate)(xxh_u64* XXH_RESTRICT, const xxh_u8* XXH_RESTRICT, const xxh_u8* XXH_RESTRICT, size_t); typedef void (*XXH3_f_scrambleAcc)(void* XXH_RESTRICT, const void*); typedef void (*XXH3_f_initCustomSecret)(void* XXH_RESTRICT, xxh_u64); @@ -3607,82 +5926,69 @@ typedef void (*XXH3_f_initCustomSecret)(void* XXH_RESTRICT, xxh_u64); #if (XXH_VECTOR == XXH_AVX512) #define XXH3_accumulate_512 XXH3_accumulate_512_avx512 +#define XXH3_accumulate XXH3_accumulate_avx512 #define XXH3_scrambleAcc XXH3_scrambleAcc_avx512 #define XXH3_initCustomSecret XXH3_initCustomSecret_avx512 #elif (XXH_VECTOR == XXH_AVX2) #define XXH3_accumulate_512 XXH3_accumulate_512_avx2 +#define XXH3_accumulate XXH3_accumulate_avx2 #define XXH3_scrambleAcc XXH3_scrambleAcc_avx2 #define XXH3_initCustomSecret XXH3_initCustomSecret_avx2 #elif (XXH_VECTOR == XXH_SSE2) #define XXH3_accumulate_512 XXH3_accumulate_512_sse2 +#define XXH3_accumulate XXH3_accumulate_sse2 #define XXH3_scrambleAcc XXH3_scrambleAcc_sse2 #define XXH3_initCustomSecret XXH3_initCustomSecret_sse2 #elif (XXH_VECTOR == XXH_NEON) #define XXH3_accumulate_512 XXH3_accumulate_512_neon +#define XXH3_accumulate XXH3_accumulate_neon #define XXH3_scrambleAcc XXH3_scrambleAcc_neon #define XXH3_initCustomSecret XXH3_initCustomSecret_scalar #elif (XXH_VECTOR == XXH_VSX) #define XXH3_accumulate_512 XXH3_accumulate_512_vsx +#define XXH3_accumulate XXH3_accumulate_vsx #define XXH3_scrambleAcc XXH3_scrambleAcc_vsx #define XXH3_initCustomSecret XXH3_initCustomSecret_scalar +#elif (XXH_VECTOR == XXH_SVE) +#define XXH3_accumulate_512 XXH3_accumulate_512_sve +#define XXH3_accumulate XXH3_accumulate_sve +#define XXH3_scrambleAcc XXH3_scrambleAcc_scalar +#define XXH3_initCustomSecret XXH3_initCustomSecret_scalar + +#elif (XXH_VECTOR == XXH_LSX) +#define XXH3_accumulate_512 XXH3_accumulate_512_lsx +#define XXH3_accumulate XXH3_accumulate_lsx +#define XXH3_scrambleAcc XXH3_scrambleAcc_lsx +#define XXH3_initCustomSecret XXH3_initCustomSecret_scalar + #else /* scalar */ #define XXH3_accumulate_512 XXH3_accumulate_512_scalar +#define XXH3_accumulate XXH3_accumulate_scalar #define XXH3_scrambleAcc XXH3_scrambleAcc_scalar #define XXH3_initCustomSecret XXH3_initCustomSecret_scalar #endif - - -#ifndef XXH_PREFETCH_DIST -# ifdef __clang__ -# define XXH_PREFETCH_DIST 320 -# else -# if (XXH_VECTOR == XXH_AVX512) -# define XXH_PREFETCH_DIST 512 -# else -# define XXH_PREFETCH_DIST 384 -# endif -# endif /* __clang__ */ -#endif /* XXH_PREFETCH_DIST */ - -/* - * XXH3_accumulate() - * Loops over XXH3_accumulate_512(). - * Assumption: nbStripes will not overflow the secret size - */ -XXH_FORCE_INLINE void -XXH3_accumulate( xxh_u64* XXH_RESTRICT acc, - const xxh_u8* XXH_RESTRICT input, - const xxh_u8* XXH_RESTRICT secret, - size_t nbStripes, - XXH3_f_accumulate_512 f_acc512) -{ - size_t n; - for (n = 0; n < nbStripes; n++ ) { - const xxh_u8* const in = input + n*XXH_STRIPE_LEN; - XXH_PREFETCH(in + XXH_PREFETCH_DIST); - f_acc512(acc, - in, - secret + n*XXH_SECRET_CONSUME_RATE); - } -} +#if XXH_SIZE_OPT >= 1 /* don't do SIMD for initialization */ +# undef XXH3_initCustomSecret +# define XXH3_initCustomSecret XXH3_initCustomSecret_scalar +#endif XXH_FORCE_INLINE void XXH3_hashLong_internal_loop(xxh_u64* XXH_RESTRICT acc, const xxh_u8* XXH_RESTRICT input, size_t len, const xxh_u8* XXH_RESTRICT secret, size_t secretSize, - XXH3_f_accumulate_512 f_acc512, + XXH3_f_accumulate f_acc, XXH3_f_scrambleAcc f_scramble) { size_t const nbStripesPerBlock = (secretSize - XXH_STRIPE_LEN) / XXH_SECRET_CONSUME_RATE; @@ -3694,7 +6000,7 @@ XXH3_hashLong_internal_loop(xxh_u64* XXH_RESTRICT acc, XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); for (n = 0; n < nb_blocks; n++) { - XXH3_accumulate(acc, input + n*block_len, secret, nbStripesPerBlock, f_acc512); + f_acc(acc, input + n*block_len, secret, nbStripesPerBlock); f_scramble(acc, secret + secretSize - XXH_STRIPE_LEN); } @@ -3702,12 +6008,12 @@ XXH3_hashLong_internal_loop(xxh_u64* XXH_RESTRICT acc, XXH_ASSERT(len > XXH_STRIPE_LEN); { size_t const nbStripes = ((len - 1) - (block_len * nb_blocks)) / XXH_STRIPE_LEN; XXH_ASSERT(nbStripes <= (secretSize / XXH_SECRET_CONSUME_RATE)); - XXH3_accumulate(acc, input + nb_blocks*block_len, secret, nbStripes, f_acc512); + f_acc(acc, input + nb_blocks*block_len, secret, nbStripes); /* last stripe */ { const xxh_u8* const p = input + len - XXH_STRIPE_LEN; #define XXH_SECRET_LASTACC_START 7 /* not aligned on 8, last secret is different from acc & scrambler */ - f_acc512(acc, p, secret + secretSize - XXH_STRIPE_LEN - XXH_SECRET_LASTACC_START); + XXH3_accumulate_512(acc, p, secret + secretSize - XXH_STRIPE_LEN - XXH_SECRET_LASTACC_START); } } } @@ -3719,7 +6025,7 @@ XXH3_mix2Accs(const xxh_u64* XXH_RESTRICT acc, const xxh_u8* XXH_RESTRICT secret acc[1] ^ XXH_readLE64(secret+8) ); } -static XXH64_hash_t +static XXH_PUREF XXH64_hash_t XXH3_mergeAccs(const xxh_u64* XXH_RESTRICT acc, const xxh_u8* XXH_RESTRICT secret, xxh_u64 start) { xxh_u64 result64 = start; @@ -3739,58 +6045,68 @@ XXH3_mergeAccs(const xxh_u64* XXH_RESTRICT acc, const xxh_u8* XXH_RESTRICT secre * without hack: 2063.7 MB/s * with hack: 2560.7 MB/s */ - __asm__("" : "+r" (result64)); + XXH_COMPILER_GUARD(result64); #endif } return XXH3_avalanche(result64); } +/* do not align on 8, so that the secret is different from the accumulator */ +#define XXH_SECRET_MERGEACCS_START 11 + +static XXH_PUREF XXH64_hash_t +XXH3_finalizeLong_64b(const xxh_u64* XXH_RESTRICT acc, const xxh_u8* XXH_RESTRICT secret, xxh_u64 len) +{ + return XXH3_mergeAccs(acc, secret + XXH_SECRET_MERGEACCS_START, len * XXH_PRIME64_1); +} + #define XXH3_INIT_ACC { XXH_PRIME32_3, XXH_PRIME64_1, XXH_PRIME64_2, XXH_PRIME64_3, \ XXH_PRIME64_4, XXH_PRIME32_2, XXH_PRIME64_5, XXH_PRIME32_1 } XXH_FORCE_INLINE XXH64_hash_t XXH3_hashLong_64b_internal(const void* XXH_RESTRICT input, size_t len, const void* XXH_RESTRICT secret, size_t secretSize, - XXH3_f_accumulate_512 f_acc512, + XXH3_f_accumulate f_acc, XXH3_f_scrambleAcc f_scramble) { XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[XXH_ACC_NB] = XXH3_INIT_ACC; - XXH3_hashLong_internal_loop(acc, (const xxh_u8*)input, len, (const xxh_u8*)secret, secretSize, f_acc512, f_scramble); + XXH3_hashLong_internal_loop(acc, (const xxh_u8*)input, len, (const xxh_u8*)secret, secretSize, f_acc, f_scramble); /* converge into final hash */ XXH_STATIC_ASSERT(sizeof(acc) == 64); - /* do not align on 8, so that the secret is different from the accumulator */ -#define XXH_SECRET_MERGEACCS_START 11 XXH_ASSERT(secretSize >= sizeof(acc) + XXH_SECRET_MERGEACCS_START); - return XXH3_mergeAccs(acc, (const xxh_u8*)secret + XXH_SECRET_MERGEACCS_START, (xxh_u64)len * XXH_PRIME64_1); + return XXH3_finalizeLong_64b(acc, (const xxh_u8*)secret, (xxh_u64)len); } /* - * It's important for performance that XXH3_hashLong is not inlined. + * It's important for performance to transmit secret's size (when it's static) + * so that the compiler can properly optimize the vectorized loop. + * This makes a big performance difference for "medium" keys (<1 KB) when using AVX instruction set. + * When the secret size is unknown, or on GCC 12 where the mix of NO_INLINE and FORCE_INLINE + * breaks -Og, this is XXH_NO_INLINE. */ -XXH_NO_INLINE XXH64_hash_t +XXH3_WITH_SECRET_INLINE XXH64_hash_t XXH3_hashLong_64b_withSecret(const void* XXH_RESTRICT input, size_t len, XXH64_hash_t seed64, const xxh_u8* XXH_RESTRICT secret, size_t secretLen) { (void)seed64; - return XXH3_hashLong_64b_internal(input, len, secret, secretLen, XXH3_accumulate_512, XXH3_scrambleAcc); + return XXH3_hashLong_64b_internal(input, len, secret, secretLen, XXH3_accumulate, XXH3_scrambleAcc); } /* - * It's important for performance that XXH3_hashLong is not inlined. - * Since the function is not inlined, the compiler may not be able to understand that, - * in some scenarios, its `secret` argument is actually a compile time constant. - * This variant enforces that the compiler can detect that, - * and uses this opportunity to streamline the generated code for better performance. + * It's preferable for performance that XXH3_hashLong is not inlined, + * as it results in a smaller function for small data, easier to the instruction cache. + * Note that inside this no_inline function, we do inline the internal loop, + * and provide a statically defined secret size to allow optimization of vector loop. */ -XXH_NO_INLINE XXH64_hash_t +XXH_NO_INLINE XXH_PUREF XXH64_hash_t XXH3_hashLong_64b_default(const void* XXH_RESTRICT input, size_t len, XXH64_hash_t seed64, const xxh_u8* XXH_RESTRICT secret, size_t secretLen) { (void)seed64; (void)secret; (void)secretLen; - return XXH3_hashLong_64b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_accumulate_512, XXH3_scrambleAcc); + return XXH3_hashLong_64b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_accumulate, XXH3_scrambleAcc); } /* @@ -3807,18 +6123,20 @@ XXH3_hashLong_64b_default(const void* XXH_RESTRICT input, size_t len, XXH_FORCE_INLINE XXH64_hash_t XXH3_hashLong_64b_withSeed_internal(const void* input, size_t len, XXH64_hash_t seed, - XXH3_f_accumulate_512 f_acc512, + XXH3_f_accumulate f_acc, XXH3_f_scrambleAcc f_scramble, XXH3_f_initCustomSecret f_initSec) { +#if XXH_SIZE_OPT <= 0 if (seed == 0) return XXH3_hashLong_64b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret), - f_acc512, f_scramble); + f_acc, f_scramble); +#endif { XXH_ALIGN(XXH_SEC_ALIGN) xxh_u8 secret[XXH_SECRET_DEFAULT_SIZE]; f_initSec(secret, seed); return XXH3_hashLong_64b_internal(input, len, secret, sizeof(secret), - f_acc512, f_scramble); + f_acc, f_scramble); } } @@ -3826,12 +6144,12 @@ XXH3_hashLong_64b_withSeed_internal(const void* input, size_t len, * It's important for performance that XXH3_hashLong is not inlined. */ XXH_NO_INLINE XXH64_hash_t -XXH3_hashLong_64b_withSeed(const void* input, size_t len, - XXH64_hash_t seed, const xxh_u8* secret, size_t secretLen) +XXH3_hashLong_64b_withSeed(const void* XXH_RESTRICT input, size_t len, + XXH64_hash_t seed, const xxh_u8* XXH_RESTRICT secret, size_t secretLen) { (void)secret; (void)secretLen; return XXH3_hashLong_64b_withSeed_internal(input, len, seed, - XXH3_accumulate_512, XXH3_scrambleAcc, XXH3_initCustomSecret); + XXH3_accumulate, XXH3_scrambleAcc, XXH3_initCustomSecret); } @@ -3863,28 +6181,39 @@ XXH3_64bits_internal(const void* XXH_RESTRICT input, size_t len, /* === Public entry point === */ -XXH_PUBLIC_API XXH64_hash_t XXH3_64bits(const void* input, size_t len) +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH64_hash_t XXH3_64bits(XXH_NOESCAPE const void* input, size_t length) { - return XXH3_64bits_internal(input, len, 0, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_64b_default); + return XXH3_64bits_internal(input, length, 0, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_64b_default); } +/*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH64_hash_t -XXH3_64bits_withSecret(const void* input, size_t len, const void* secret, size_t secretSize) +XXH3_64bits_withSecret(XXH_NOESCAPE const void* input, size_t length, XXH_NOESCAPE const void* secret, size_t secretSize) { - return XXH3_64bits_internal(input, len, 0, secret, secretSize, XXH3_hashLong_64b_withSecret); + return XXH3_64bits_internal(input, length, 0, secret, secretSize, XXH3_hashLong_64b_withSecret); } +/*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH64_hash_t -XXH3_64bits_withSeed(const void* input, size_t len, XXH64_hash_t seed) +XXH3_64bits_withSeed(XXH_NOESCAPE const void* input, size_t length, XXH64_hash_t seed) { - return XXH3_64bits_internal(input, len, seed, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_64b_withSeed); + return XXH3_64bits_internal(input, length, seed, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_64b_withSeed); } +XXH_PUBLIC_API XXH64_hash_t +XXH3_64bits_withSecretandSeed(XXH_NOESCAPE const void* input, size_t length, XXH_NOESCAPE const void* secret, size_t secretSize, XXH64_hash_t seed) +{ + if (length <= XXH3_MIDSIZE_MAX) + return XXH3_64bits_internal(input, length, seed, XXH3_kSecret, sizeof(XXH3_kSecret), NULL); + return XXH3_hashLong_64b_withSecret(input, length, seed, (const xxh_u8*)secret, secretSize); +} -/* === XXH3 streaming === */ +/* === XXH3 streaming === */ +#ifndef XXH_NO_STREAM /* - * Malloc's a pointer that is always aligned to align. + * Malloc's a pointer that is always aligned to @align. * * This must be freed with `XXH_alignedFree()`. * @@ -3906,7 +6235,7 @@ XXH3_64bits_withSeed(const void* input, size_t len, XXH64_hash_t seed) * * Align must be a power of 2 and 8 <= align <= 128. */ -static void* XXH_alignedMalloc(size_t s, size_t align) +static XXH_MALLOCF void* XXH_alignedMalloc(size_t s, size_t align) { XXH_ASSERT(align <= 128 && align >= 8); /* range check */ XXH_ASSERT((align & (align-1)) == 0); /* power of 2 */ @@ -3948,6 +6277,17 @@ static void XXH_alignedFree(void* p) XXH_free(base); } } +/*! @ingroup XXH3_family */ +/*! + * @brief Allocate an @ref XXH3_state_t. + * + * @return An allocated pointer of @ref XXH3_state_t on success. + * @return `NULL` on failure. + * + * @note Must be freed with XXH3_freeState(). + * + * @see @ref streaming_example "Streaming Example" + */ XXH_PUBLIC_API XXH3_state_t* XXH3_createState(void) { XXH3_state_t* const state = (XXH3_state_t*)XXH_alignedMalloc(sizeof(XXH3_state_t), 64); @@ -3956,22 +6296,35 @@ XXH_PUBLIC_API XXH3_state_t* XXH3_createState(void) return state; } +/*! @ingroup XXH3_family */ +/*! + * @brief Frees an @ref XXH3_state_t. + * + * @param statePtr A pointer to an @ref XXH3_state_t allocated with @ref XXH3_createState(). + * + * @return @ref XXH_OK. + * + * @note Must be allocated with XXH3_createState(). + * + * @see @ref streaming_example "Streaming Example" + */ XXH_PUBLIC_API XXH_errorcode XXH3_freeState(XXH3_state_t* statePtr) { XXH_alignedFree(statePtr); return XXH_OK; } +/*! @ingroup XXH3_family */ XXH_PUBLIC_API void -XXH3_copyState(XXH3_state_t* dst_state, const XXH3_state_t* src_state) +XXH3_copyState(XXH_NOESCAPE XXH3_state_t* dst_state, XXH_NOESCAPE const XXH3_state_t* src_state) { - memcpy(dst_state, src_state, sizeof(*dst_state)); + XXH_memcpy(dst_state, src_state, sizeof(*dst_state)); } static void -XXH3_64bits_reset_internal(XXH3_state_t* statePtr, - XXH64_hash_t seed, - const void* secret, size_t secretSize) +XXH3_reset_internal(XXH3_state_t* statePtr, + XXH64_hash_t seed, + const void* secret, size_t secretSize) { size_t const initStart = offsetof(XXH3_state_t, bufferedSize); size_t const initLength = offsetof(XXH3_state_t, nbStripesPerBlock) - initStart; @@ -3988,95 +6341,153 @@ XXH3_64bits_reset_internal(XXH3_state_t* statePtr, statePtr->acc[6] = XXH_PRIME64_5; statePtr->acc[7] = XXH_PRIME32_1; statePtr->seed = seed; + statePtr->useSeed = (seed != 0); statePtr->extSecret = (const unsigned char*)secret; XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); statePtr->secretLimit = secretSize - XXH_STRIPE_LEN; statePtr->nbStripesPerBlock = statePtr->secretLimit / XXH_SECRET_CONSUME_RATE; } +/*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH_errorcode -XXH3_64bits_reset(XXH3_state_t* statePtr) +XXH3_64bits_reset(XXH_NOESCAPE XXH3_state_t* statePtr) { if (statePtr == NULL) return XXH_ERROR; - XXH3_64bits_reset_internal(statePtr, 0, XXH3_kSecret, XXH_SECRET_DEFAULT_SIZE); + XXH3_reset_internal(statePtr, 0, XXH3_kSecret, XXH_SECRET_DEFAULT_SIZE); return XXH_OK; } +/*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH_errorcode -XXH3_64bits_reset_withSecret(XXH3_state_t* statePtr, const void* secret, size_t secretSize) +XXH3_64bits_reset_withSecret(XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* secret, size_t secretSize) { if (statePtr == NULL) return XXH_ERROR; - XXH3_64bits_reset_internal(statePtr, 0, secret, secretSize); + XXH3_reset_internal(statePtr, 0, secret, secretSize); if (secret == NULL) return XXH_ERROR; if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR; return XXH_OK; } +/*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH_errorcode -XXH3_64bits_reset_withSeed(XXH3_state_t* statePtr, XXH64_hash_t seed) +XXH3_64bits_reset_withSeed(XXH_NOESCAPE XXH3_state_t* statePtr, XXH64_hash_t seed) { if (statePtr == NULL) return XXH_ERROR; if (seed==0) return XXH3_64bits_reset(statePtr); - if (seed != statePtr->seed) XXH3_initCustomSecret(statePtr->customSecret, seed); - XXH3_64bits_reset_internal(statePtr, seed, NULL, XXH_SECRET_DEFAULT_SIZE); + if ((seed != statePtr->seed) || (statePtr->extSecret != NULL)) + XXH3_initCustomSecret(statePtr->customSecret, seed); + XXH3_reset_internal(statePtr, seed, NULL, XXH_SECRET_DEFAULT_SIZE); return XXH_OK; } -/* Note : when XXH3_consumeStripes() is invoked, - * there must be a guarantee that at least one more byte must be consumed from input - * so that the function can blindly consume all stripes using the "normal" secret segment */ -XXH_FORCE_INLINE void +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH_errorcode +XXH3_64bits_reset_withSecretandSeed(XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* secret, size_t secretSize, XXH64_hash_t seed64) +{ + if (statePtr == NULL) return XXH_ERROR; + if (secret == NULL) return XXH_ERROR; + if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR; + XXH3_reset_internal(statePtr, seed64, secret, secretSize); + statePtr->useSeed = 1; /* always, even if seed64==0 */ + return XXH_OK; +} + +/*! + * @internal + * @brief Processes a large input for XXH3_update() and XXH3_digest_long(). + * + * Unlike XXH3_hashLong_internal_loop(), this can process data that overlaps a block. + * + * @param acc Pointer to the 8 accumulator lanes + * @param nbStripesSoFarPtr In/out pointer to the number of leftover stripes in the block* + * @param nbStripesPerBlock Number of stripes in a block + * @param input Input pointer + * @param nbStripes Number of stripes to process + * @param secret Secret pointer + * @param secretLimit Offset of the last block in @p secret + * @param f_acc Pointer to an XXH3_accumulate implementation + * @param f_scramble Pointer to an XXH3_scrambleAcc implementation + * @return Pointer past the end of @p input after processing + */ +XXH_FORCE_INLINE const xxh_u8 * XXH3_consumeStripes(xxh_u64* XXH_RESTRICT acc, size_t* XXH_RESTRICT nbStripesSoFarPtr, size_t nbStripesPerBlock, const xxh_u8* XXH_RESTRICT input, size_t nbStripes, const xxh_u8* XXH_RESTRICT secret, size_t secretLimit, - XXH3_f_accumulate_512 f_acc512, + XXH3_f_accumulate f_acc, XXH3_f_scrambleAcc f_scramble) { - XXH_ASSERT(nbStripes <= nbStripesPerBlock); /* can handle max 1 scramble per invocation */ - XXH_ASSERT(*nbStripesSoFarPtr < nbStripesPerBlock); - if (nbStripesPerBlock - *nbStripesSoFarPtr <= nbStripes) { - /* need a scrambling operation */ - size_t const nbStripesToEndofBlock = nbStripesPerBlock - *nbStripesSoFarPtr; - size_t const nbStripesAfterBlock = nbStripes - nbStripesToEndofBlock; - XXH3_accumulate(acc, input, secret + nbStripesSoFarPtr[0] * XXH_SECRET_CONSUME_RATE, nbStripesToEndofBlock, f_acc512); - f_scramble(acc, secret + secretLimit); - XXH3_accumulate(acc, input + nbStripesToEndofBlock * XXH_STRIPE_LEN, secret, nbStripesAfterBlock, f_acc512); - *nbStripesSoFarPtr = nbStripesAfterBlock; - } else { - XXH3_accumulate(acc, input, secret + nbStripesSoFarPtr[0] * XXH_SECRET_CONSUME_RATE, nbStripes, f_acc512); + const xxh_u8* initialSecret = secret + *nbStripesSoFarPtr * XXH_SECRET_CONSUME_RATE; + /* Process full blocks */ + if (nbStripes >= (nbStripesPerBlock - *nbStripesSoFarPtr)) { + /* Process the initial partial block... */ + size_t nbStripesThisIter = nbStripesPerBlock - *nbStripesSoFarPtr; + + do { + /* Accumulate and scramble */ + f_acc(acc, input, initialSecret, nbStripesThisIter); + f_scramble(acc, secret + secretLimit); + input += nbStripesThisIter * XXH_STRIPE_LEN; + nbStripes -= nbStripesThisIter; + /* Then continue the loop with the full block size */ + nbStripesThisIter = nbStripesPerBlock; + initialSecret = secret; + } while (nbStripes >= nbStripesPerBlock); + *nbStripesSoFarPtr = 0; + } + /* Process a partial block */ + if (nbStripes > 0) { + f_acc(acc, input, initialSecret, nbStripes); + input += nbStripes * XXH_STRIPE_LEN; *nbStripesSoFarPtr += nbStripes; } + /* Return end pointer */ + return input; } +#ifndef XXH3_STREAM_USE_STACK +# if XXH_SIZE_OPT <= 0 && !defined(__clang__) /* clang doesn't need additional stack space */ +# define XXH3_STREAM_USE_STACK 1 +# endif +#endif /* * Both XXH3_64bits_update and XXH3_128bits_update use this routine. */ XXH_FORCE_INLINE XXH_errorcode -XXH3_update(XXH3_state_t* state, - const xxh_u8* input, size_t len, - XXH3_f_accumulate_512 f_acc512, +XXH3_update(XXH3_state_t* XXH_RESTRICT const state, + const xxh_u8* XXH_RESTRICT input, size_t len, + XXH3_f_accumulate f_acc, XXH3_f_scrambleAcc f_scramble) { - if (input==NULL) -#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + if (input==NULL) { + XXH_ASSERT(len == 0); return XXH_OK; -#else - return XXH_ERROR; -#endif + } + XXH_ASSERT(state != NULL); { const xxh_u8* const bEnd = input + len; const unsigned char* const secret = (state->extSecret == NULL) ? state->customSecret : state->extSecret; - +#if defined(XXH3_STREAM_USE_STACK) && XXH3_STREAM_USE_STACK >= 1 + /* For some reason, gcc and MSVC seem to suffer greatly + * when operating accumulators directly into state. + * Operating into stack space seems to enable proper optimization. + * clang, on the other hand, doesn't seem to need this trick */ + XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[8]; + XXH_memcpy(acc, state->acc, sizeof(acc)); +#else + xxh_u64* XXH_RESTRICT const acc = state->acc; +#endif state->totalLen += len; + XXH_ASSERT(state->bufferedSize <= XXH3_INTERNALBUFFER_SIZE); - if (state->bufferedSize + len <= XXH3_INTERNALBUFFER_SIZE) { /* fill in tmp buffer */ + /* small input : just fill in tmp buffer */ + if (len <= XXH3_INTERNALBUFFER_SIZE - state->bufferedSize) { XXH_memcpy(state->buffer + state->bufferedSize, input, len); state->bufferedSize += (XXH32_hash_t)len; return XXH_OK; } - /* total input is now > XXH3_INTERNALBUFFER_SIZE */ + /* total input is now > XXH3_INTERNALBUFFER_SIZE */ #define XXH3_INTERNALBUFFER_STRIPES (XXH3_INTERNALBUFFER_SIZE / XXH_STRIPE_LEN) XXH_STATIC_ASSERT(XXH3_INTERNALBUFFER_SIZE % XXH_STRIPE_LEN == 0); /* clean multiple */ @@ -4088,44 +6499,45 @@ XXH3_update(XXH3_state_t* state, size_t const loadSize = XXH3_INTERNALBUFFER_SIZE - state->bufferedSize; XXH_memcpy(state->buffer + state->bufferedSize, input, loadSize); input += loadSize; - XXH3_consumeStripes(state->acc, + XXH3_consumeStripes(acc, &state->nbStripesSoFar, state->nbStripesPerBlock, state->buffer, XXH3_INTERNALBUFFER_STRIPES, secret, state->secretLimit, - f_acc512, f_scramble); + f_acc, f_scramble); state->bufferedSize = 0; } XXH_ASSERT(input < bEnd); + if (bEnd - input > XXH3_INTERNALBUFFER_SIZE) { + size_t nbStripes = (size_t)(bEnd - 1 - input) / XXH_STRIPE_LEN; + input = XXH3_consumeStripes(acc, + &state->nbStripesSoFar, state->nbStripesPerBlock, + input, nbStripes, + secret, state->secretLimit, + f_acc, f_scramble); + XXH_memcpy(state->buffer + sizeof(state->buffer) - XXH_STRIPE_LEN, input - XXH_STRIPE_LEN, XXH_STRIPE_LEN); - /* Consume input by a multiple of internal buffer size */ - if (input+XXH3_INTERNALBUFFER_SIZE < bEnd) { - const xxh_u8* const limit = bEnd - XXH3_INTERNALBUFFER_SIZE; - do { - XXH3_consumeStripes(state->acc, - &state->nbStripesSoFar, state->nbStripesPerBlock, - input, XXH3_INTERNALBUFFER_STRIPES, - secret, state->secretLimit, - f_acc512, f_scramble); - input += XXH3_INTERNALBUFFER_SIZE; - } while (inputbuffer + sizeof(state->buffer) - XXH_STRIPE_LEN, input - XXH_STRIPE_LEN, XXH_STRIPE_LEN); } - XXH_ASSERT(input < bEnd); - /* Some remaining input (always) : buffer it */ + XXH_ASSERT(input < bEnd); + XXH_ASSERT(bEnd - input <= XXH3_INTERNALBUFFER_SIZE); + XXH_ASSERT(state->bufferedSize == 0); XXH_memcpy(state->buffer, input, (size_t)(bEnd-input)); state->bufferedSize = (XXH32_hash_t)(bEnd-input); +#if defined(XXH3_STREAM_USE_STACK) && XXH3_STREAM_USE_STACK >= 1 + /* save stack accumulators into state */ + XXH_memcpy(state->acc, acc, sizeof(acc)); +#endif } return XXH_OK; } +/*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH_errorcode -XXH3_64bits_update(XXH3_state_t* state, const void* input, size_t len) +XXH3_64bits_update(XXH_NOESCAPE XXH3_state_t* state, XXH_NOESCAPE const void* input, size_t len) { return XXH3_update(state, (const xxh_u8*)input, len, - XXH3_accumulate_512, XXH3_scrambleAcc); + XXH3_accumulate, XXH3_scrambleAcc); } @@ -4134,96 +6546,54 @@ XXH3_digest_long (XXH64_hash_t* acc, const XXH3_state_t* state, const unsigned char* secret) { + xxh_u8 lastStripe[XXH_STRIPE_LEN]; + const xxh_u8* lastStripePtr; + /* * Digest on a local copy. This way, the state remains unaltered, and it can * continue ingesting more input afterwards. */ - memcpy(acc, state->acc, sizeof(state->acc)); + XXH_memcpy(acc, state->acc, sizeof(state->acc)); if (state->bufferedSize >= XXH_STRIPE_LEN) { + /* Consume remaining stripes then point to remaining data in buffer */ size_t const nbStripes = (state->bufferedSize - 1) / XXH_STRIPE_LEN; size_t nbStripesSoFar = state->nbStripesSoFar; XXH3_consumeStripes(acc, &nbStripesSoFar, state->nbStripesPerBlock, state->buffer, nbStripes, secret, state->secretLimit, - XXH3_accumulate_512, XXH3_scrambleAcc); - /* last stripe */ - XXH3_accumulate_512(acc, - state->buffer + state->bufferedSize - XXH_STRIPE_LEN, - secret + state->secretLimit - XXH_SECRET_LASTACC_START); + XXH3_accumulate, XXH3_scrambleAcc); + lastStripePtr = state->buffer + state->bufferedSize - XXH_STRIPE_LEN; } else { /* bufferedSize < XXH_STRIPE_LEN */ - xxh_u8 lastStripe[XXH_STRIPE_LEN]; + /* Copy to temp buffer */ size_t const catchupSize = XXH_STRIPE_LEN - state->bufferedSize; XXH_ASSERT(state->bufferedSize > 0); /* there is always some input buffered */ - memcpy(lastStripe, state->buffer + sizeof(state->buffer) - catchupSize, catchupSize); - memcpy(lastStripe + catchupSize, state->buffer, state->bufferedSize); - XXH3_accumulate_512(acc, - lastStripe, - secret + state->secretLimit - XXH_SECRET_LASTACC_START); + XXH_memcpy(lastStripe, state->buffer + sizeof(state->buffer) - catchupSize, catchupSize); + XXH_memcpy(lastStripe + catchupSize, state->buffer, state->bufferedSize); + lastStripePtr = lastStripe; } + /* Last stripe */ + XXH3_accumulate_512(acc, + lastStripePtr, + secret + state->secretLimit - XXH_SECRET_LASTACC_START); } -XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_digest (const XXH3_state_t* state) +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_digest (XXH_NOESCAPE const XXH3_state_t* state) { const unsigned char* const secret = (state->extSecret == NULL) ? state->customSecret : state->extSecret; if (state->totalLen > XXH3_MIDSIZE_MAX) { XXH_ALIGN(XXH_ACC_ALIGN) XXH64_hash_t acc[XXH_ACC_NB]; XXH3_digest_long(acc, state, secret); - return XXH3_mergeAccs(acc, - secret + XXH_SECRET_MERGEACCS_START, - (xxh_u64)state->totalLen * XXH_PRIME64_1); + return XXH3_finalizeLong_64b(acc, secret, (xxh_u64)state->totalLen); } /* totalLen <= XXH3_MIDSIZE_MAX: digesting a short input */ - if (state->seed) + if (state->useSeed) return XXH3_64bits_withSeed(state->buffer, (size_t)state->totalLen, state->seed); return XXH3_64bits_withSecret(state->buffer, (size_t)(state->totalLen), secret, state->secretLimit + XXH_STRIPE_LEN); } - - -#define XXH_MIN(x, y) (((x) > (y)) ? (y) : (x)) - -XXH_PUBLIC_API void -XXH3_generateSecret(void* secretBuffer, const void* customSeed, size_t customSeedSize) -{ - XXH_ASSERT(secretBuffer != NULL); - if (customSeedSize == 0) { - memcpy(secretBuffer, XXH3_kSecret, XXH_SECRET_DEFAULT_SIZE); - return; - } - XXH_ASSERT(customSeed != NULL); - - { size_t const segmentSize = sizeof(XXH128_hash_t); - size_t const nbSegments = XXH_SECRET_DEFAULT_SIZE / segmentSize; - XXH128_canonical_t scrambler; - XXH64_hash_t seeds[12]; - size_t segnb; - XXH_ASSERT(nbSegments == 12); - XXH_ASSERT(segmentSize * nbSegments == XXH_SECRET_DEFAULT_SIZE); /* exact multiple */ - XXH128_canonicalFromHash(&scrambler, XXH128(customSeed, customSeedSize, 0)); - - /* - * Copy customSeed to seeds[], truncating or repeating as necessary. - */ - { size_t toFill = XXH_MIN(customSeedSize, sizeof(seeds)); - size_t filled = toFill; - memcpy(seeds, customSeed, toFill); - while (filled < sizeof(seeds)) { - toFill = XXH_MIN(filled, sizeof(seeds) - filled); - memcpy((char*)seeds + filled, seeds, toFill); - filled += toFill; - } } - - /* generate secret */ - memcpy(secretBuffer, &scrambler, sizeof(scrambler)); - for (segnb=1; segnb < nbSegments; segnb++) { - size_t const segmentStart = segnb * segmentSize; - XXH128_canonical_t segment; - XXH128_canonicalFromHash(&segment, - XXH128(&scrambler, sizeof(scrambler), XXH_readLE64(seeds + segnb) + segnb) ); - memcpy((char*)secretBuffer + segmentStart, &segment, sizeof(segment)); - } } -} +#endif /* !XXH_NO_STREAM */ /* ========================================== @@ -4243,7 +6613,7 @@ XXH3_generateSecret(void* secretBuffer, const void* customSeed, size_t customSee * fast for a _128-bit_ hash on 32-bit (it usually clears XXH64). */ -XXH_FORCE_INLINE XXH128_hash_t +XXH_FORCE_INLINE XXH_PUREF XXH128_hash_t XXH3_len_1to3_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) { /* A doubled version of 1to3_64b with different constants. */ @@ -4272,7 +6642,7 @@ XXH3_len_1to3_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_ } } -XXH_FORCE_INLINE XXH128_hash_t +XXH_FORCE_INLINE XXH_PUREF XXH128_hash_t XXH3_len_4to8_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) { XXH_ASSERT(input != NULL); @@ -4292,14 +6662,14 @@ XXH3_len_4to8_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_ m128.low64 ^= (m128.high64 >> 3); m128.low64 = XXH_xorshift64(m128.low64, 35); - m128.low64 *= 0x9FB21C651E98DF25ULL; + m128.low64 *= PRIME_MX2; m128.low64 = XXH_xorshift64(m128.low64, 28); m128.high64 = XXH3_avalanche(m128.high64); return m128; } } -XXH_FORCE_INLINE XXH128_hash_t +XXH_FORCE_INLINE XXH_PUREF XXH128_hash_t XXH3_len_9to16_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) { XXH_ASSERT(input != NULL); @@ -4374,7 +6744,7 @@ XXH3_len_9to16_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64 /* * Assumption: `secret` size is >= XXH3_SECRET_SIZE_MIN */ -XXH_FORCE_INLINE XXH128_hash_t +XXH_FORCE_INLINE XXH_PUREF XXH128_hash_t XXH3_len_0to16_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) { XXH_ASSERT(len <= 16); @@ -4405,7 +6775,7 @@ XXH128_mix32B(XXH128_hash_t acc, const xxh_u8* input_1, const xxh_u8* input_2, } -XXH_FORCE_INLINE XXH128_hash_t +XXH_FORCE_INLINE XXH_PUREF XXH128_hash_t XXH3_len_17to128_128b(const xxh_u8* XXH_RESTRICT input, size_t len, const xxh_u8* XXH_RESTRICT secret, size_t secretSize, XXH64_hash_t seed) @@ -4416,6 +6786,16 @@ XXH3_len_17to128_128b(const xxh_u8* XXH_RESTRICT input, size_t len, { XXH128_hash_t acc; acc.low64 = len * XXH_PRIME64_1; acc.high64 = 0; + +#if XXH_SIZE_OPT >= 1 + { + /* Smaller, but slightly slower. */ + unsigned int i = (unsigned int)(len - 1) / 32; + do { + acc = XXH128_mix32B(acc, input+16*i, input+len-16*(i+1), secret+32*i, seed); + } while (i-- != 0); + } +#else if (len > 32) { if (len > 64) { if (len > 96) { @@ -4426,6 +6806,7 @@ XXH3_len_17to128_128b(const xxh_u8* XXH_RESTRICT input, size_t len, acc = XXH128_mix32B(acc, input+16, input+len-32, secret+32, seed); } acc = XXH128_mix32B(acc, input, input+len-16, secret, seed); +#endif { XXH128_hash_t h128; h128.low64 = acc.low64 + acc.high64; h128.high64 = (acc.low64 * XXH_PRIME64_1) @@ -4438,7 +6819,7 @@ XXH3_len_17to128_128b(const xxh_u8* XXH_RESTRICT input, size_t len, } } -XXH_NO_INLINE XXH128_hash_t +XXH_NO_INLINE XXH_PUREF XXH128_hash_t XXH3_len_129to240_128b(const xxh_u8* XXH_RESTRICT input, size_t len, const xxh_u8* XXH_RESTRICT secret, size_t secretSize, XXH64_hash_t seed) @@ -4447,25 +6828,34 @@ XXH3_len_129to240_128b(const xxh_u8* XXH_RESTRICT input, size_t len, XXH_ASSERT(128 < len && len <= XXH3_MIDSIZE_MAX); { XXH128_hash_t acc; - int const nbRounds = (int)len / 32; - int i; + unsigned i; acc.low64 = len * XXH_PRIME64_1; acc.high64 = 0; - for (i=0; i<4; i++) { + /* + * We set as `i` as offset + 32. We do this so that unchanged + * `len` can be used as upper bound. This reaches a sweet spot + * where both x86 and aarch64 get simple agen and good codegen + * for the loop. + */ + for (i = 32; i < 160; i += 32) { acc = XXH128_mix32B(acc, - input + (32 * i), - input + (32 * i) + 16, - secret + (32 * i), + input + i - 32, + input + i - 16, + secret + i - 32, seed); } acc.low64 = XXH3_avalanche(acc.low64); acc.high64 = XXH3_avalanche(acc.high64); - XXH_ASSERT(nbRounds >= 4); - for (i=4 ; i < nbRounds; i++) { + /* + * NB: `i <= len` will duplicate the last 32-bytes if + * len % 32 was zero. This is an unfortunate necessity to keep + * the hash result stable. + */ + for (i=160; i <= len; i += 32) { acc = XXH128_mix32B(acc, - input + (32 * i), - input + (32 * i) + 16, - secret + XXH3_MIDSIZE_STARTOFFSET + (32 * (i - 4)), + input + i - 32, + input + i - 16, + secret + XXH3_MIDSIZE_STARTOFFSET + i - 160, seed); } /* last bytes */ @@ -4473,7 +6863,7 @@ XXH3_len_129to240_128b(const xxh_u8* XXH_RESTRICT input, size_t len, input + len - 16, input + len - 32, secret + XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET - 16, - 0ULL - seed); + (XXH64_hash_t)0 - seed); { XXH128_hash_t h128; h128.low64 = acc.low64 + acc.high64; @@ -4487,72 +6877,78 @@ XXH3_len_129to240_128b(const xxh_u8* XXH_RESTRICT input, size_t len, } } +static XXH_PUREF XXH128_hash_t +XXH3_finalizeLong_128b(const xxh_u64* XXH_RESTRICT acc, const xxh_u8* XXH_RESTRICT secret, size_t secretSize, xxh_u64 len) +{ + XXH128_hash_t h128; + h128.low64 = XXH3_finalizeLong_64b(acc, secret, len); + h128.high64 = XXH3_mergeAccs(acc, secret + secretSize + - XXH_STRIPE_LEN - XXH_SECRET_MERGEACCS_START, + ~(len * XXH_PRIME64_2)); + return h128; +} + XXH_FORCE_INLINE XXH128_hash_t XXH3_hashLong_128b_internal(const void* XXH_RESTRICT input, size_t len, const xxh_u8* XXH_RESTRICT secret, size_t secretSize, - XXH3_f_accumulate_512 f_acc512, + XXH3_f_accumulate f_acc, XXH3_f_scrambleAcc f_scramble) { XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[XXH_ACC_NB] = XXH3_INIT_ACC; - XXH3_hashLong_internal_loop(acc, (const xxh_u8*)input, len, secret, secretSize, f_acc512, f_scramble); + XXH3_hashLong_internal_loop(acc, (const xxh_u8*)input, len, secret, secretSize, f_acc, f_scramble); /* converge into final hash */ XXH_STATIC_ASSERT(sizeof(acc) == 64); XXH_ASSERT(secretSize >= sizeof(acc) + XXH_SECRET_MERGEACCS_START); - { XXH128_hash_t h128; - h128.low64 = XXH3_mergeAccs(acc, - secret + XXH_SECRET_MERGEACCS_START, - (xxh_u64)len * XXH_PRIME64_1); - h128.high64 = XXH3_mergeAccs(acc, - secret + secretSize - - sizeof(acc) - XXH_SECRET_MERGEACCS_START, - ~((xxh_u64)len * XXH_PRIME64_2)); - return h128; - } + return XXH3_finalizeLong_128b(acc, secret, secretSize, (xxh_u64)len); } /* - * It's important for performance that XXH3_hashLong is not inlined. + * It's important for performance that XXH3_hashLong() is not inlined. */ -XXH_NO_INLINE XXH128_hash_t +XXH_NO_INLINE XXH_PUREF XXH128_hash_t XXH3_hashLong_128b_default(const void* XXH_RESTRICT input, size_t len, XXH64_hash_t seed64, const void* XXH_RESTRICT secret, size_t secretLen) { (void)seed64; (void)secret; (void)secretLen; return XXH3_hashLong_128b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret), - XXH3_accumulate_512, XXH3_scrambleAcc); + XXH3_accumulate, XXH3_scrambleAcc); } /* - * It's important for performance that XXH3_hashLong is not inlined. + * It's important for performance to pass @p secretLen (when it's static) + * to the compiler, so that it can properly optimize the vectorized loop. + * + * When the secret size is unknown, or on GCC 12 where the mix of NO_INLINE and FORCE_INLINE + * breaks -Og, this is XXH_NO_INLINE. */ -XXH_NO_INLINE XXH128_hash_t +XXH3_WITH_SECRET_INLINE XXH128_hash_t XXH3_hashLong_128b_withSecret(const void* XXH_RESTRICT input, size_t len, XXH64_hash_t seed64, const void* XXH_RESTRICT secret, size_t secretLen) { (void)seed64; return XXH3_hashLong_128b_internal(input, len, (const xxh_u8*)secret, secretLen, - XXH3_accumulate_512, XXH3_scrambleAcc); + XXH3_accumulate, XXH3_scrambleAcc); } XXH_FORCE_INLINE XXH128_hash_t XXH3_hashLong_128b_withSeed_internal(const void* XXH_RESTRICT input, size_t len, XXH64_hash_t seed64, - XXH3_f_accumulate_512 f_acc512, + XXH3_f_accumulate f_acc, XXH3_f_scrambleAcc f_scramble, XXH3_f_initCustomSecret f_initSec) { if (seed64 == 0) return XXH3_hashLong_128b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret), - f_acc512, f_scramble); + f_acc, f_scramble); { XXH_ALIGN(XXH_SEC_ALIGN) xxh_u8 secret[XXH_SECRET_DEFAULT_SIZE]; f_initSec(secret, seed64); return XXH3_hashLong_128b_internal(input, len, (const xxh_u8*)secret, sizeof(secret), - f_acc512, f_scramble); + f_acc, f_scramble); } } @@ -4565,7 +6961,7 @@ XXH3_hashLong_128b_withSeed(const void* input, size_t len, { (void)secret; (void)secretLen; return XXH3_hashLong_128b_withSeed_internal(input, len, seed64, - XXH3_accumulate_512, XXH3_scrambleAcc, XXH3_initCustomSecret); + XXH3_accumulate, XXH3_scrambleAcc, XXH3_initCustomSecret); } typedef XXH128_hash_t (*XXH3_hashLong128_f)(const void* XXH_RESTRICT, size_t, @@ -4595,116 +6991,114 @@ XXH3_128bits_internal(const void* input, size_t len, /* === Public XXH128 API === */ -XXH_PUBLIC_API XXH128_hash_t XXH3_128bits(const void* input, size_t len) +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH128_hash_t XXH3_128bits(XXH_NOESCAPE const void* input, size_t len) { return XXH3_128bits_internal(input, len, 0, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_128b_default); } +/*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH128_hash_t -XXH3_128bits_withSecret(const void* input, size_t len, const void* secret, size_t secretSize) +XXH3_128bits_withSecret(XXH_NOESCAPE const void* input, size_t len, XXH_NOESCAPE const void* secret, size_t secretSize) { return XXH3_128bits_internal(input, len, 0, (const xxh_u8*)secret, secretSize, XXH3_hashLong_128b_withSecret); } +/*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH128_hash_t -XXH3_128bits_withSeed(const void* input, size_t len, XXH64_hash_t seed) +XXH3_128bits_withSeed(XXH_NOESCAPE const void* input, size_t len, XXH64_hash_t seed) { return XXH3_128bits_internal(input, len, seed, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_128b_withSeed); } +/*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH128_hash_t -XXH128(const void* input, size_t len, XXH64_hash_t seed) +XXH3_128bits_withSecretandSeed(XXH_NOESCAPE const void* input, size_t len, XXH_NOESCAPE const void* secret, size_t secretSize, XXH64_hash_t seed) +{ + if (len <= XXH3_MIDSIZE_MAX) + return XXH3_128bits_internal(input, len, seed, XXH3_kSecret, sizeof(XXH3_kSecret), NULL); + return XXH3_hashLong_128b_withSecret(input, len, seed, secret, secretSize); +} + +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH128_hash_t +XXH128(XXH_NOESCAPE const void* input, size_t len, XXH64_hash_t seed) { return XXH3_128bits_withSeed(input, len, seed); } /* === XXH3 128-bit streaming === */ - +#ifndef XXH_NO_STREAM /* - * All the functions are actually the same as for 64-bit streaming variant. - * The only difference is the finalizatiom routine. + * All initialization and update functions are identical to 64-bit streaming variant. + * The only difference is the finalization routine. */ -static void -XXH3_128bits_reset_internal(XXH3_state_t* statePtr, - XXH64_hash_t seed, - const void* secret, size_t secretSize) +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH_errorcode +XXH3_128bits_reset(XXH_NOESCAPE XXH3_state_t* statePtr) { - XXH3_64bits_reset_internal(statePtr, seed, secret, secretSize); + return XXH3_64bits_reset(statePtr); } +/*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH_errorcode -XXH3_128bits_reset(XXH3_state_t* statePtr) +XXH3_128bits_reset_withSecret(XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* secret, size_t secretSize) { - if (statePtr == NULL) return XXH_ERROR; - XXH3_128bits_reset_internal(statePtr, 0, XXH3_kSecret, XXH_SECRET_DEFAULT_SIZE); - return XXH_OK; + return XXH3_64bits_reset_withSecret(statePtr, secret, secretSize); } +/*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH_errorcode -XXH3_128bits_reset_withSecret(XXH3_state_t* statePtr, const void* secret, size_t secretSize) +XXH3_128bits_reset_withSeed(XXH_NOESCAPE XXH3_state_t* statePtr, XXH64_hash_t seed) { - if (statePtr == NULL) return XXH_ERROR; - XXH3_128bits_reset_internal(statePtr, 0, secret, secretSize); - if (secret == NULL) return XXH_ERROR; - if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR; - return XXH_OK; + return XXH3_64bits_reset_withSeed(statePtr, seed); } +/*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH_errorcode -XXH3_128bits_reset_withSeed(XXH3_state_t* statePtr, XXH64_hash_t seed) +XXH3_128bits_reset_withSecretandSeed(XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* secret, size_t secretSize, XXH64_hash_t seed) { - if (statePtr == NULL) return XXH_ERROR; - if (seed==0) return XXH3_128bits_reset(statePtr); - if (seed != statePtr->seed) XXH3_initCustomSecret(statePtr->customSecret, seed); - XXH3_128bits_reset_internal(statePtr, seed, NULL, XXH_SECRET_DEFAULT_SIZE); - return XXH_OK; + return XXH3_64bits_reset_withSecretandSeed(statePtr, secret, secretSize, seed); } +/*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH_errorcode -XXH3_128bits_update(XXH3_state_t* state, const void* input, size_t len) +XXH3_128bits_update(XXH_NOESCAPE XXH3_state_t* state, XXH_NOESCAPE const void* input, size_t len) { - return XXH3_update(state, (const xxh_u8*)input, len, - XXH3_accumulate_512, XXH3_scrambleAcc); + return XXH3_64bits_update(state, input, len); } -XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_digest (const XXH3_state_t* state) +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_digest (XXH_NOESCAPE const XXH3_state_t* state) { const unsigned char* const secret = (state->extSecret == NULL) ? state->customSecret : state->extSecret; if (state->totalLen > XXH3_MIDSIZE_MAX) { XXH_ALIGN(XXH_ACC_ALIGN) XXH64_hash_t acc[XXH_ACC_NB]; XXH3_digest_long(acc, state, secret); XXH_ASSERT(state->secretLimit + XXH_STRIPE_LEN >= sizeof(acc) + XXH_SECRET_MERGEACCS_START); - { XXH128_hash_t h128; - h128.low64 = XXH3_mergeAccs(acc, - secret + XXH_SECRET_MERGEACCS_START, - (xxh_u64)state->totalLen * XXH_PRIME64_1); - h128.high64 = XXH3_mergeAccs(acc, - secret + state->secretLimit + XXH_STRIPE_LEN - - sizeof(acc) - XXH_SECRET_MERGEACCS_START, - ~((xxh_u64)state->totalLen * XXH_PRIME64_2)); - return h128; - } + return XXH3_finalizeLong_128b(acc, secret, state->secretLimit + XXH_STRIPE_LEN, (xxh_u64)state->totalLen); } /* len <= XXH3_MIDSIZE_MAX : short code */ - if (state->seed) + if (state->useSeed) return XXH3_128bits_withSeed(state->buffer, (size_t)state->totalLen, state->seed); return XXH3_128bits_withSecret(state->buffer, (size_t)(state->totalLen), secret, state->secretLimit + XXH_STRIPE_LEN); } - +#endif /* !XXH_NO_STREAM */ /* 128-bit utility functions */ #include /* memcmp, memcpy */ /* return : 1 is equal, 0 if different */ +/*! @ingroup XXH3_family */ XXH_PUBLIC_API int XXH128_isEqual(XXH128_hash_t h1, XXH128_hash_t h2) { /* note : XXH128_hash_t is compact, it has no padding byte */ @@ -4712,10 +7106,11 @@ XXH_PUBLIC_API int XXH128_isEqual(XXH128_hash_t h1, XXH128_hash_t h2) } /* This prototype is compatible with stdlib's qsort(). - * return : >0 if *h128_1 > *h128_2 - * <0 if *h128_1 < *h128_2 - * =0 if *h128_1 == *h128_2 */ -XXH_PUBLIC_API int XXH128_cmp(const void* h128_1, const void* h128_2) + * @return : >0 if *h128_1 > *h128_2 + * <0 if *h128_1 < *h128_2 + * =0 if *h128_1 == *h128_2 */ +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API int XXH128_cmp(XXH_NOESCAPE const void* h128_1, XXH_NOESCAPE const void* h128_2) { XXH128_hash_t const h1 = *(const XXH128_hash_t*)h128_1; XXH128_hash_t const h2 = *(const XXH128_hash_t*)h128_2; @@ -4727,20 +7122,22 @@ XXH_PUBLIC_API int XXH128_cmp(const void* h128_1, const void* h128_2) /*====== Canonical representation ======*/ +/*! @ingroup XXH3_family */ XXH_PUBLIC_API void -XXH128_canonicalFromHash(XXH128_canonical_t* dst, XXH128_hash_t hash) +XXH128_canonicalFromHash(XXH_NOESCAPE XXH128_canonical_t* dst, XXH128_hash_t hash) { XXH_STATIC_ASSERT(sizeof(XXH128_canonical_t) == sizeof(XXH128_hash_t)); if (XXH_CPU_LITTLE_ENDIAN) { hash.high64 = XXH_swap64(hash.high64); hash.low64 = XXH_swap64(hash.low64); } - memcpy(dst, &hash.high64, sizeof(hash.high64)); - memcpy((char*)dst + sizeof(hash.high64), &hash.low64, sizeof(hash.low64)); + XXH_memcpy(dst, &hash.high64, sizeof(hash.high64)); + XXH_memcpy((char*)dst + sizeof(hash.high64), &hash.low64, sizeof(hash.low64)); } +/*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH128_hash_t -XXH128_hashFromCanonical(const XXH128_canonical_t* src) +XXH128_hashFromCanonical(XXH_NOESCAPE const XXH128_canonical_t* src) { XXH128_hash_t h; h.high64 = XXH_readBE64(src); @@ -4748,19 +7145,94 @@ XXH128_hashFromCanonical(const XXH128_canonical_t* src) return h; } + + +/* ========================================== + * Secret generators + * ========================================== + */ +#define XXH_MIN(x, y) (((x) > (y)) ? (y) : (x)) + +XXH_FORCE_INLINE void XXH3_combine16(void* dst, XXH128_hash_t h128) +{ + XXH_writeLE64( dst, XXH_readLE64(dst) ^ h128.low64 ); + XXH_writeLE64( (char*)dst+8, XXH_readLE64((char*)dst+8) ^ h128.high64 ); +} + +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH_errorcode +XXH3_generateSecret(XXH_NOESCAPE void* secretBuffer, size_t secretSize, XXH_NOESCAPE const void* customSeed, size_t customSeedSize) +{ +#if (XXH_DEBUGLEVEL >= 1) + XXH_ASSERT(secretBuffer != NULL); + XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); +#else + /* production mode, assert() are disabled */ + if (secretBuffer == NULL) return XXH_ERROR; + if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR; +#endif + + if (customSeedSize == 0) { + customSeed = XXH3_kSecret; + customSeedSize = XXH_SECRET_DEFAULT_SIZE; + } +#if (XXH_DEBUGLEVEL >= 1) + XXH_ASSERT(customSeed != NULL); +#else + if (customSeed == NULL) return XXH_ERROR; +#endif + + /* Fill secretBuffer with a copy of customSeed - repeat as needed */ + { size_t pos = 0; + while (pos < secretSize) { + size_t const toCopy = XXH_MIN((secretSize - pos), customSeedSize); + memcpy((char*)secretBuffer + pos, customSeed, toCopy); + pos += toCopy; + } } + + { size_t const nbSeg16 = secretSize / 16; + size_t n; + XXH128_canonical_t scrambler; + XXH128_canonicalFromHash(&scrambler, XXH128(customSeed, customSeedSize, 0)); + for (n=0; n Date: Tue, 21 Jan 2025 12:21:55 +0100 Subject: [PATCH 28/58] Update GLideN64 INI --- custom/GLideN64/GLideN64.custom.ini.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom/GLideN64/GLideN64.custom.ini.h b/custom/GLideN64/GLideN64.custom.ini.h index 1af023bf..bc377cff 100644 --- a/custom/GLideN64/GLideN64.custom.ini.h +++ b/custom/GLideN64/GLideN64.custom.ini.h @@ -22,7 +22,7 @@ char customini[] = "Good_Name=Bio F.R.E.A.K.S. (E)(U)\n" "frameBufferEmulation\\copyToRDRAM=1\n" "\n" -"[BioHazard%20II]\n" +"[BIOHAZARD%20II]\n" "Good_Name=Biohazard 2 (J)\n" "frameBufferEmulation\\copyFromRDRAM=1\n" "frameBufferEmulation\\copyToRDRAM=0\n" From 1d9ce200a2aed0f7f45aad559b3ec9b8765ca599 Mon Sep 17 00:00:00 2001 From: Bobby Smith <33353403+bslenul@users.noreply.github.com> Date: Fri, 4 Oct 2024 13:37:27 +0200 Subject: [PATCH 29/58] Parse "&" properly from GLideN64.custom.ini --- custom/GLideN64/mupenplus/Config_mupenplus.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/custom/GLideN64/mupenplus/Config_mupenplus.cpp b/custom/GLideN64/mupenplus/Config_mupenplus.cpp index 4649e224..3a6ce8a5 100644 --- a/custom/GLideN64/mupenplus/Config_mupenplus.cpp +++ b/custom/GLideN64/mupenplus/Config_mupenplus.cpp @@ -29,6 +29,10 @@ std::string replaceChars(std::string myString) { myString.replace(pos, 1, "%27"); } + for (size_t pos = myString.find('&'); pos != std::string::npos; pos = myString.find('&', pos)) + { + myString.replace(pos, 1, "%26"); + } return myString; } From 3a676196500545b637b83cb19fb393d2359e1f9d Mon Sep 17 00:00:00 2001 From: Bobby Smith <33353403+bslenul@users.noreply.github.com> Date: Tue, 21 Jan 2025 17:18:24 +0100 Subject: [PATCH 30/58] Add "EnableCopyColorFromRDRAM" core option --- custom/GLideN64/mupenplus/Config_mupenplus.cpp | 1 + custom/mupen64plus-next_common.h | 1 + libretro/libretro.c | 8 ++++++++ libretro/libretro_core_options.h | 14 ++++++++++++++ 4 files changed, 24 insertions(+) diff --git a/custom/GLideN64/mupenplus/Config_mupenplus.cpp b/custom/GLideN64/mupenplus/Config_mupenplus.cpp index 3a6ce8a5..fb51f345 100644 --- a/custom/GLideN64/mupenplus/Config_mupenplus.cpp +++ b/custom/GLideN64/mupenplus/Config_mupenplus.cpp @@ -168,6 +168,7 @@ extern "C" void Config_LoadConfig() config.frameBufferEmulation.copyDepthToRDRAM = EnableCopyDepthToRDRAM; config.frameBufferEmulation.copyToRDRAM = EnableCopyColorToRDRAM; + config.frameBufferEmulation.copyFromRDRAM = EnableCopyColorFromRDRAM; // TODO: Make this a Core options or maybe only default to bsOnVerticalInterrupt on Android with Thr Renderer config.frameBufferEmulation.bufferSwapMode = Config::bsOnVerticalInterrupt; diff --git a/custom/mupen64plus-next_common.h b/custom/mupen64plus-next_common.h index 01e968f5..3ff303fe 100644 --- a/custom/mupen64plus-next_common.h +++ b/custom/mupen64plus-next_common.h @@ -99,6 +99,7 @@ extern uint32_t EnableInaccurateTextureCoordinates; extern uint32_t enableNativeResTexrects; extern uint32_t enableLegacyBlending; extern uint32_t EnableCopyColorToRDRAM; +extern uint32_t EnableCopyColorFromRDRAM; extern uint32_t EnableCopyDepthToRDRAM; extern uint32_t AspectRatio; extern uint32_t MaxTxCacheSize; diff --git a/libretro/libretro.c b/libretro/libretro.c index e960b564..a0a9f041 100644 --- a/libretro/libretro.c +++ b/libretro/libretro.c @@ -175,6 +175,7 @@ uint32_t EnableInaccurateTextureCoordinates = 0; uint32_t enableNativeResTexrects = 0; uint32_t enableLegacyBlending = 0; uint32_t EnableCopyColorToRDRAM = 0; +uint32_t EnableCopyColorFromRDRAM = 0; uint32_t EnableCopyDepthToRDRAM = 0; uint32_t AspectRatio = 0; uint32_t MaxTxCacheSize = 0; @@ -1061,6 +1062,13 @@ static void update_variables(bool startup) EnableCopyColorToRDRAM = 0; } + var.key = CORE_NAME "-EnableCopyColorFromRDRAM"; + var.value = NULL; + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) + { + EnableCopyColorFromRDRAM = !strcmp(var.value, "False") ? 0 : 1; + } + var.key = CORE_NAME "-EnableCopyDepthToRDRAM"; var.value = NULL; if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) diff --git a/libretro/libretro_core_options.h b/libretro/libretro_core_options.h index b1805dbe..39a93ec3 100644 --- a/libretro/libretro_core_options.h +++ b/libretro/libretro_core_options.h @@ -359,6 +359,20 @@ struct retro_core_option_v2_definition option_defs_us[] = { "Sync" #endif // HAVE_OPENGLES2 }, + { + CORE_NAME "-EnableCopyColorFromRDRAM", + "Enable color buffer copy from RDRAM", + NULL, + NULL, + NULL, + "gliden64", + { + {"False", NULL}, + {"True", NULL}, + { NULL, NULL }, + }, + "False", + }, { CORE_NAME "-EnableCopyDepthToRDRAM", "Depth buffer to RDRAM", From 1b124af1eb05a6198acb8533b881d27c1c1bb6a1 Mon Sep 17 00:00:00 2001 From: M4xw Date: Wed, 26 Feb 2025 15:44:17 +0100 Subject: [PATCH 31/58] git subrepo push mupen64plus-core subrepo: subdir: "mupen64plus-core" merged: "06fde7f12" upstream: origin: "https://github.com/libretro/mupen64plus-core.git" branch: "master" commit: "06fde7f12" git-subrepo: version: "0.4.6" origin: "???" commit: "???" --- mupen64plus-core/.gitrepo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mupen64plus-core/.gitrepo b/mupen64plus-core/.gitrepo index 8901cdf7..ff512809 100644 --- a/mupen64plus-core/.gitrepo +++ b/mupen64plus-core/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/libretro/mupen64plus-core.git branch = master - commit = a362165c0af96cce7780e3d277dd85a18a3eae8e - parent = ddbdbb5056f36029bf1c165d97c027d318b996d9 + commit = 06fde7f121e1a1e20361667e0d68d9506559e22e + parent = 3a676196500545b637b83cb19fb393d2359e1f9d method = rebase cmdver = 0.4.6 From 8bbcd26454ff0367ca05cdafbb1f8858931a264f Mon Sep 17 00:00:00 2001 From: M4xw Date: Wed, 26 Feb 2025 15:58:17 +0100 Subject: [PATCH 32/58] git subrepo clone --force ../mupen64plus-core mupen64plus-core/ subrepo: subdir: "mupen64plus-core" merged: "ef94b0dc7" upstream: origin: "../mupen64plus-core" branch: "master" commit: "ef94b0dc7" git-subrepo: version: "0.4.6" origin: "???" commit: "???" --- mupen64plus-core/.gitrepo | 4 +-- mupen64plus-core/README | 2 +- mupen64plus-core/README.md | 2 +- mupen64plus-core/projects/unix/Makefile | 30 ++++++---------- mupen64plus-core/src/api/vidext.c | 36 +++---------------- mupen64plus-core/src/device/dd/disk.c | 2 +- mupen64plus-core/src/device/gb/gb_cart.c | 2 +- mupen64plus-core/src/device/r4300/cp1.c | 2 +- mupen64plus-core/src/main/eventloop.c | 46 ------------------------ mupen64plus-core/src/main/netplay.c | 5 +-- mupen64plus-core/src/main/workqueue.c | 5 +-- mupen64plus-core/src/osd/osd.c | 2 -- 12 files changed, 24 insertions(+), 114 deletions(-) mode change 100755 => 100644 mupen64plus-core/projects/unix/Makefile diff --git a/mupen64plus-core/.gitrepo b/mupen64plus-core/.gitrepo index ff512809..bda2635a 100644 --- a/mupen64plus-core/.gitrepo +++ b/mupen64plus-core/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/libretro/mupen64plus-core.git branch = master - commit = 06fde7f121e1a1e20361667e0d68d9506559e22e - parent = 3a676196500545b637b83cb19fb393d2359e1f9d + commit = ef94b0dc740e993a58783052bdcda64d97737d67 + parent = 1b124af1eb05a6198acb8533b881d27c1c1bb6a1 method = rebase cmdver = 0.4.6 diff --git a/mupen64plus-core/README b/mupen64plus-core/README index c346bff9..10185019 100644 --- a/mupen64plus-core/README +++ b/mupen64plus-core/README @@ -22,7 +22,7 @@ README Sections *Binary Package Requirements* - - SDL 1.2 or 2.0 + - SDL 2 - libpng - freetype 2 - zlib diff --git a/mupen64plus-core/README.md b/mupen64plus-core/README.md index c7c5fc64..53e66a37 100644 --- a/mupen64plus-core/README.md +++ b/mupen64plus-core/README.md @@ -27,7 +27,7 @@ as well as a user interface program (called a front-end). **Binary Package Requirements** - - SDL 1.2 or 2.0 + - SDL 2 - libpng - freetype 2 - zlib diff --git a/mupen64plus-core/projects/unix/Makefile b/mupen64plus-core/projects/unix/Makefile old mode 100755 new mode 100644 index 962bc10a..2be48280 --- a/mupen64plus-core/projects/unix/Makefile +++ b/mupen64plus-core/projects/unix/Makefile @@ -159,7 +159,7 @@ SUBDIR = ../../subprojects # base CFLAGS, LDLIBS, and LDFLAGS OPTFLAGS ?= -O3 -flto WARNFLAGS ?= -Wall -CFLAGS += -fno-strict-aliasing -fvisibility=hidden -I$(SRCDIR) -I$(SRCDIR)/asm_defines -DM64P_PARALLEL +CFLAGS += -fvisibility=hidden -I$(SRCDIR) -I$(SRCDIR)/asm_defines -DM64P_PARALLEL CXXFLAGS += -fvisibility-inlines-hidden LDLIBS += -lm @@ -351,26 +351,18 @@ endif # test for presence of SDL ifeq ($(origin SDL_CFLAGS) $(origin SDL_LDLIBS), undefined undefined) - SDL_CONFIG = $(CROSS_COMPILE)sdl2-config - ifeq ($(shell which $(SDL_CONFIG) 2>/dev/null),) - SDL_CONFIG = $(CROSS_COMPILE)sdl-config - ifeq ($(shell which $(SDL_CONFIG) 2>/dev/null),) - $(error No SDL development libraries found!) - else - ifeq ($(NETPLAY), 1) - SDL_LDLIBS += -lSDL_net - endif - # SDL1 doesn't support vulkan - VULKAN = 0 - $(warning Using SDL 1.2 libraries) - endif - else - ifeq ($(NETPLAY), 1) - SDL_LDLIBS += -lSDL2_net + ifeq ($(shell $(PKG_CONFIG) --modversion sdl2 2>/dev/null),) + $(error No SDL2 development libraries found!) + endif + ifeq ($(NETPLAY), 1) + ifeq ($(shell $(PKG_CONFIG) --modversion SDL2_net 2>/dev/null),) + $(error No SDL2_net development libraries found!) endif + SDL_CFLAGS += $(shell $(PKG_CONFIG) --cflags SDL2_net) + SDL_LDLIBS += $(shell $(PKG_CONFIG) --libs SDL2_net) endif - SDL_CFLAGS += $(shell $(SDL_CONFIG) --cflags) - SDL_LDLIBS += $(shell $(SDL_CONFIG) --libs) + SDL_CFLAGS += $(shell $(PKG_CONFIG) --cflags sdl2) + SDL_LDLIBS += $(shell $(PKG_CONFIG) --libs sdl2) endif CFLAGS += $(SDL_CFLAGS) LDLIBS += $(SDL_LDLIBS) diff --git a/mupen64plus-core/src/api/vidext.c b/mupen64plus-core/src/api/vidext.c index 1bc4a331..07e08297 100644 --- a/mupen64plus-core/src/api/vidext.c +++ b/mupen64plus-core/src/api/vidext.c @@ -34,13 +34,12 @@ #include "m64p_vidext.h" #include "vidext.h" -#if SDL_VERSION_ATLEAST(2,0,0) - #ifndef USE_GLES - static int l_ForceCompatibilityContext = 1; - #endif -#include "vidext_sdl2_compat.h" +#ifndef USE_GLES +static int l_ForceCompatibilityContext = 1; #endif +#include "vidext_sdl2_compat.h" + /* local variables */ static m64p_video_extension_functions l_ExternalVideoFuncTable = {17, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; static int l_VideoExtensionActive = 0; @@ -125,13 +124,11 @@ EXPORT m64p_error CALL VidExt_InitWithRenderMode(m64p_render_mode RenderMode) l_RenderMode = RenderMode; #endif -#if SDL_VERSION_ATLEAST(2,0,0) SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); /* retrieve default swap interval/VSync */ if (RenderMode == M64P_RENDER_OPENGL) { l_SwapControl = SDL_GL_GetSwapInterval(); } -#endif #if SDL_VERSION_ATLEAST(2,24,0) /* fix DPI scaling issues on Windows */ @@ -179,9 +176,7 @@ EXPORT m64p_error CALL VidExt_Quit(void) return M64ERR_NOT_INIT; SDL_ShowCursor(SDL_ENABLE); -#if SDL_VERSION_ATLEAST(2,0,0) SDL2_DestroyWindow(); -#endif #ifdef VIDEXT_VULKAN if (l_RenderMode == M64P_RENDER_VULKAN) { SDL_Vulkan_UnloadLibrary(); @@ -255,7 +250,6 @@ EXPORT m64p_error CALL VidExt_ListFullscreenRates(m64p_2d_size Size, int *NumRat if (l_VideoExtensionActive) return (*l_ExternalVideoFuncTable.VidExtFuncListRates)(Size, NumRates, Rates); -#if SDL_VERSION_ATLEAST(2,0,0) if (!SDL_WasInit(SDL_INIT_VIDEO)) return M64ERR_NOT_INIT; @@ -290,10 +284,6 @@ EXPORT m64p_error CALL VidExt_ListFullscreenRates(m64p_2d_size Size, int *NumRat *NumRates = rateCount; return M64ERR_SUCCESS; -#else - // SDL1 doesn't support getting refresh rates - return M64ERR_UNSUPPORTED; -#endif } EXPORT m64p_error CALL VidExt_SetVideoMode(int Width, int Height, int BitsPerPixel, m64p_video_mode ScreenMode, m64p_video_flags Flags) @@ -368,14 +358,12 @@ EXPORT m64p_error CALL VidExt_SetVideoMode(int Width, int Height, int BitsPerPix SDL_ShowCursor(SDL_DISABLE); -#if SDL_VERSION_ATLEAST(2,0,0) /* set swap interval/VSync */ if (l_RenderMode == M64P_RENDER_OPENGL && SDL_GL_SetSwapInterval(l_SwapControl) != 0) { DebugMessage(M64MSG_ERROR, "SDL swap interval (VSync) set failed: %s", SDL_GetError()); } -#endif l_Fullscreen = (ScreenMode == M64VIDEO_FULLSCREEN); l_VideoOutputActive = 1; @@ -400,7 +388,6 @@ EXPORT m64p_error CALL VidExt_SetVideoModeWithRate(int Width, int Height, int Re return rval; } -#if SDL_VERSION_ATLEAST(2,0,0) if (!SDL_WasInit(SDL_INIT_VIDEO) || !SDL_VideoWindow) return M64ERR_NOT_INIT; @@ -477,10 +464,6 @@ EXPORT m64p_error CALL VidExt_SetVideoModeWithRate(int Width, int Height, int Re StateChanged(M64CORE_VIDEO_SIZE, (Width << 16) | Height); return M64ERR_SUCCESS; -#else - // SDL1 doesn't support setting refresh rates - return M64ERR_UNSUPPORTED; -#endif } EXPORT m64p_error CALL VidExt_ResizeWindow(int Width, int Height) @@ -634,16 +617,11 @@ static const GLAttrMapNode GLAttrMap[] = { { M64P_GL_GREEN_SIZE, SDL_GL_GREEN_SIZE }, { M64P_GL_BLUE_SIZE, SDL_GL_BLUE_SIZE }, { M64P_GL_ALPHA_SIZE, SDL_GL_ALPHA_SIZE }, -#if !SDL_VERSION_ATLEAST(1,3,0) - { M64P_GL_SWAP_CONTROL, SDL_GL_SWAP_CONTROL }, -#endif { M64P_GL_MULTISAMPLEBUFFERS, SDL_GL_MULTISAMPLEBUFFERS }, { M64P_GL_MULTISAMPLESAMPLES, SDL_GL_MULTISAMPLESAMPLES } -#if SDL_VERSION_ATLEAST(2,0,0) ,{ M64P_GL_CONTEXT_MAJOR_VERSION, SDL_GL_CONTEXT_MAJOR_VERSION }, { M64P_GL_CONTEXT_MINOR_VERSION, SDL_GL_CONTEXT_MINOR_VERSION }, { M64P_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_MASK } -#endif }; static const int mapSize = sizeof(GLAttrMap) / sizeof(GLAttrMapNode); @@ -668,7 +646,6 @@ EXPORT m64p_error CALL VidExt_GL_SetAttribute(m64p_GLattr Attr, int Value) } /* translate the GL context type mask if necessary */ -#if SDL_VERSION_ATLEAST(2,0,0) if (Attr == M64P_GL_CONTEXT_PROFILE_MASK) { switch (Value) @@ -689,7 +666,6 @@ EXPORT m64p_error CALL VidExt_GL_SetAttribute(m64p_GLattr Attr, int Value) Value = 0; } } -#endif for (i = 0; i < mapSize; i++) { @@ -718,13 +694,11 @@ EXPORT m64p_error CALL VidExt_GL_GetAttribute(m64p_GLattr Attr, int *pValue) if (!SDL_WasInit(SDL_INIT_VIDEO)) return M64ERR_NOT_INIT; -#if SDL_VERSION_ATLEAST(2,0,0) if (Attr == M64P_GL_SWAP_CONTROL) { *pValue = SDL_GL_GetSwapInterval(); return M64ERR_SUCCESS; } -#endif for (i = 0; i < mapSize; i++) { @@ -734,7 +708,6 @@ EXPORT m64p_error CALL VidExt_GL_GetAttribute(m64p_GLattr Attr, int *pValue) if (SDL_GL_GetAttribute(GLAttrMap[i].sdlAttr, &NewValue) != 0) return M64ERR_SYSTEM_FAIL; /* translate the GL context type mask if necessary */ -#if SDL_VERSION_ATLEAST(2,0,0) if (Attr == M64P_GL_CONTEXT_PROFILE_MASK) { switch (NewValue) @@ -752,7 +725,6 @@ EXPORT m64p_error CALL VidExt_GL_GetAttribute(m64p_GLattr Attr, int *pValue) NewValue = 0; } } -#endif *pValue = NewValue; return M64ERR_SUCCESS; } diff --git a/mupen64plus-core/src/device/dd/disk.c b/mupen64plus-core/src/device/dd/disk.c index 405ab46a..405d28fd 100644 --- a/mupen64plus-core/src/device/dd/disk.c +++ b/mupen64plus-core/src/device/dd/disk.c @@ -429,7 +429,7 @@ uint8_t* scan_and_expand_disk_format(uint8_t* data, size_t size, //IPL Load Address uint32_t ipl_load_addr = big32(sys_data->ipl_load_addr); - if (ipl_load_addr < 0x80000000 && ipl_load_addr >= 0x80800000) continue; + if (ipl_load_addr < 0x80000000 || ipl_load_addr >= 0x80800000) continue; //Country Code uint32_t disk_region = big32(sys_data->region); diff --git a/mupen64plus-core/src/device/gb/gb_cart.c b/mupen64plus-core/src/device/gb/gb_cart.c index a6414171..b25992ad 100644 --- a/mupen64plus-core/src/device/gb/gb_cart.c +++ b/mupen64plus-core/src/device/gb/gb_cart.c @@ -240,7 +240,7 @@ static int write_gb_cart_mbc1(struct gb_cart* gb_cart, uint16_t address, const u /* 0x2000-0x3fff: ROM bank select (low 5 bits) */ case (0x2000 >> 13): bank = value & 0x1f; - gb_cart->rom_bank = (gb_cart->rom_bank & ~UINT8_C(0x1f)) | (bank == 0) ? 1 : bank; + gb_cart->rom_bank = (gb_cart->rom_bank & ~UINT8_C(0x1f)) | ((bank == 0) ? 1 : bank); DebugMessage(M64MSG_VERBOSE, "MBC1 set rom bank %02x", gb_cart->rom_bank); break; diff --git a/mupen64plus-core/src/device/r4300/cp1.c b/mupen64plus-core/src/device/r4300/cp1.c index c744f1b6..4426728e 100644 --- a/mupen64plus-core/src/device/r4300/cp1.c +++ b/mupen64plus-core/src/device/r4300/cp1.c @@ -110,7 +110,7 @@ void set_fpr_pointers(struct cp1* cp1, uint32_t newStatus) { for (i = 0; i < 32; i++) { - (r4300_cp1_regs_simple(cp1))[i] = &cp1->regs[i & ~1].float32[i & 1 ^ DOUBLE_HALF_XOR]; + (r4300_cp1_regs_simple(cp1))[i] = &cp1->regs[i & ~1].float32[(i & 1) ^ DOUBLE_HALF_XOR]; (r4300_cp1_regs_double(cp1))[i] = &cp1->regs[i & ~1].float64; } } diff --git a/mupen64plus-core/src/main/eventloop.c b/mupen64plus-core/src/main/eventloop.c index 8f1e70ac..2522e6e0 100644 --- a/mupen64plus-core/src/main/eventloop.c +++ b/mupen64plus-core/src/main/eventloop.c @@ -157,9 +157,7 @@ static int MatchJoyCommand(const SDL_Event *event, eJoyCommand cmd) else if (phrase_str[1] >= '0' && phrase_str[1] <= '9') { dev_number = phrase_str[1] - '0'; -#if SDL_VERSION_ATLEAST(2,0,0) dev_number = l_iJoyInstanceID[dev_number]; -#endif } else { @@ -280,24 +278,15 @@ static int SDLCALL event_sdl_filter(void *userdata, SDL_Event *event) break; case SDL_KEYDOWN: -#if SDL_VERSION_ATLEAST(1,3,0) if (event->key.repeat) return 0; event_sdl_keydown(event->key.keysym.scancode, event->key.keysym.mod); -#else - event_sdl_keydown(event->key.keysym.sym, event->key.keysym.mod); -#endif return 0; case SDL_KEYUP: -#if SDL_VERSION_ATLEAST(1,3,0) event_sdl_keyup(event->key.keysym.scancode, event->key.keysym.mod); -#else - event_sdl_keyup(event->key.keysym.sym, event->key.keysym.mod); -#endif return 0; -#if SDL_VERSION_ATLEAST(1,3,0) case SDL_WINDOWEVENT: switch (event->window.event) { case SDL_WINDOWEVENT_RESIZED: @@ -313,22 +302,6 @@ static int SDLCALL event_sdl_filter(void *userdata, SDL_Event *event) break; } break; -#else - case SDL_VIDEORESIZE: - // call the video plugin. if the video plugin supports resizing, it will resize its viewport and call - // VidExt_ResizeWindow to update the window manager handling our opengl output window - gfx.resizeVideoOutput(event->resize.w, event->resize.h); - return 0; // consumed the event - break; - -#ifdef WIN32 - case SDL_SYSWMEVENT: - if(event->syswm.msg->msg == WM_MOVE) - gfx.moveScreen(0,0); // The video plugin is responsible for getting the new window position - return 0; // consumed the event - break; -#endif -#endif #ifndef NO_KEYBINDINGS // if joystick action is detected, check if it's mapped to a special function @@ -452,13 +425,8 @@ void event_initialize(void) { if (!SDL_WasInit(SDL_INIT_JOYSTICK)) SDL_InitSubSystem(SDL_INIT_JOYSTICK); -#if SDL_VERSION_ATLEAST(2,0,0) SDL_Joystick *thisJoy = SDL_JoystickOpen(device); l_iJoyInstanceID[device] = SDL_JoystickInstanceID(thisJoy); -#else - if (!SDL_JoystickOpened(device)) - SDL_JoystickOpen(device); -#endif } phrase_str = strtok(NULL, ","); @@ -469,17 +437,7 @@ void event_initialize(void) /* set up SDL event filter and disable key repeat */ -#if !SDL_VERSION_ATLEAST(2,0,0) - SDL_EnableKeyRepeat(0, 0); -#endif SDL_SetEventFilter(event_sdl_filter, NULL); - -#if defined(WIN32) && !SDL_VERSION_ATLEAST(1,3,0) - SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE); - - if (SDL_EventState(SDL_SYSWMEVENT, SDL_QUERY) != SDL_ENABLE) - DebugMessage(M64MSG_WARNING, "Failed to change event state: %s", SDL_GetError()); -#endif } int event_set_core_defaults(void) @@ -520,11 +478,7 @@ int event_set_core_defaults(void) int key = SDL_SCANCODE_UNKNOWN; for (int slot = 0; slot < 10; slot++) { -#if ! SDL_VERSION_ATLEAST(1,3,0) - key = SDL_SCANCODE_0 + slot; -#else key = slot == 0 ? SDL_SCANCODE_0 : SDL_SCANCODE_1 + (slot - 1); -#endif sprintf(kbdSaveSlotStr, "%s%i", kbdSaveSlot, slot); sprintf(kbdSaveSlotHelpStr, "SDL keysym for save slot %i", slot); ConfigSetDefaultInt(l_CoreEventsConfig, kbdSaveSlotStr, sdl_native2keysym(key), kbdSaveSlotHelpStr); diff --git a/mupen64plus-core/src/main/netplay.c b/mupen64plus-core/src/main/netplay.c index 6cc6d664..7de022e5 100644 --- a/mupen64plus-core/src/main/netplay.c +++ b/mupen64plus-core/src/main/netplay.c @@ -321,11 +321,8 @@ static int netplay_ensure_valid(uint8_t control_id) if (l_udpChannel == -1) return 0; -#if SDL_VERSION_ATLEAST(2,0,0) SDL_Thread* thread = SDL_CreateThread(netplay_require_response, "Netplay key request", &control_id); -#else - SDL_Thread* thread = SDL_CreateThread(netplay_require_response, &control_id); -#endif + while (!check_valid(control_id, l_cin_compats[control_id].netplay_count) && l_udpChannel != -1) netplay_process(); int success; diff --git a/mupen64plus-core/src/main/workqueue.c b/mupen64plus-core/src/main/workqueue.c index 2dd28d94..8efcc573 100644 --- a/mupen64plus-core/src/main/workqueue.c +++ b/mupen64plus-core/src/main/workqueue.c @@ -131,11 +131,8 @@ int workqueue_init(void) return -1; } -#if SDL_VERSION_ATLEAST(2,0,0) thread->thread = SDL_CreateThread(workqueue_thread_handler, "m64pwq", thread); -#else - thread->thread = SDL_CreateThread(workqueue_thread_handler, thread); -#endif + if (!thread->thread) { DebugMessage(M64MSG_ERROR, "Could not create workqueue thread handler"); SDL_UnlockMutex(workqueue_mgmt.lock); diff --git a/mupen64plus-core/src/osd/osd.c b/mupen64plus-core/src/osd/osd.c index 5930fbdf..684d7352 100644 --- a/mupen64plus-core/src/osd/osd.c +++ b/mupen64plus-core/src/osd/osd.c @@ -234,7 +234,6 @@ void osd_init(int width, int height) return; } -#if SDL_VERSION_ATLEAST(2,0,0) int gl_context; VidExt_GL_GetAttribute(M64P_GL_CONTEXT_PROFILE_MASK, &gl_context); if (gl_context == M64P_GL_CONTEXT_PROFILE_CORE) @@ -242,7 +241,6 @@ void osd_init(int width, int height) DebugMessage(M64MSG_WARNING, "OSD not compatible with OpenGL core context. OSD deactivated."); return; } -#endif // clear statics for (i = 0; i < OSD_NUM_CORNERS; i++) From 6ccb9dd46f19f8c76bcaeed1b1f24466fb95a481 Mon Sep 17 00:00:00 2001 From: M4xw Date: Wed, 26 Feb 2025 16:01:34 +0100 Subject: [PATCH 33/58] git subrepo pull mupen64plus-rsp-paraLLEl subrepo: subdir: "mupen64plus-rsp-paraLLEl" merged: "fd28f47a9" upstream: origin: "git@github.com:libretro/parallel-rsp.git" branch: "master" commit: "fd28f47a9" git-subrepo: version: "0.4.6" origin: "https://github.com/ingydotnet/git-subrepo.git" commit: "110b9eb" --- mupen64plus-rsp-paraLLEl/.gitrepo | 4 +- mupen64plus-rsp-paraLLEl/CMakeLists.txt | 1 - mupen64plus-rsp-paraLLEl/CREDITS.txt | 6 +- .../arch/simd/rsp/rsp_impl.h | 1 - mupen64plus-rsp-paraLLEl/arch/simd/rsp/vmov.h | 18 --- mupen64plus-rsp-paraLLEl/jit_allocator.cpp | 2 +- mupen64plus-rsp-paraLLEl/parallel.cpp | 2 + mupen64plus-rsp-paraLLEl/rsp/cp0.cpp | 118 ++++++++---------- mupen64plus-rsp-paraLLEl/rsp/vfunctions.cpp | 114 +++++++++++++++-- mupen64plus-rsp-paraLLEl/rsp_jit.cpp | 11 +- mupen64plus-rsp-paraLLEl/rsp_op.hpp | 3 + mupen64plus-rsp-paraLLEl/state.hpp | 2 +- 12 files changed, 173 insertions(+), 109 deletions(-) delete mode 100644 mupen64plus-rsp-paraLLEl/arch/simd/rsp/vmov.h diff --git a/mupen64plus-rsp-paraLLEl/.gitrepo b/mupen64plus-rsp-paraLLEl/.gitrepo index 4c424381..ef9d7440 100644 --- a/mupen64plus-rsp-paraLLEl/.gitrepo +++ b/mupen64plus-rsp-paraLLEl/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = git@github.com:libretro/parallel-rsp.git branch = master - commit = 5b0f81bd08ad6bca177e2713237697b04282dbe4 - parent = 4892ef6fb9d5a00e7e602977202a228dbe7f92b9 + commit = fd28f47a96370c8bfcf41d680d1ab01879a801a8 + parent = 8bbcd26454ff0367ca05cdafbb1f8858931a264f method = rebase cmdver = 0.4.6 diff --git a/mupen64plus-rsp-paraLLEl/CMakeLists.txt b/mupen64plus-rsp-paraLLEl/CMakeLists.txt index 31c1ce5c..ef5fc01b 100644 --- a/mupen64plus-rsp-paraLLEl/CMakeLists.txt +++ b/mupen64plus-rsp-paraLLEl/CMakeLists.txt @@ -34,7 +34,6 @@ add_library(parallel-rsp STATIC arch/simd/rsp/vcmp.h arch/simd/rsp/vdivh.h arch/simd/rsp/vmac.h - arch/simd/rsp/vmov.h arch/simd/rsp/vmrg.h arch/simd/rsp/vmudh.h arch/simd/rsp/vmul.h diff --git a/mupen64plus-rsp-paraLLEl/CREDITS.txt b/mupen64plus-rsp-paraLLEl/CREDITS.txt index 49f40644..d5f5eb00 100644 --- a/mupen64plus-rsp-paraLLEl/CREDITS.txt +++ b/mupen64plus-rsp-paraLLEl/CREDITS.txt @@ -1,11 +1,11 @@ Written by Themaister. -The code is heavily reliant on MarathonMan's CEN64 RSP implementation, as well as CXD4's RSP implementation. +The code is heavily reliant on MarathonMan's CEN64 RSP implementation, as well as Ares and CXD4's RSP implementations. MIPS core: Rewritten from scratch -CP0: Near copy-pasta from CEN64 +CP0: Near copy-pasta from CEN64, with some fixes from Ares brought in CP2: Near copy-pasta from CEN64 -LS pipe: Near copy-pasta from CXD4 +LS pipe: Ported from Ares Mupen64plus glue code: Reused most of CXD4. Lightning jitter interface: Written from scratch diff --git a/mupen64plus-rsp-paraLLEl/arch/simd/rsp/rsp_impl.h b/mupen64plus-rsp-paraLLEl/arch/simd/rsp/rsp_impl.h index f81dfa60..e12bce2a 100644 --- a/mupen64plus-rsp-paraLLEl/arch/simd/rsp/rsp_impl.h +++ b/mupen64plus-rsp-paraLLEl/arch/simd/rsp/rsp_impl.h @@ -12,7 +12,6 @@ #include "vcr.h" #include "vdivh.h" #include "vmac.h" -#include "vmov.h" #include "vmrg.h" #include "vmul.h" #include "vmulh.h" diff --git a/mupen64plus-rsp-paraLLEl/arch/simd/rsp/vmov.h b/mupen64plus-rsp-paraLLEl/arch/simd/rsp/vmov.h deleted file mode 100644 index be5856e2..00000000 --- a/mupen64plus-rsp-paraLLEl/arch/simd/rsp/vmov.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// arch/x86_64/rsp/vmov.c -// -// This file is subject to the terms and conditions defined in -// 'LICENSE', which is part of this source code package. -// - -inline __m128i rsp_vmov(RSP::CPUState *rsp, unsigned src, unsigned e, unsigned dest, unsigned de) -{ - uint16_t data; - - // Get the element from VT. - data = rsp->cp2.regs[src].e[e & 0x7]; - - // Write out the upper part of the result. - rsp->cp2.regs[dest].e[de & 0x7] = data; - return rsp_vect_load_unshuffled_operand(rsp->cp2.regs[dest].e); -} diff --git a/mupen64plus-rsp-paraLLEl/jit_allocator.cpp b/mupen64plus-rsp-paraLLEl/jit_allocator.cpp index 4207fabf..5100e60d 100644 --- a/mupen64plus-rsp-paraLLEl/jit_allocator.cpp +++ b/mupen64plus-rsp-paraLLEl/jit_allocator.cpp @@ -14,7 +14,7 @@ namespace RSP { namespace JIT { -#if TARGET_OS_TV // tvOS is 64bit but does not allow an infinite amount of VA space +#ifdef IOS // iOS/tvOS is 64bit but does not allow an infinite amount of VA space static constexpr bool huge_va = false; #else static constexpr bool huge_va = std::numeric_limits::max() > 0x100000000ull; diff --git a/mupen64plus-rsp-paraLLEl/parallel.cpp b/mupen64plus-rsp-paraLLEl/parallel.cpp index 02d35398..c7ce7a0e 100644 --- a/mupen64plus-rsp-paraLLEl/parallel.cpp +++ b/mupen64plus-rsp-paraLLEl/parallel.cpp @@ -83,6 +83,8 @@ extern "C" return cycles; else if (*RSP::cpu.get_state().cp0.irq & 1) RSP::rsp.CheckInterrupts(); + else if (*RSP::rsp.SP_STATUS_REG & SP_STATUS_HALT) + return cycles; else if (*RSP::rsp.SP_SEMAPHORE_REG != 0) // Semaphore lock fixes. { } diff --git a/mupen64plus-rsp-paraLLEl/rsp/cp0.cpp b/mupen64plus-rsp-paraLLEl/rsp/cp0.cpp index 22d0ca51..6647d584 100644 --- a/mupen64plus-rsp-paraLLEl/rsp/cp0.cpp +++ b/mupen64plus-rsp-paraLLEl/rsp/cp0.cpp @@ -27,14 +27,10 @@ extern "C" if (rt) rsp->sr[rt] = res; - // CFG_MEND_SEMAPHORE_LOCK == 0 by default, - // so don't bother implementing semaphores. - // It makes Mario Golf run terribly for some reason. - #ifdef PARALLEL_INTEGRATION - // WAIT_FOR_CPU_HOST. From CXD4. if (rd == CP0_REGISTER_SP_STATUS) { + // Might be waiting for the CPU to set a signal bit on the STATUS register. Increment timeout RSP::MFC0_count[rt] += 1; if (RSP::MFC0_count[rt] >= RSP::SP_STATUS_TIMEOUT) { @@ -44,81 +40,67 @@ extern "C" } #endif +#if 0 // FIXME: this is broken with upstream mupen64plus-core + if (rd == CP0_REGISTER_SP_SEMAPHORE) + { + if (*rsp->cp0.cr[CP0_REGISTER_SP_SEMAPHORE]) + { +#ifdef PARALLEL_INTEGRATION + RSP::MFC0_count[rt] += 8; // Almost certainly waiting on the CPU. Timeout faster. + if (RSP::MFC0_count[rt] >= RSP::SP_STATUS_TIMEOUT) + { + *RSP::rsp.SP_STATUS_REG |= SP_STATUS_HALT; + return MODE_CHECK_FLAGS; + } +#endif + } + else + *rsp->cp0.cr[CP0_REGISTER_SP_SEMAPHORE] = 1; + } +#endif + //if (rd == 4) // SP_STATUS_REG // fprintf(stderr, "READING STATUS REG!\n"); return MODE_CONTINUE; } +#define RSP_HANDLE_STATUS_WRITE(flag) \ + switch (rt & (SP_SET_##flag | SP_CLR_##flag)) \ + { \ + case SP_SET_##flag: status |= SP_STATUS_##flag; break; \ + case SP_CLR_##flag: status &= ~SP_STATUS_##flag; break; \ + default: break; \ + } + static inline int rsp_status_write(RSP::CPUState *rsp, uint32_t rt) { //fprintf(stderr, "Writing 0x%x to status reg!\n", rt); uint32_t status = *rsp->cp0.cr[CP0_REGISTER_SP_STATUS]; - if (rt & SP_CLR_HALT) - status &= ~SP_STATUS_HALT; - else if (rt & SP_SET_HALT) - status |= SP_STATUS_HALT; + RSP_HANDLE_STATUS_WRITE(HALT) + RSP_HANDLE_STATUS_WRITE(SSTEP) + RSP_HANDLE_STATUS_WRITE(INTR_BREAK) + RSP_HANDLE_STATUS_WRITE(SIG0) + RSP_HANDLE_STATUS_WRITE(SIG1) + RSP_HANDLE_STATUS_WRITE(SIG2) + RSP_HANDLE_STATUS_WRITE(SIG3) + RSP_HANDLE_STATUS_WRITE(SIG4) + RSP_HANDLE_STATUS_WRITE(SIG5) + RSP_HANDLE_STATUS_WRITE(SIG6) + RSP_HANDLE_STATUS_WRITE(SIG7) + + switch (rt & (SP_SET_INTR | SP_CLR_INTR)) + { + case SP_SET_INTR: *rsp->cp0.irq |= 1; break; + case SP_CLR_INTR: *rsp->cp0.irq &= ~1; break; + default: break; + } if (rt & SP_CLR_BROKE) status &= ~SP_STATUS_BROKE; - if (rt & SP_CLR_INTR) - *rsp->cp0.irq &= ~1; - else if (rt & SP_SET_INTR) - *rsp->cp0.irq |= 1; - - if (rt & SP_CLR_SSTEP) - status &= ~SP_STATUS_SSTEP; - else if (rt & SP_SET_SSTEP) - status |= SP_STATUS_SSTEP; - - if (rt & SP_CLR_INTR_BREAK) - status &= ~SP_STATUS_INTR_BREAK; - else if (rt & SP_SET_INTR_BREAK) - status |= SP_STATUS_INTR_BREAK; - - if (rt & SP_CLR_SIG0) - status &= ~SP_STATUS_SIG0; - else if (rt & SP_SET_SIG0) - status |= SP_STATUS_SIG0; - - if (rt & SP_CLR_SIG1) - status &= ~SP_STATUS_SIG1; - else if (rt & SP_SET_SIG1) - status |= SP_STATUS_SIG1; - - if (rt & SP_CLR_SIG2) - status &= ~SP_STATUS_SIG2; - else if (rt & SP_SET_SIG2) - status |= SP_STATUS_SIG2; - - if (rt & SP_CLR_SIG3) - status &= ~SP_STATUS_SIG3; - else if (rt & SP_SET_SIG3) - status |= SP_STATUS_SIG3; - - if (rt & SP_CLR_SIG4) - status &= ~SP_STATUS_SIG4; - else if (rt & SP_SET_SIG4) - status |= SP_STATUS_SIG4; - - if (rt & SP_CLR_SIG5) - status &= ~SP_STATUS_SIG5; - else if (rt & SP_SET_SIG5) - status |= SP_STATUS_SIG5; - - if (rt & SP_CLR_SIG6) - status &= ~SP_STATUS_SIG6; - else if (rt & SP_SET_SIG6) - status |= SP_STATUS_SIG6; - - if (rt & SP_CLR_SIG7) - status &= ~SP_STATUS_SIG7; - else if (rt & SP_SET_SIG7) - status |= SP_STATUS_SIG7; - *rsp->cp0.cr[CP0_REGISTER_SP_STATUS] = status; return ((*rsp->cp0.irq & 1) || (status & SP_STATUS_HALT)) ? MODE_CHECK_FLAGS : MODE_CONTINUE; } @@ -178,6 +160,7 @@ extern "C" *rsp->cp0.cr[CP0_REGISTER_DMA_DRAM] = source; *rsp->cp0.cr[CP0_REGISTER_DMA_CACHE] = dest; + *rsp->cp0.cr[CP0_REGISTER_DMA_READ_LENGTH] = 0xff8; #ifdef INTENSE_DEBUG log_rsp_mem_parallel(); @@ -231,6 +214,7 @@ extern "C" *rsp->cp0.cr[CP0_REGISTER_DMA_CACHE] = source; *rsp->cp0.cr[CP0_REGISTER_DMA_DRAM] = dest; + *rsp->cp0.cr[CP0_REGISTER_DMA_WRITE_LENGTH] = 0xff8; #ifdef INTENSE_DEBUG log_rsp_mem_parallel(); #endif @@ -269,9 +253,9 @@ extern "C" case CP0_REGISTER_SP_STATUS: return rsp_status_write(rsp, val); - case CP0_REGISTER_SP_RESERVED: - // CXD4 forces this to 0. - *rsp->cp0.cr[CP0_REGISTER_SP_RESERVED] = 0; + case CP0_REGISTER_SP_SEMAPHORE: + // Any write to the semaphore register, regardless of value, sets it to 0 for the next read + *rsp->cp0.cr[CP0_REGISTER_SP_SEMAPHORE] = 0; break; case CP0_REGISTER_CMD_START: diff --git a/mupen64plus-rsp-paraLLEl/rsp/vfunctions.cpp b/mupen64plus-rsp-paraLLEl/rsp/vfunctions.cpp index c236d818..2f993b23 100644 --- a/mupen64plus-rsp-paraLLEl/rsp/vfunctions.cpp +++ b/mupen64plus-rsp-paraLLEl/rsp/vfunctions.cpp @@ -25,6 +25,13 @@ extern "C" { + static inline int32_t clamp16s(int32_t x) + { + if (x > 0x7fff) return 0x7fff; + if (x < -0x8000) return -0x8000; + return x; + } + // // VABS // @@ -297,6 +304,25 @@ extern "C" STORE_RESULT(); } + void RSP_VMACQ(RSP::CPUState *rsp, unsigned vd, unsigned, unsigned, unsigned) + { + TRACE_VU(VMACQ); + uint16_t *acc = rsp->cp2.acc.e; + for (unsigned i = 0; i < 8; i++) + { + int32_t prod = (int16_t)acc[i] << 16; + prod |= acc[8+i]; + if (prod < 0 && !(prod & 1 << 5)) + prod += 32; + else if (prod >= 32 && !(prod & 1 << 5)) + prod -= 32; + acc[i] = prod >> 16; + acc[8+i] = prod & 0xffffu; + + rsp->cp2.regs[vd].e[i] = clamp16s(prod >> 1) & ~15; + } + } + // // VMADH // VMUDH @@ -464,10 +490,9 @@ extern "C" { TRACE_VU(VMOV); uint16_t *acc = rsp->cp2.acc.e; - unsigned de = vs & 0x7; write_acc_lo(acc, LOAD_VT()); - __m128i result = rsp_vmov(rsp, vt, e, vd, de); - STORE_RESULT(); + vs &= 0x7; + rsp->cp2.regs[vd].e[vs] = rsp->cp2.acc.e[16+vs]; } // @@ -489,6 +514,7 @@ extern "C" // // VMULF + // VMULQ // VMULU // void RSP_VMULF(RSP::CPUState *rsp, unsigned vd, unsigned vs, unsigned vt, unsigned e) @@ -505,6 +531,28 @@ extern "C" STORE_RESULT(); } + void RSP_VMULQ(RSP::CPUState *rsp, unsigned vd, unsigned vs, unsigned vt, unsigned e) + { + TRACE_VU(VMULQ); + uint16_t *acc = rsp->cp2.acc.e; + uint16_t *vde = rsp->cp2.regs[vd].e; + int16_t *vse = (int16_t*)rsp->cp2.regs[vs].e; + + int16_t vte[8]; + rsp_vect_t vtt = LOAD_VT(); + rsp_vect_write_operand((uint16_t*)vte, vtt); + + for (unsigned i = 0; i < 8; i++) + { + int32_t prod = vse[i] * vte[i]; + if (prod < 0) prod += 31; + acc[i] = prod >> 16; + acc[8+i] = prod & 0xffff; + acc[16+i] = 0; + vde[i] = clamp16s(prod >> 1) & ~15; + } + } + void RSP_VMULU(RSP::CPUState *rsp, unsigned vd, unsigned vs, unsigned vt, unsigned e) { TRACE_VU(VMULU); @@ -519,6 +567,52 @@ extern "C" STORE_RESULT(); } + // + // VRNDP + // VRNDN + // + static inline void RSP_VRND(RSP::CPUState *rsp, unsigned vd, unsigned vs, unsigned vt, unsigned e, uint_fast8_t variant) + { + int16_t vte[8]; + rsp_vect_t vtt = LOAD_VT(); + rsp_vect_write_operand((uint16_t*)vte, vtt); + uint16_t *acc = rsp->cp2.acc.e; + uint16_t *vde = rsp->cp2.regs[vd].e; + + for (unsigned i = 0; i < 8; i++) + { + int64_t acc48 = + ((int64_t)(int16_t)acc[i] << 32) | + ((int64_t)acc[8+i] << 16) | + (int64_t)acc[16+i]; + + const uint_fast8_t negative_acc = acc48 < 0; + if (!!variant xor !!negative_acc) + { + int64_t value = (int64_t)(int16_t)vte[i]; + if (vs & 1) value <<= 16; + acc48 += value; + } + + acc[i] = (acc48 >> 32) & 0xffff; + acc[8+i] = (acc48 >> 16) & 0xffff; + acc[16+i] = acc48 & 0xffff; + vde[i] = clamp16s((int32_t)(acc48 >> 16)); + } + } + + void RSP_VRNDN(RSP::CPUState *rsp, unsigned vd, unsigned vs, unsigned vt, unsigned e) + { + TRACE_VU(RSP_VRNDN); + RSP_VRND(rsp, vd, vs, vt, e, 0); + } + + void RSP_VRNDP(RSP::CPUState *rsp, unsigned vd, unsigned vs, unsigned vt, unsigned e) + { + TRACE_VU(RSP_VRNDP); + RSP_VRND(rsp, vd, vs, vt, e, 1); + } + // // VNOP // @@ -563,7 +657,6 @@ extern "C" TRACE_VU(VRCP); uint16_t *acc = rsp->cp2.acc.e; unsigned de = vs & 0x7; - e &= 0x7; write_acc_lo(acc, LOAD_VT()); @@ -577,7 +670,6 @@ extern "C" TRACE_VU(VRCPL); uint16_t *acc = rsp->cp2.acc.e; unsigned de = vs & 0x7; - e &= 0x7; write_acc_lo(acc, LOAD_VT()); @@ -593,7 +685,6 @@ extern "C" TRACE_VU(VRSQ); uint16_t *acc = rsp->cp2.acc.e; unsigned de = vs & 0x7; - e &= 0x7; write_acc_lo(acc, LOAD_VT()); @@ -607,7 +698,6 @@ extern "C" TRACE_VU(VRSQL); uint16_t *acc = rsp->cp2.acc.e; unsigned de = vs & 0x7; - e &= 0x7; write_acc_lo(acc, LOAD_VT()); @@ -627,7 +717,6 @@ extern "C" TRACE_VU(VRCPH); uint16_t *acc = rsp->cp2.acc.e; unsigned de = vs & 0x7; - e &= 0x7; write_acc_lo(acc, LOAD_VT()); @@ -643,7 +732,6 @@ extern "C" TRACE_VU(VRSQH); uint16_t *acc = rsp->cp2.acc.e; unsigned de = vs & 0x7; - e &= 0x7; write_acc_lo(acc, LOAD_VT()); @@ -745,9 +833,13 @@ extern "C" } // RESERVED - void RSP_RESERVED(RSP::CPUState *rsp, unsigned vd, unsigned, unsigned, unsigned) + void RSP_RESERVED(RSP::CPUState *rsp, unsigned vd, unsigned vs, unsigned vt, unsigned e) { - rsp_vect_t result = rsp_vzero(); + uint16_t *acc = rsp->cp2.acc.e; + rsp_vect_t result = _mm_add_epi16(LOAD_VS(), LOAD_VT()); + write_acc_lo(acc, result); + + result = rsp_vzero(); STORE_RESULT(); } } diff --git a/mupen64plus-rsp-paraLLEl/rsp_jit.cpp b/mupen64plus-rsp-paraLLEl/rsp_jit.cpp index d91cba15..20524047 100644 --- a/mupen64plus-rsp-paraLLEl/rsp_jit.cpp +++ b/mupen64plus-rsp-paraLLEl/rsp_jit.cpp @@ -874,12 +874,12 @@ void CPU::jit_instruction(jit_state_t *_jit, uint32_t pc, uint32_t instr, using VUOp = void (*)(RSP::CPUState *, unsigned vd, unsigned vs, unsigned vt, unsigned e); static const VUOp ops[64] = { - RSP_VMULF, RSP_VMULU, nullptr, nullptr, RSP_VMUDL, RSP_VMUDM, RSP_VMUDN, RSP_VMUDH, RSP_VMACF, RSP_VMACU, nullptr, - nullptr, RSP_VMADL, RSP_VMADM, RSP_VMADN, RSP_VMADH, RSP_VADD, RSP_VSUB, nullptr, RSP_VABS, RSP_VADDC, RSP_VSUBC, + RSP_VMULF, RSP_VMULU, RSP_VRNDP, RSP_VMULQ, RSP_VMUDL, RSP_VMUDM, RSP_VMUDN, RSP_VMUDH, RSP_VMACF, RSP_VMACU, RSP_VRNDN, + RSP_VMACQ, RSP_VMADL, RSP_VMADM, RSP_VMADN, RSP_VMADH, RSP_VADD, RSP_VSUB, nullptr, RSP_VABS, RSP_VADDC, RSP_VSUBC, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, RSP_VSAR, nullptr, nullptr, RSP_VLT, RSP_VEQ, RSP_VNE, RSP_VGE, RSP_VCL, RSP_VCH, RSP_VCR, RSP_VMRG, RSP_VAND, RSP_VNAND, RSP_VOR, RSP_VNOR, RSP_VXOR, RSP_VNXOR, nullptr, nullptr, RSP_VRCP, RSP_VRCPL, RSP_VRCPH, RSP_VMOV, RSP_VRSQ, RSP_VRSQL, RSP_VRSQH, - RSP_VNOP, + RSP_VNOP, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, RSP_VNOP }; auto *vuop = ops[op]; @@ -985,6 +985,7 @@ void CPU::jit_instruction(jit_state_t *_jit, uint32_t pc, uint32_t instr, case 007: // SRAV { + NOP_IF_RD_ZERO(); unsigned rt_reg = regs.load_mips_register_sext(_jit, rt); unsigned rs_reg = regs.load_mips_register_noext(_jit, rs); unsigned rs_tmp_reg = regs.modify_mips_register(_jit, RegisterCache::SCRATCH_REGISTER0); @@ -1385,7 +1386,8 @@ void CPU::jit_instruction(jit_state_t *_jit, uint32_t pc, uint32_t instr, case 013: // SLTIU { - TWO_REG_IMM_OP(lti_u, uint16_t, zext); + // SLTIU sign extends the immediate to 32 bit but then does an unsigned comparison + TWO_REG_IMM_OP(lti_u, int16_t, sext); break; } @@ -1564,6 +1566,7 @@ void CPU::jit_instruction(jit_state_t *_jit, uint32_t pc, uint32_t instr, } case 043: // LW + case 047: // LWU { jit_emit_load_operation(_jit, pc, instr, [](jit_state_t *_jit, unsigned a, unsigned b, unsigned c) { jit_ldxr_i(a, b, c); }, diff --git a/mupen64plus-rsp-paraLLEl/rsp_op.hpp b/mupen64plus-rsp-paraLLEl/rsp_op.hpp index 76722aaa..29523b89 100644 --- a/mupen64plus-rsp-paraLLEl/rsp_op.hpp +++ b/mupen64plus-rsp-paraLLEl/rsp_op.hpp @@ -49,12 +49,15 @@ extern "C" #define DECL_COP2(op) void RSP_##op(RSP::CPUState *rsp, unsigned vd, unsigned vs, unsigned vt, unsigned e) DECL_COP2(VMULF); DECL_COP2(VMULU); + DECL_COP2(VRNDP); + DECL_COP2(VMULQ); DECL_COP2(VMUDL); DECL_COP2(VMUDM); DECL_COP2(VMUDN); DECL_COP2(VMUDH); DECL_COP2(VMACF); DECL_COP2(VMACU); + DECL_COP2(VRNDN); DECL_COP2(VMACQ); DECL_COP2(VMADL); DECL_COP2(VMADM); diff --git a/mupen64plus-rsp-paraLLEl/state.hpp b/mupen64plus-rsp-paraLLEl/state.hpp index ce80dae5..3822c73c 100644 --- a/mupen64plus-rsp-paraLLEl/state.hpp +++ b/mupen64plus-rsp-paraLLEl/state.hpp @@ -37,7 +37,7 @@ enum CP0Registers CP0_REGISTER_SP_STATUS = 4, CP0_REGISTER_DMA_FULL = 5, CP0_REGISTER_DMA_BUSY = 6, - CP0_REGISTER_SP_RESERVED = 7, + CP0_REGISTER_SP_SEMAPHORE = 7, CP0_REGISTER_CMD_START = 8, CP0_REGISTER_CMD_END = 9, CP0_REGISTER_CMD_CURRENT = 10, From 74792591fa40c11b7fa4a08eeedb054287aae092 Mon Sep 17 00:00:00 2001 From: M4xw Date: Tue, 4 Mar 2025 13:41:15 +0100 Subject: [PATCH 34/58] Yeet precompiled headers --- .../r4300/new_dynarec/arm/asm_defines_gas.h | 35 ------------------- .../r4300/new_dynarec/arm/asm_defines_nasm.h | 35 ------------------- .../r4300/new_dynarec/arm64/asm_defines_gas.h | 35 ------------------- .../new_dynarec/arm64/asm_defines_nasm.h | 35 ------------------- .../r4300/new_dynarec/x64/asm_defines_gas.h | 35 ------------------- .../r4300/new_dynarec/x64/asm_defines_nasm.h | 35 ------------------- .../r4300/new_dynarec/x86/asm_defines_gas.h | 35 ------------------- .../r4300/new_dynarec/x86/asm_defines_nasm.h | 35 ------------------- 8 files changed, 280 deletions(-) delete mode 100644 mupen64plus-core/src/device/r4300/new_dynarec/arm/asm_defines_gas.h delete mode 100644 mupen64plus-core/src/device/r4300/new_dynarec/arm/asm_defines_nasm.h delete mode 100644 mupen64plus-core/src/device/r4300/new_dynarec/arm64/asm_defines_gas.h delete mode 100644 mupen64plus-core/src/device/r4300/new_dynarec/arm64/asm_defines_nasm.h delete mode 100644 mupen64plus-core/src/device/r4300/new_dynarec/x64/asm_defines_gas.h delete mode 100644 mupen64plus-core/src/device/r4300/new_dynarec/x64/asm_defines_nasm.h delete mode 100644 mupen64plus-core/src/device/r4300/new_dynarec/x86/asm_defines_gas.h delete mode 100644 mupen64plus-core/src/device/r4300/new_dynarec/x86/asm_defines_nasm.h diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/arm/asm_defines_gas.h b/mupen64plus-core/src/device/r4300/new_dynarec/arm/asm_defines_gas.h deleted file mode 100644 index 7cbf7dd0..00000000 --- a/mupen64plus-core/src/device/r4300/new_dynarec/arm/asm_defines_gas.h +++ /dev/null @@ -1,35 +0,0 @@ -#define offsetof_struct_device_r4300 (0x00000000) -#define offsetof_struct_r4300_core_cp0 (0x029014dc) -#define offsetof_struct_cp0_last_addr (0x00000194) -#define offsetof_struct_cp0_count_per_op (0x00000198) -#define offsetof_struct_cp0_tlb (0x000001a0) -#define offsetof_struct_tlb_entries (0x00000000) -#define offsetof_struct_tlb_LUT_r (0x00000680) -#define offsetof_struct_tlb_LUT_w (0x00400680) -#define offsetof_struct_r4300_core_cached_interp (0x00000098) -#define offsetof_struct_cached_interp_invalid_code (0x00000000) -#define offsetof_struct_r4300_core_new_dynarec_hot_state (0x02501000) -#define offsetof_struct_r4300_core_extra_memory (0x00501000) -#define offsetof_struct_new_dynarec_hot_state_dynarec_local (0x00000000) -#define offsetof_struct_new_dynarec_hot_state_cycle_count (0x00000040) -#define offsetof_struct_new_dynarec_hot_state_pending_exception (0x00000044) -#define offsetof_struct_new_dynarec_hot_state_pcaddr (0x00000048) -#define offsetof_struct_new_dynarec_hot_state_stop (0x0000004c) -#define offsetof_struct_new_dynarec_hot_state_invc_ptr (0x00000050) -#define offsetof_struct_new_dynarec_hot_state_cp1_fcr0 (0x0000006c) -#define offsetof_struct_new_dynarec_hot_state_cp1_fcr31 (0x00000070) -#define offsetof_struct_new_dynarec_hot_state_regs (0x00000078) -#define offsetof_struct_new_dynarec_hot_state_hi (0x00000178) -#define offsetof_struct_new_dynarec_hot_state_lo (0x00000180) -#define offsetof_struct_new_dynarec_hot_state_cp0_regs (0x00000188) -#define offsetof_struct_new_dynarec_hot_state_cp1_regs_simple (0x00000210) -#define offsetof_struct_new_dynarec_hot_state_cp1_regs_double (0x00000290) -#define offsetof_struct_new_dynarec_hot_state_rounding_modes (0x00000318) -#define offsetof_struct_new_dynarec_hot_state_branch_target (0x00000328) -#define offsetof_struct_new_dynarec_hot_state_pc (0x0000032c) -#define offsetof_struct_new_dynarec_hot_state_fake_pc (0x00000330) -#define offsetof_struct_new_dynarec_hot_state_rs (0x000003b8) -#define offsetof_struct_new_dynarec_hot_state_rt (0x000003c0) -#define offsetof_struct_new_dynarec_hot_state_rd (0x000003c8) -#define offsetof_struct_new_dynarec_hot_state_mini_ht (0x000003d4) -#define offsetof_struct_new_dynarec_hot_state_memory_map (0x000004d4) diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/arm/asm_defines_nasm.h b/mupen64plus-core/src/device/r4300/new_dynarec/arm/asm_defines_nasm.h deleted file mode 100644 index 7b8581fd..00000000 --- a/mupen64plus-core/src/device/r4300/new_dynarec/arm/asm_defines_nasm.h +++ /dev/null @@ -1,35 +0,0 @@ -%define offsetof_struct_device_r4300 (0x00000000) -%define offsetof_struct_r4300_core_cp0 (0x029014dc) -%define offsetof_struct_cp0_last_addr (0x00000194) -%define offsetof_struct_cp0_count_per_op (0x00000198) -%define offsetof_struct_cp0_tlb (0x000001a0) -%define offsetof_struct_tlb_entries (0x00000000) -%define offsetof_struct_tlb_LUT_r (0x00000680) -%define offsetof_struct_tlb_LUT_w (0x00400680) -%define offsetof_struct_r4300_core_cached_interp (0x00000098) -%define offsetof_struct_cached_interp_invalid_code (0x00000000) -%define offsetof_struct_r4300_core_new_dynarec_hot_state (0x02501000) -%define offsetof_struct_r4300_core_extra_memory (0x00501000) -%define offsetof_struct_new_dynarec_hot_state_dynarec_local (0x00000000) -%define offsetof_struct_new_dynarec_hot_state_cycle_count (0x00000040) -%define offsetof_struct_new_dynarec_hot_state_pending_exception (0x00000044) -%define offsetof_struct_new_dynarec_hot_state_pcaddr (0x00000048) -%define offsetof_struct_new_dynarec_hot_state_stop (0x0000004c) -%define offsetof_struct_new_dynarec_hot_state_invc_ptr (0x00000050) -%define offsetof_struct_new_dynarec_hot_state_cp1_fcr0 (0x0000006c) -%define offsetof_struct_new_dynarec_hot_state_cp1_fcr31 (0x00000070) -%define offsetof_struct_new_dynarec_hot_state_regs (0x00000078) -%define offsetof_struct_new_dynarec_hot_state_hi (0x00000178) -%define offsetof_struct_new_dynarec_hot_state_lo (0x00000180) -%define offsetof_struct_new_dynarec_hot_state_cp0_regs (0x00000188) -%define offsetof_struct_new_dynarec_hot_state_cp1_regs_simple (0x00000210) -%define offsetof_struct_new_dynarec_hot_state_cp1_regs_double (0x00000290) -%define offsetof_struct_new_dynarec_hot_state_rounding_modes (0x00000318) -%define offsetof_struct_new_dynarec_hot_state_branch_target (0x00000328) -%define offsetof_struct_new_dynarec_hot_state_pc (0x0000032c) -%define offsetof_struct_new_dynarec_hot_state_fake_pc (0x00000330) -%define offsetof_struct_new_dynarec_hot_state_rs (0x000003b8) -%define offsetof_struct_new_dynarec_hot_state_rt (0x000003c0) -%define offsetof_struct_new_dynarec_hot_state_rd (0x000003c8) -%define offsetof_struct_new_dynarec_hot_state_mini_ht (0x000003d4) -%define offsetof_struct_new_dynarec_hot_state_memory_map (0x000004d4) diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/arm64/asm_defines_gas.h b/mupen64plus-core/src/device/r4300/new_dynarec/arm64/asm_defines_gas.h deleted file mode 100644 index a827dc16..00000000 --- a/mupen64plus-core/src/device/r4300/new_dynarec/arm64/asm_defines_gas.h +++ /dev/null @@ -1,35 +0,0 @@ -#define offsetof_struct_device_r4300 (0x00000000) -#define offsetof_struct_r4300_core_cp0 (0x031017e8) -#define offsetof_struct_cp0_last_addr (0x000002a8) -#define offsetof_struct_cp0_count_per_op (0x000002ac) -#define offsetof_struct_cp0_tlb (0x000002b4) -#define offsetof_struct_tlb_entries (0x00000000) -#define offsetof_struct_tlb_LUT_r (0x00000680) -#define offsetof_struct_tlb_LUT_w (0x00400680) -#define offsetof_struct_r4300_core_cached_interp (0x000000e0) -#define offsetof_struct_cached_interp_invalid_code (0x00000000) -#define offsetof_struct_r4300_core_new_dynarec_hot_state (0x02901000) -#define offsetof_struct_r4300_core_extra_memory (0x00901000) -#define offsetof_struct_new_dynarec_hot_state_dynarec_local (0x00000000) -#define offsetof_struct_new_dynarec_hot_state_cycle_count (0x00000100) -#define offsetof_struct_new_dynarec_hot_state_pending_exception (0x00000104) -#define offsetof_struct_new_dynarec_hot_state_pcaddr (0x00000108) -#define offsetof_struct_new_dynarec_hot_state_stop (0x0000010c) -#define offsetof_struct_new_dynarec_hot_state_invc_ptr (0x00000110) -#define offsetof_struct_new_dynarec_hot_state_cp1_fcr0 (0x00000134) -#define offsetof_struct_new_dynarec_hot_state_cp1_fcr31 (0x00000138) -#define offsetof_struct_new_dynarec_hot_state_regs (0x00000140) -#define offsetof_struct_new_dynarec_hot_state_hi (0x00000240) -#define offsetof_struct_new_dynarec_hot_state_lo (0x00000248) -#define offsetof_struct_new_dynarec_hot_state_cp0_regs (0x00000250) -#define offsetof_struct_new_dynarec_hot_state_cp1_regs_simple (0x000002d8) -#define offsetof_struct_new_dynarec_hot_state_cp1_regs_double (0x000003d8) -#define offsetof_struct_new_dynarec_hot_state_rounding_modes (0x000004e0) -#define offsetof_struct_new_dynarec_hot_state_branch_target (0x000004f0) -#define offsetof_struct_new_dynarec_hot_state_pc (0x000004f8) -#define offsetof_struct_new_dynarec_hot_state_fake_pc (0x00000500) -#define offsetof_struct_new_dynarec_hot_state_rs (0x000005c0) -#define offsetof_struct_new_dynarec_hot_state_rt (0x000005c8) -#define offsetof_struct_new_dynarec_hot_state_rd (0x000005d0) -#define offsetof_struct_new_dynarec_hot_state_mini_ht (0x000005e0) -#define offsetof_struct_new_dynarec_hot_state_memory_map (0x000007e0) diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/arm64/asm_defines_nasm.h b/mupen64plus-core/src/device/r4300/new_dynarec/arm64/asm_defines_nasm.h deleted file mode 100644 index 4cb61e1e..00000000 --- a/mupen64plus-core/src/device/r4300/new_dynarec/arm64/asm_defines_nasm.h +++ /dev/null @@ -1,35 +0,0 @@ -%define offsetof_struct_device_r4300 (0x00000000) -%define offsetof_struct_r4300_core_cp0 (0x031017e8) -%define offsetof_struct_cp0_last_addr (0x000002a8) -%define offsetof_struct_cp0_count_per_op (0x000002ac) -%define offsetof_struct_cp0_tlb (0x000002b4) -%define offsetof_struct_tlb_entries (0x00000000) -%define offsetof_struct_tlb_LUT_r (0x00000680) -%define offsetof_struct_tlb_LUT_w (0x00400680) -%define offsetof_struct_r4300_core_cached_interp (0x000000e0) -%define offsetof_struct_cached_interp_invalid_code (0x00000000) -%define offsetof_struct_r4300_core_new_dynarec_hot_state (0x02901000) -%define offsetof_struct_r4300_core_extra_memory (0x00901000) -%define offsetof_struct_new_dynarec_hot_state_dynarec_local (0x00000000) -%define offsetof_struct_new_dynarec_hot_state_cycle_count (0x00000100) -%define offsetof_struct_new_dynarec_hot_state_pending_exception (0x00000104) -%define offsetof_struct_new_dynarec_hot_state_pcaddr (0x00000108) -%define offsetof_struct_new_dynarec_hot_state_stop (0x0000010c) -%define offsetof_struct_new_dynarec_hot_state_invc_ptr (0x00000110) -%define offsetof_struct_new_dynarec_hot_state_cp1_fcr0 (0x00000134) -%define offsetof_struct_new_dynarec_hot_state_cp1_fcr31 (0x00000138) -%define offsetof_struct_new_dynarec_hot_state_regs (0x00000140) -%define offsetof_struct_new_dynarec_hot_state_hi (0x00000240) -%define offsetof_struct_new_dynarec_hot_state_lo (0x00000248) -%define offsetof_struct_new_dynarec_hot_state_cp0_regs (0x00000250) -%define offsetof_struct_new_dynarec_hot_state_cp1_regs_simple (0x000002d8) -%define offsetof_struct_new_dynarec_hot_state_cp1_regs_double (0x000003d8) -%define offsetof_struct_new_dynarec_hot_state_rounding_modes (0x000004e0) -%define offsetof_struct_new_dynarec_hot_state_branch_target (0x000004f0) -%define offsetof_struct_new_dynarec_hot_state_pc (0x000004f8) -%define offsetof_struct_new_dynarec_hot_state_fake_pc (0x00000500) -%define offsetof_struct_new_dynarec_hot_state_rs (0x000005c0) -%define offsetof_struct_new_dynarec_hot_state_rt (0x000005c8) -%define offsetof_struct_new_dynarec_hot_state_rd (0x000005d0) -%define offsetof_struct_new_dynarec_hot_state_mini_ht (0x000005e0) -%define offsetof_struct_new_dynarec_hot_state_memory_map (0x000007e0) diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/x64/asm_defines_gas.h b/mupen64plus-core/src/device/r4300/new_dynarec/x64/asm_defines_gas.h deleted file mode 100644 index 7f5a81fd..00000000 --- a/mupen64plus-core/src/device/r4300/new_dynarec/x64/asm_defines_gas.h +++ /dev/null @@ -1,35 +0,0 @@ -#define offsetof_struct_device_r4300 (0x00000000) -#define offsetof_struct_r4300_core_cp0 (0x031017f8) -#define offsetof_struct_cp0_last_addr (0x000002a8) -#define offsetof_struct_cp0_count_per_op (0x000002ac) -#define offsetof_struct_cp0_tlb (0x000002b4) -#define offsetof_struct_tlb_entries (0x00000000) -#define offsetof_struct_tlb_LUT_r (0x00000680) -#define offsetof_struct_tlb_LUT_w (0x00400680) -#define offsetof_struct_r4300_core_cached_interp (0x000000f0) -#define offsetof_struct_cached_interp_invalid_code (0x00000000) -#define offsetof_struct_r4300_core_new_dynarec_hot_state (0x02901000) -#define offsetof_struct_r4300_core_extra_memory (0x00901000) -#define offsetof_struct_new_dynarec_hot_state_dynarec_local (0x00000000) -#define offsetof_struct_new_dynarec_hot_state_cycle_count (0x00000100) -#define offsetof_struct_new_dynarec_hot_state_pending_exception (0x00000104) -#define offsetof_struct_new_dynarec_hot_state_pcaddr (0x00000108) -#define offsetof_struct_new_dynarec_hot_state_stop (0x0000010c) -#define offsetof_struct_new_dynarec_hot_state_invc_ptr (0x00000110) -#define offsetof_struct_new_dynarec_hot_state_cp1_fcr0 (0x00000134) -#define offsetof_struct_new_dynarec_hot_state_cp1_fcr31 (0x00000138) -#define offsetof_struct_new_dynarec_hot_state_regs (0x00000140) -#define offsetof_struct_new_dynarec_hot_state_hi (0x00000240) -#define offsetof_struct_new_dynarec_hot_state_lo (0x00000248) -#define offsetof_struct_new_dynarec_hot_state_cp0_regs (0x00000250) -#define offsetof_struct_new_dynarec_hot_state_cp1_regs_simple (0x000002d8) -#define offsetof_struct_new_dynarec_hot_state_cp1_regs_double (0x000003d8) -#define offsetof_struct_new_dynarec_hot_state_rounding_modes (0x000004e0) -#define offsetof_struct_new_dynarec_hot_state_branch_target (0x000004f0) -#define offsetof_struct_new_dynarec_hot_state_pc (0x000004f8) -#define offsetof_struct_new_dynarec_hot_state_fake_pc (0x00000500) -#define offsetof_struct_new_dynarec_hot_state_rs (0x000005d0) -#define offsetof_struct_new_dynarec_hot_state_rt (0x000005d8) -#define offsetof_struct_new_dynarec_hot_state_rd (0x000005e0) -#define offsetof_struct_new_dynarec_hot_state_mini_ht (0x000005f0) -#define offsetof_struct_new_dynarec_hot_state_memory_map (0x000007f0) diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/x64/asm_defines_nasm.h b/mupen64plus-core/src/device/r4300/new_dynarec/x64/asm_defines_nasm.h deleted file mode 100644 index a6c0525a..00000000 --- a/mupen64plus-core/src/device/r4300/new_dynarec/x64/asm_defines_nasm.h +++ /dev/null @@ -1,35 +0,0 @@ -%define offsetof_struct_device_r4300 (0x00000000) -%define offsetof_struct_r4300_core_cp0 (0x031017f8) -%define offsetof_struct_cp0_last_addr (0x000002a8) -%define offsetof_struct_cp0_count_per_op (0x000002ac) -%define offsetof_struct_cp0_tlb (0x000002b4) -%define offsetof_struct_tlb_entries (0x00000000) -%define offsetof_struct_tlb_LUT_r (0x00000680) -%define offsetof_struct_tlb_LUT_w (0x00400680) -%define offsetof_struct_r4300_core_cached_interp (0x000000f0) -%define offsetof_struct_cached_interp_invalid_code (0x00000000) -%define offsetof_struct_r4300_core_new_dynarec_hot_state (0x02901000) -%define offsetof_struct_r4300_core_extra_memory (0x00901000) -%define offsetof_struct_new_dynarec_hot_state_dynarec_local (0x00000000) -%define offsetof_struct_new_dynarec_hot_state_cycle_count (0x00000100) -%define offsetof_struct_new_dynarec_hot_state_pending_exception (0x00000104) -%define offsetof_struct_new_dynarec_hot_state_pcaddr (0x00000108) -%define offsetof_struct_new_dynarec_hot_state_stop (0x0000010c) -%define offsetof_struct_new_dynarec_hot_state_invc_ptr (0x00000110) -%define offsetof_struct_new_dynarec_hot_state_cp1_fcr0 (0x00000134) -%define offsetof_struct_new_dynarec_hot_state_cp1_fcr31 (0x00000138) -%define offsetof_struct_new_dynarec_hot_state_regs (0x00000140) -%define offsetof_struct_new_dynarec_hot_state_hi (0x00000240) -%define offsetof_struct_new_dynarec_hot_state_lo (0x00000248) -%define offsetof_struct_new_dynarec_hot_state_cp0_regs (0x00000250) -%define offsetof_struct_new_dynarec_hot_state_cp1_regs_simple (0x000002d8) -%define offsetof_struct_new_dynarec_hot_state_cp1_regs_double (0x000003d8) -%define offsetof_struct_new_dynarec_hot_state_rounding_modes (0x000004e0) -%define offsetof_struct_new_dynarec_hot_state_branch_target (0x000004f0) -%define offsetof_struct_new_dynarec_hot_state_pc (0x000004f8) -%define offsetof_struct_new_dynarec_hot_state_fake_pc (0x00000500) -%define offsetof_struct_new_dynarec_hot_state_rs (0x000005d0) -%define offsetof_struct_new_dynarec_hot_state_rt (0x000005d8) -%define offsetof_struct_new_dynarec_hot_state_rd (0x000005e0) -%define offsetof_struct_new_dynarec_hot_state_mini_ht (0x000005f0) -%define offsetof_struct_new_dynarec_hot_state_memory_map (0x000007f0) diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/x86/asm_defines_gas.h b/mupen64plus-core/src/device/r4300/new_dynarec/x86/asm_defines_gas.h deleted file mode 100644 index 54378d5f..00000000 --- a/mupen64plus-core/src/device/r4300/new_dynarec/x86/asm_defines_gas.h +++ /dev/null @@ -1,35 +0,0 @@ -#define offsetof_struct_device_r4300 (0x00000000) -#define offsetof_struct_r4300_core_cp0 (0x029014d0) -#define offsetof_struct_cp0_last_addr (0x00000194) -#define offsetof_struct_cp0_count_per_op (0x00000198) -#define offsetof_struct_cp0_tlb (0x000001a0) -#define offsetof_struct_tlb_entries (0x00000000) -#define offsetof_struct_tlb_LUT_r (0x00000680) -#define offsetof_struct_tlb_LUT_w (0x00400680) -#define offsetof_struct_r4300_core_cached_interp (0x00000098) -#define offsetof_struct_cached_interp_invalid_code (0x00000000) -#define offsetof_struct_r4300_core_new_dynarec_hot_state (0x02501000) -#define offsetof_struct_r4300_core_extra_memory (0x00501000) -#define offsetof_struct_new_dynarec_hot_state_dynarec_local (0x00000000) -#define offsetof_struct_new_dynarec_hot_state_cycle_count (0x00000040) -#define offsetof_struct_new_dynarec_hot_state_pending_exception (0x00000044) -#define offsetof_struct_new_dynarec_hot_state_pcaddr (0x00000048) -#define offsetof_struct_new_dynarec_hot_state_stop (0x0000004c) -#define offsetof_struct_new_dynarec_hot_state_invc_ptr (0x00000050) -#define offsetof_struct_new_dynarec_hot_state_cp1_fcr0 (0x0000006c) -#define offsetof_struct_new_dynarec_hot_state_cp1_fcr31 (0x00000070) -#define offsetof_struct_new_dynarec_hot_state_regs (0x00000074) -#define offsetof_struct_new_dynarec_hot_state_hi (0x00000174) -#define offsetof_struct_new_dynarec_hot_state_lo (0x0000017c) -#define offsetof_struct_new_dynarec_hot_state_cp0_regs (0x00000184) -#define offsetof_struct_new_dynarec_hot_state_cp1_regs_simple (0x0000020c) -#define offsetof_struct_new_dynarec_hot_state_cp1_regs_double (0x0000028c) -#define offsetof_struct_new_dynarec_hot_state_rounding_modes (0x00000314) -#define offsetof_struct_new_dynarec_hot_state_branch_target (0x00000324) -#define offsetof_struct_new_dynarec_hot_state_pc (0x00000328) -#define offsetof_struct_new_dynarec_hot_state_fake_pc (0x0000032c) -#define offsetof_struct_new_dynarec_hot_state_rs (0x000003b0) -#define offsetof_struct_new_dynarec_hot_state_rt (0x000003b8) -#define offsetof_struct_new_dynarec_hot_state_rd (0x000003c0) -#define offsetof_struct_new_dynarec_hot_state_mini_ht (0x000003cc) -#define offsetof_struct_new_dynarec_hot_state_memory_map (0x000004cc) diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/x86/asm_defines_nasm.h b/mupen64plus-core/src/device/r4300/new_dynarec/x86/asm_defines_nasm.h deleted file mode 100644 index 208aa2f8..00000000 --- a/mupen64plus-core/src/device/r4300/new_dynarec/x86/asm_defines_nasm.h +++ /dev/null @@ -1,35 +0,0 @@ -%define offsetof_struct_device_r4300 (0x00000000) -%define offsetof_struct_r4300_core_cp0 (0x029014d0) -%define offsetof_struct_cp0_last_addr (0x00000194) -%define offsetof_struct_cp0_count_per_op (0x00000198) -%define offsetof_struct_cp0_tlb (0x000001a0) -%define offsetof_struct_tlb_entries (0x00000000) -%define offsetof_struct_tlb_LUT_r (0x00000680) -%define offsetof_struct_tlb_LUT_w (0x00400680) -%define offsetof_struct_r4300_core_cached_interp (0x00000098) -%define offsetof_struct_cached_interp_invalid_code (0x00000000) -%define offsetof_struct_r4300_core_new_dynarec_hot_state (0x02501000) -%define offsetof_struct_r4300_core_extra_memory (0x00501000) -%define offsetof_struct_new_dynarec_hot_state_dynarec_local (0x00000000) -%define offsetof_struct_new_dynarec_hot_state_cycle_count (0x00000040) -%define offsetof_struct_new_dynarec_hot_state_pending_exception (0x00000044) -%define offsetof_struct_new_dynarec_hot_state_pcaddr (0x00000048) -%define offsetof_struct_new_dynarec_hot_state_stop (0x0000004c) -%define offsetof_struct_new_dynarec_hot_state_invc_ptr (0x00000050) -%define offsetof_struct_new_dynarec_hot_state_cp1_fcr0 (0x0000006c) -%define offsetof_struct_new_dynarec_hot_state_cp1_fcr31 (0x00000070) -%define offsetof_struct_new_dynarec_hot_state_regs (0x00000074) -%define offsetof_struct_new_dynarec_hot_state_hi (0x00000174) -%define offsetof_struct_new_dynarec_hot_state_lo (0x0000017c) -%define offsetof_struct_new_dynarec_hot_state_cp0_regs (0x00000184) -%define offsetof_struct_new_dynarec_hot_state_cp1_regs_simple (0x0000020c) -%define offsetof_struct_new_dynarec_hot_state_cp1_regs_double (0x0000028c) -%define offsetof_struct_new_dynarec_hot_state_rounding_modes (0x00000314) -%define offsetof_struct_new_dynarec_hot_state_branch_target (0x00000324) -%define offsetof_struct_new_dynarec_hot_state_pc (0x00000328) -%define offsetof_struct_new_dynarec_hot_state_fake_pc (0x0000032c) -%define offsetof_struct_new_dynarec_hot_state_rs (0x000003b0) -%define offsetof_struct_new_dynarec_hot_state_rt (0x000003b8) -%define offsetof_struct_new_dynarec_hot_state_rd (0x000003c0) -%define offsetof_struct_new_dynarec_hot_state_mini_ht (0x000003cc) -%define offsetof_struct_new_dynarec_hot_state_memory_map (0x000004cc) From 5abd4b6884fbb10bdb181bae65fd94fc7fe25fde Mon Sep 17 00:00:00 2001 From: M4xw Date: Tue, 4 Mar 2025 13:48:12 +0100 Subject: [PATCH 35/58] Rely on HAVE_UNISTD_H --- custom/dependencies/libzlib/zconf.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/custom/dependencies/libzlib/zconf.h b/custom/dependencies/libzlib/zconf.h index 9987a775..215b083f 100644 --- a/custom/dependencies/libzlib/zconf.h +++ b/custom/dependencies/libzlib/zconf.h @@ -408,9 +408,7 @@ typedef uLong FAR uLongf; typedef unsigned long z_crc_t; #endif -#ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */ -# define Z_HAVE_UNISTD_H -#endif +#define Z_HAVE_UNISTD_H #ifdef HAVE_STDARG_H /* may be set to #if 1 by ./configure */ # define Z_HAVE_STDARG_H From 8507239877699fe092edba9725804f97705c1b4c Mon Sep 17 00:00:00 2001 From: M4xw Date: Tue, 4 Mar 2025 13:57:18 +0100 Subject: [PATCH 36/58] Add precompiled headers --- .../r4300/new_dynarec/arm/asm_defines_gas.h | 35 +++++++++++++++++++ .../r4300/new_dynarec/arm/asm_defines_nasm.h | 35 +++++++++++++++++++ .../r4300/new_dynarec/arm64/asm_defines_gas.h | 35 +++++++++++++++++++ .../new_dynarec/arm64/asm_defines_nasm.h | 35 +++++++++++++++++++ .../r4300/new_dynarec/x64/asm_defines_gas.h | 35 +++++++++++++++++++ .../r4300/new_dynarec/x64/asm_defines_nasm.h | 35 +++++++++++++++++++ .../r4300/new_dynarec/x86/asm_defines_gas.h | 35 +++++++++++++++++++ .../r4300/new_dynarec/x86/asm_defines_nasm.h | 35 +++++++++++++++++++ 8 files changed, 280 insertions(+) create mode 100644 mupen64plus-core/src/device/r4300/new_dynarec/arm/asm_defines_gas.h create mode 100644 mupen64plus-core/src/device/r4300/new_dynarec/arm/asm_defines_nasm.h create mode 100644 mupen64plus-core/src/device/r4300/new_dynarec/arm64/asm_defines_gas.h create mode 100644 mupen64plus-core/src/device/r4300/new_dynarec/arm64/asm_defines_nasm.h create mode 100644 mupen64plus-core/src/device/r4300/new_dynarec/x64/asm_defines_gas.h create mode 100644 mupen64plus-core/src/device/r4300/new_dynarec/x64/asm_defines_nasm.h create mode 100644 mupen64plus-core/src/device/r4300/new_dynarec/x86/asm_defines_gas.h create mode 100644 mupen64plus-core/src/device/r4300/new_dynarec/x86/asm_defines_nasm.h diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/arm/asm_defines_gas.h b/mupen64plus-core/src/device/r4300/new_dynarec/arm/asm_defines_gas.h new file mode 100644 index 00000000..7cbf7dd0 --- /dev/null +++ b/mupen64plus-core/src/device/r4300/new_dynarec/arm/asm_defines_gas.h @@ -0,0 +1,35 @@ +#define offsetof_struct_device_r4300 (0x00000000) +#define offsetof_struct_r4300_core_cp0 (0x029014dc) +#define offsetof_struct_cp0_last_addr (0x00000194) +#define offsetof_struct_cp0_count_per_op (0x00000198) +#define offsetof_struct_cp0_tlb (0x000001a0) +#define offsetof_struct_tlb_entries (0x00000000) +#define offsetof_struct_tlb_LUT_r (0x00000680) +#define offsetof_struct_tlb_LUT_w (0x00400680) +#define offsetof_struct_r4300_core_cached_interp (0x00000098) +#define offsetof_struct_cached_interp_invalid_code (0x00000000) +#define offsetof_struct_r4300_core_new_dynarec_hot_state (0x02501000) +#define offsetof_struct_r4300_core_extra_memory (0x00501000) +#define offsetof_struct_new_dynarec_hot_state_dynarec_local (0x00000000) +#define offsetof_struct_new_dynarec_hot_state_cycle_count (0x00000040) +#define offsetof_struct_new_dynarec_hot_state_pending_exception (0x00000044) +#define offsetof_struct_new_dynarec_hot_state_pcaddr (0x00000048) +#define offsetof_struct_new_dynarec_hot_state_stop (0x0000004c) +#define offsetof_struct_new_dynarec_hot_state_invc_ptr (0x00000050) +#define offsetof_struct_new_dynarec_hot_state_cp1_fcr0 (0x0000006c) +#define offsetof_struct_new_dynarec_hot_state_cp1_fcr31 (0x00000070) +#define offsetof_struct_new_dynarec_hot_state_regs (0x00000078) +#define offsetof_struct_new_dynarec_hot_state_hi (0x00000178) +#define offsetof_struct_new_dynarec_hot_state_lo (0x00000180) +#define offsetof_struct_new_dynarec_hot_state_cp0_regs (0x00000188) +#define offsetof_struct_new_dynarec_hot_state_cp1_regs_simple (0x00000210) +#define offsetof_struct_new_dynarec_hot_state_cp1_regs_double (0x00000290) +#define offsetof_struct_new_dynarec_hot_state_rounding_modes (0x00000318) +#define offsetof_struct_new_dynarec_hot_state_branch_target (0x00000328) +#define offsetof_struct_new_dynarec_hot_state_pc (0x0000032c) +#define offsetof_struct_new_dynarec_hot_state_fake_pc (0x00000330) +#define offsetof_struct_new_dynarec_hot_state_rs (0x000003b8) +#define offsetof_struct_new_dynarec_hot_state_rt (0x000003c0) +#define offsetof_struct_new_dynarec_hot_state_rd (0x000003c8) +#define offsetof_struct_new_dynarec_hot_state_mini_ht (0x000003d4) +#define offsetof_struct_new_dynarec_hot_state_memory_map (0x000004d4) diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/arm/asm_defines_nasm.h b/mupen64plus-core/src/device/r4300/new_dynarec/arm/asm_defines_nasm.h new file mode 100644 index 00000000..7b8581fd --- /dev/null +++ b/mupen64plus-core/src/device/r4300/new_dynarec/arm/asm_defines_nasm.h @@ -0,0 +1,35 @@ +%define offsetof_struct_device_r4300 (0x00000000) +%define offsetof_struct_r4300_core_cp0 (0x029014dc) +%define offsetof_struct_cp0_last_addr (0x00000194) +%define offsetof_struct_cp0_count_per_op (0x00000198) +%define offsetof_struct_cp0_tlb (0x000001a0) +%define offsetof_struct_tlb_entries (0x00000000) +%define offsetof_struct_tlb_LUT_r (0x00000680) +%define offsetof_struct_tlb_LUT_w (0x00400680) +%define offsetof_struct_r4300_core_cached_interp (0x00000098) +%define offsetof_struct_cached_interp_invalid_code (0x00000000) +%define offsetof_struct_r4300_core_new_dynarec_hot_state (0x02501000) +%define offsetof_struct_r4300_core_extra_memory (0x00501000) +%define offsetof_struct_new_dynarec_hot_state_dynarec_local (0x00000000) +%define offsetof_struct_new_dynarec_hot_state_cycle_count (0x00000040) +%define offsetof_struct_new_dynarec_hot_state_pending_exception (0x00000044) +%define offsetof_struct_new_dynarec_hot_state_pcaddr (0x00000048) +%define offsetof_struct_new_dynarec_hot_state_stop (0x0000004c) +%define offsetof_struct_new_dynarec_hot_state_invc_ptr (0x00000050) +%define offsetof_struct_new_dynarec_hot_state_cp1_fcr0 (0x0000006c) +%define offsetof_struct_new_dynarec_hot_state_cp1_fcr31 (0x00000070) +%define offsetof_struct_new_dynarec_hot_state_regs (0x00000078) +%define offsetof_struct_new_dynarec_hot_state_hi (0x00000178) +%define offsetof_struct_new_dynarec_hot_state_lo (0x00000180) +%define offsetof_struct_new_dynarec_hot_state_cp0_regs (0x00000188) +%define offsetof_struct_new_dynarec_hot_state_cp1_regs_simple (0x00000210) +%define offsetof_struct_new_dynarec_hot_state_cp1_regs_double (0x00000290) +%define offsetof_struct_new_dynarec_hot_state_rounding_modes (0x00000318) +%define offsetof_struct_new_dynarec_hot_state_branch_target (0x00000328) +%define offsetof_struct_new_dynarec_hot_state_pc (0x0000032c) +%define offsetof_struct_new_dynarec_hot_state_fake_pc (0x00000330) +%define offsetof_struct_new_dynarec_hot_state_rs (0x000003b8) +%define offsetof_struct_new_dynarec_hot_state_rt (0x000003c0) +%define offsetof_struct_new_dynarec_hot_state_rd (0x000003c8) +%define offsetof_struct_new_dynarec_hot_state_mini_ht (0x000003d4) +%define offsetof_struct_new_dynarec_hot_state_memory_map (0x000004d4) diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/arm64/asm_defines_gas.h b/mupen64plus-core/src/device/r4300/new_dynarec/arm64/asm_defines_gas.h new file mode 100644 index 00000000..a827dc16 --- /dev/null +++ b/mupen64plus-core/src/device/r4300/new_dynarec/arm64/asm_defines_gas.h @@ -0,0 +1,35 @@ +#define offsetof_struct_device_r4300 (0x00000000) +#define offsetof_struct_r4300_core_cp0 (0x031017e8) +#define offsetof_struct_cp0_last_addr (0x000002a8) +#define offsetof_struct_cp0_count_per_op (0x000002ac) +#define offsetof_struct_cp0_tlb (0x000002b4) +#define offsetof_struct_tlb_entries (0x00000000) +#define offsetof_struct_tlb_LUT_r (0x00000680) +#define offsetof_struct_tlb_LUT_w (0x00400680) +#define offsetof_struct_r4300_core_cached_interp (0x000000e0) +#define offsetof_struct_cached_interp_invalid_code (0x00000000) +#define offsetof_struct_r4300_core_new_dynarec_hot_state (0x02901000) +#define offsetof_struct_r4300_core_extra_memory (0x00901000) +#define offsetof_struct_new_dynarec_hot_state_dynarec_local (0x00000000) +#define offsetof_struct_new_dynarec_hot_state_cycle_count (0x00000100) +#define offsetof_struct_new_dynarec_hot_state_pending_exception (0x00000104) +#define offsetof_struct_new_dynarec_hot_state_pcaddr (0x00000108) +#define offsetof_struct_new_dynarec_hot_state_stop (0x0000010c) +#define offsetof_struct_new_dynarec_hot_state_invc_ptr (0x00000110) +#define offsetof_struct_new_dynarec_hot_state_cp1_fcr0 (0x00000134) +#define offsetof_struct_new_dynarec_hot_state_cp1_fcr31 (0x00000138) +#define offsetof_struct_new_dynarec_hot_state_regs (0x00000140) +#define offsetof_struct_new_dynarec_hot_state_hi (0x00000240) +#define offsetof_struct_new_dynarec_hot_state_lo (0x00000248) +#define offsetof_struct_new_dynarec_hot_state_cp0_regs (0x00000250) +#define offsetof_struct_new_dynarec_hot_state_cp1_regs_simple (0x000002d8) +#define offsetof_struct_new_dynarec_hot_state_cp1_regs_double (0x000003d8) +#define offsetof_struct_new_dynarec_hot_state_rounding_modes (0x000004e0) +#define offsetof_struct_new_dynarec_hot_state_branch_target (0x000004f0) +#define offsetof_struct_new_dynarec_hot_state_pc (0x000004f8) +#define offsetof_struct_new_dynarec_hot_state_fake_pc (0x00000500) +#define offsetof_struct_new_dynarec_hot_state_rs (0x000005c0) +#define offsetof_struct_new_dynarec_hot_state_rt (0x000005c8) +#define offsetof_struct_new_dynarec_hot_state_rd (0x000005d0) +#define offsetof_struct_new_dynarec_hot_state_mini_ht (0x000005e0) +#define offsetof_struct_new_dynarec_hot_state_memory_map (0x000007e0) diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/arm64/asm_defines_nasm.h b/mupen64plus-core/src/device/r4300/new_dynarec/arm64/asm_defines_nasm.h new file mode 100644 index 00000000..4cb61e1e --- /dev/null +++ b/mupen64plus-core/src/device/r4300/new_dynarec/arm64/asm_defines_nasm.h @@ -0,0 +1,35 @@ +%define offsetof_struct_device_r4300 (0x00000000) +%define offsetof_struct_r4300_core_cp0 (0x031017e8) +%define offsetof_struct_cp0_last_addr (0x000002a8) +%define offsetof_struct_cp0_count_per_op (0x000002ac) +%define offsetof_struct_cp0_tlb (0x000002b4) +%define offsetof_struct_tlb_entries (0x00000000) +%define offsetof_struct_tlb_LUT_r (0x00000680) +%define offsetof_struct_tlb_LUT_w (0x00400680) +%define offsetof_struct_r4300_core_cached_interp (0x000000e0) +%define offsetof_struct_cached_interp_invalid_code (0x00000000) +%define offsetof_struct_r4300_core_new_dynarec_hot_state (0x02901000) +%define offsetof_struct_r4300_core_extra_memory (0x00901000) +%define offsetof_struct_new_dynarec_hot_state_dynarec_local (0x00000000) +%define offsetof_struct_new_dynarec_hot_state_cycle_count (0x00000100) +%define offsetof_struct_new_dynarec_hot_state_pending_exception (0x00000104) +%define offsetof_struct_new_dynarec_hot_state_pcaddr (0x00000108) +%define offsetof_struct_new_dynarec_hot_state_stop (0x0000010c) +%define offsetof_struct_new_dynarec_hot_state_invc_ptr (0x00000110) +%define offsetof_struct_new_dynarec_hot_state_cp1_fcr0 (0x00000134) +%define offsetof_struct_new_dynarec_hot_state_cp1_fcr31 (0x00000138) +%define offsetof_struct_new_dynarec_hot_state_regs (0x00000140) +%define offsetof_struct_new_dynarec_hot_state_hi (0x00000240) +%define offsetof_struct_new_dynarec_hot_state_lo (0x00000248) +%define offsetof_struct_new_dynarec_hot_state_cp0_regs (0x00000250) +%define offsetof_struct_new_dynarec_hot_state_cp1_regs_simple (0x000002d8) +%define offsetof_struct_new_dynarec_hot_state_cp1_regs_double (0x000003d8) +%define offsetof_struct_new_dynarec_hot_state_rounding_modes (0x000004e0) +%define offsetof_struct_new_dynarec_hot_state_branch_target (0x000004f0) +%define offsetof_struct_new_dynarec_hot_state_pc (0x000004f8) +%define offsetof_struct_new_dynarec_hot_state_fake_pc (0x00000500) +%define offsetof_struct_new_dynarec_hot_state_rs (0x000005c0) +%define offsetof_struct_new_dynarec_hot_state_rt (0x000005c8) +%define offsetof_struct_new_dynarec_hot_state_rd (0x000005d0) +%define offsetof_struct_new_dynarec_hot_state_mini_ht (0x000005e0) +%define offsetof_struct_new_dynarec_hot_state_memory_map (0x000007e0) diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/x64/asm_defines_gas.h b/mupen64plus-core/src/device/r4300/new_dynarec/x64/asm_defines_gas.h new file mode 100644 index 00000000..7f5a81fd --- /dev/null +++ b/mupen64plus-core/src/device/r4300/new_dynarec/x64/asm_defines_gas.h @@ -0,0 +1,35 @@ +#define offsetof_struct_device_r4300 (0x00000000) +#define offsetof_struct_r4300_core_cp0 (0x031017f8) +#define offsetof_struct_cp0_last_addr (0x000002a8) +#define offsetof_struct_cp0_count_per_op (0x000002ac) +#define offsetof_struct_cp0_tlb (0x000002b4) +#define offsetof_struct_tlb_entries (0x00000000) +#define offsetof_struct_tlb_LUT_r (0x00000680) +#define offsetof_struct_tlb_LUT_w (0x00400680) +#define offsetof_struct_r4300_core_cached_interp (0x000000f0) +#define offsetof_struct_cached_interp_invalid_code (0x00000000) +#define offsetof_struct_r4300_core_new_dynarec_hot_state (0x02901000) +#define offsetof_struct_r4300_core_extra_memory (0x00901000) +#define offsetof_struct_new_dynarec_hot_state_dynarec_local (0x00000000) +#define offsetof_struct_new_dynarec_hot_state_cycle_count (0x00000100) +#define offsetof_struct_new_dynarec_hot_state_pending_exception (0x00000104) +#define offsetof_struct_new_dynarec_hot_state_pcaddr (0x00000108) +#define offsetof_struct_new_dynarec_hot_state_stop (0x0000010c) +#define offsetof_struct_new_dynarec_hot_state_invc_ptr (0x00000110) +#define offsetof_struct_new_dynarec_hot_state_cp1_fcr0 (0x00000134) +#define offsetof_struct_new_dynarec_hot_state_cp1_fcr31 (0x00000138) +#define offsetof_struct_new_dynarec_hot_state_regs (0x00000140) +#define offsetof_struct_new_dynarec_hot_state_hi (0x00000240) +#define offsetof_struct_new_dynarec_hot_state_lo (0x00000248) +#define offsetof_struct_new_dynarec_hot_state_cp0_regs (0x00000250) +#define offsetof_struct_new_dynarec_hot_state_cp1_regs_simple (0x000002d8) +#define offsetof_struct_new_dynarec_hot_state_cp1_regs_double (0x000003d8) +#define offsetof_struct_new_dynarec_hot_state_rounding_modes (0x000004e0) +#define offsetof_struct_new_dynarec_hot_state_branch_target (0x000004f0) +#define offsetof_struct_new_dynarec_hot_state_pc (0x000004f8) +#define offsetof_struct_new_dynarec_hot_state_fake_pc (0x00000500) +#define offsetof_struct_new_dynarec_hot_state_rs (0x000005d0) +#define offsetof_struct_new_dynarec_hot_state_rt (0x000005d8) +#define offsetof_struct_new_dynarec_hot_state_rd (0x000005e0) +#define offsetof_struct_new_dynarec_hot_state_mini_ht (0x000005f0) +#define offsetof_struct_new_dynarec_hot_state_memory_map (0x000007f0) diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/x64/asm_defines_nasm.h b/mupen64plus-core/src/device/r4300/new_dynarec/x64/asm_defines_nasm.h new file mode 100644 index 00000000..a6c0525a --- /dev/null +++ b/mupen64plus-core/src/device/r4300/new_dynarec/x64/asm_defines_nasm.h @@ -0,0 +1,35 @@ +%define offsetof_struct_device_r4300 (0x00000000) +%define offsetof_struct_r4300_core_cp0 (0x031017f8) +%define offsetof_struct_cp0_last_addr (0x000002a8) +%define offsetof_struct_cp0_count_per_op (0x000002ac) +%define offsetof_struct_cp0_tlb (0x000002b4) +%define offsetof_struct_tlb_entries (0x00000000) +%define offsetof_struct_tlb_LUT_r (0x00000680) +%define offsetof_struct_tlb_LUT_w (0x00400680) +%define offsetof_struct_r4300_core_cached_interp (0x000000f0) +%define offsetof_struct_cached_interp_invalid_code (0x00000000) +%define offsetof_struct_r4300_core_new_dynarec_hot_state (0x02901000) +%define offsetof_struct_r4300_core_extra_memory (0x00901000) +%define offsetof_struct_new_dynarec_hot_state_dynarec_local (0x00000000) +%define offsetof_struct_new_dynarec_hot_state_cycle_count (0x00000100) +%define offsetof_struct_new_dynarec_hot_state_pending_exception (0x00000104) +%define offsetof_struct_new_dynarec_hot_state_pcaddr (0x00000108) +%define offsetof_struct_new_dynarec_hot_state_stop (0x0000010c) +%define offsetof_struct_new_dynarec_hot_state_invc_ptr (0x00000110) +%define offsetof_struct_new_dynarec_hot_state_cp1_fcr0 (0x00000134) +%define offsetof_struct_new_dynarec_hot_state_cp1_fcr31 (0x00000138) +%define offsetof_struct_new_dynarec_hot_state_regs (0x00000140) +%define offsetof_struct_new_dynarec_hot_state_hi (0x00000240) +%define offsetof_struct_new_dynarec_hot_state_lo (0x00000248) +%define offsetof_struct_new_dynarec_hot_state_cp0_regs (0x00000250) +%define offsetof_struct_new_dynarec_hot_state_cp1_regs_simple (0x000002d8) +%define offsetof_struct_new_dynarec_hot_state_cp1_regs_double (0x000003d8) +%define offsetof_struct_new_dynarec_hot_state_rounding_modes (0x000004e0) +%define offsetof_struct_new_dynarec_hot_state_branch_target (0x000004f0) +%define offsetof_struct_new_dynarec_hot_state_pc (0x000004f8) +%define offsetof_struct_new_dynarec_hot_state_fake_pc (0x00000500) +%define offsetof_struct_new_dynarec_hot_state_rs (0x000005d0) +%define offsetof_struct_new_dynarec_hot_state_rt (0x000005d8) +%define offsetof_struct_new_dynarec_hot_state_rd (0x000005e0) +%define offsetof_struct_new_dynarec_hot_state_mini_ht (0x000005f0) +%define offsetof_struct_new_dynarec_hot_state_memory_map (0x000007f0) diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/x86/asm_defines_gas.h b/mupen64plus-core/src/device/r4300/new_dynarec/x86/asm_defines_gas.h new file mode 100644 index 00000000..54378d5f --- /dev/null +++ b/mupen64plus-core/src/device/r4300/new_dynarec/x86/asm_defines_gas.h @@ -0,0 +1,35 @@ +#define offsetof_struct_device_r4300 (0x00000000) +#define offsetof_struct_r4300_core_cp0 (0x029014d0) +#define offsetof_struct_cp0_last_addr (0x00000194) +#define offsetof_struct_cp0_count_per_op (0x00000198) +#define offsetof_struct_cp0_tlb (0x000001a0) +#define offsetof_struct_tlb_entries (0x00000000) +#define offsetof_struct_tlb_LUT_r (0x00000680) +#define offsetof_struct_tlb_LUT_w (0x00400680) +#define offsetof_struct_r4300_core_cached_interp (0x00000098) +#define offsetof_struct_cached_interp_invalid_code (0x00000000) +#define offsetof_struct_r4300_core_new_dynarec_hot_state (0x02501000) +#define offsetof_struct_r4300_core_extra_memory (0x00501000) +#define offsetof_struct_new_dynarec_hot_state_dynarec_local (0x00000000) +#define offsetof_struct_new_dynarec_hot_state_cycle_count (0x00000040) +#define offsetof_struct_new_dynarec_hot_state_pending_exception (0x00000044) +#define offsetof_struct_new_dynarec_hot_state_pcaddr (0x00000048) +#define offsetof_struct_new_dynarec_hot_state_stop (0x0000004c) +#define offsetof_struct_new_dynarec_hot_state_invc_ptr (0x00000050) +#define offsetof_struct_new_dynarec_hot_state_cp1_fcr0 (0x0000006c) +#define offsetof_struct_new_dynarec_hot_state_cp1_fcr31 (0x00000070) +#define offsetof_struct_new_dynarec_hot_state_regs (0x00000074) +#define offsetof_struct_new_dynarec_hot_state_hi (0x00000174) +#define offsetof_struct_new_dynarec_hot_state_lo (0x0000017c) +#define offsetof_struct_new_dynarec_hot_state_cp0_regs (0x00000184) +#define offsetof_struct_new_dynarec_hot_state_cp1_regs_simple (0x0000020c) +#define offsetof_struct_new_dynarec_hot_state_cp1_regs_double (0x0000028c) +#define offsetof_struct_new_dynarec_hot_state_rounding_modes (0x00000314) +#define offsetof_struct_new_dynarec_hot_state_branch_target (0x00000324) +#define offsetof_struct_new_dynarec_hot_state_pc (0x00000328) +#define offsetof_struct_new_dynarec_hot_state_fake_pc (0x0000032c) +#define offsetof_struct_new_dynarec_hot_state_rs (0x000003b0) +#define offsetof_struct_new_dynarec_hot_state_rt (0x000003b8) +#define offsetof_struct_new_dynarec_hot_state_rd (0x000003c0) +#define offsetof_struct_new_dynarec_hot_state_mini_ht (0x000003cc) +#define offsetof_struct_new_dynarec_hot_state_memory_map (0x000004cc) diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/x86/asm_defines_nasm.h b/mupen64plus-core/src/device/r4300/new_dynarec/x86/asm_defines_nasm.h new file mode 100644 index 00000000..208aa2f8 --- /dev/null +++ b/mupen64plus-core/src/device/r4300/new_dynarec/x86/asm_defines_nasm.h @@ -0,0 +1,35 @@ +%define offsetof_struct_device_r4300 (0x00000000) +%define offsetof_struct_r4300_core_cp0 (0x029014d0) +%define offsetof_struct_cp0_last_addr (0x00000194) +%define offsetof_struct_cp0_count_per_op (0x00000198) +%define offsetof_struct_cp0_tlb (0x000001a0) +%define offsetof_struct_tlb_entries (0x00000000) +%define offsetof_struct_tlb_LUT_r (0x00000680) +%define offsetof_struct_tlb_LUT_w (0x00400680) +%define offsetof_struct_r4300_core_cached_interp (0x00000098) +%define offsetof_struct_cached_interp_invalid_code (0x00000000) +%define offsetof_struct_r4300_core_new_dynarec_hot_state (0x02501000) +%define offsetof_struct_r4300_core_extra_memory (0x00501000) +%define offsetof_struct_new_dynarec_hot_state_dynarec_local (0x00000000) +%define offsetof_struct_new_dynarec_hot_state_cycle_count (0x00000040) +%define offsetof_struct_new_dynarec_hot_state_pending_exception (0x00000044) +%define offsetof_struct_new_dynarec_hot_state_pcaddr (0x00000048) +%define offsetof_struct_new_dynarec_hot_state_stop (0x0000004c) +%define offsetof_struct_new_dynarec_hot_state_invc_ptr (0x00000050) +%define offsetof_struct_new_dynarec_hot_state_cp1_fcr0 (0x0000006c) +%define offsetof_struct_new_dynarec_hot_state_cp1_fcr31 (0x00000070) +%define offsetof_struct_new_dynarec_hot_state_regs (0x00000074) +%define offsetof_struct_new_dynarec_hot_state_hi (0x00000174) +%define offsetof_struct_new_dynarec_hot_state_lo (0x0000017c) +%define offsetof_struct_new_dynarec_hot_state_cp0_regs (0x00000184) +%define offsetof_struct_new_dynarec_hot_state_cp1_regs_simple (0x0000020c) +%define offsetof_struct_new_dynarec_hot_state_cp1_regs_double (0x0000028c) +%define offsetof_struct_new_dynarec_hot_state_rounding_modes (0x00000314) +%define offsetof_struct_new_dynarec_hot_state_branch_target (0x00000324) +%define offsetof_struct_new_dynarec_hot_state_pc (0x00000328) +%define offsetof_struct_new_dynarec_hot_state_fake_pc (0x0000032c) +%define offsetof_struct_new_dynarec_hot_state_rs (0x000003b0) +%define offsetof_struct_new_dynarec_hot_state_rt (0x000003b8) +%define offsetof_struct_new_dynarec_hot_state_rd (0x000003c0) +%define offsetof_struct_new_dynarec_hot_state_mini_ht (0x000003cc) +%define offsetof_struct_new_dynarec_hot_state_memory_map (0x000004cc) From 3fb891daed7661bf7314e0b8c8c52b678eeb3ba6 Mon Sep 17 00:00:00 2001 From: M4xw Date: Tue, 4 Mar 2025 14:01:29 +0100 Subject: [PATCH 37/58] Bump ver --- libretro/libretro.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libretro/libretro.c b/libretro/libretro.c index a0a9f041..3df19d2e 100644 --- a/libretro/libretro.c +++ b/libretro/libretro.c @@ -651,7 +651,7 @@ void retro_set_environment(retro_environment_t cb) void retro_get_system_info(struct retro_system_info *info) { info->library_name = "Mupen64Plus-Next"; - info->library_version = "2.7" FLAVOUR_VERSION GIT_VERSION; + info->library_version = "2.8" FLAVOUR_VERSION GIT_VERSION; info->valid_extensions = "n64|v64|z64|bin|u1"; info->need_fullpath = false; info->block_extract = false; From 7c7f11061f29b2ccd5d0952e2373ae5b55cfea8f Mon Sep 17 00:00:00 2001 From: Bobby Smith <33353403+bslenul@users.noreply.github.com> Date: Wed, 22 Jan 2025 14:42:30 +0100 Subject: [PATCH 38/58] Apply controllers options on-the-fly --- .../emulate_game_controller_via_libretro.c | 2 +- libretro/libretro.c | 201 +++++++++--------- 2 files changed, 106 insertions(+), 97 deletions(-) diff --git a/custom/mupen64plus-core/plugin/emulate_game_controller_via_libretro.c b/custom/mupen64plus-core/plugin/emulate_game_controller_via_libretro.c index 45cd2c12..8a132d4a 100644 --- a/custom/mupen64plus-core/plugin/emulate_game_controller_via_libretro.c +++ b/custom/mupen64plus-core/plugin/emulate_game_controller_via_libretro.c @@ -73,7 +73,7 @@ void inputGetKeys_default( int Control, BUTTONS *Keys ); typedef void (*get_keys_t)(int, BUTTONS*); static get_keys_t getKeys = inputGetKeys_default; -static void inputGetKeys_default_descriptor(void) +void inputGetKeys_default_descriptor(void) { if (alternate_mapping) { diff --git a/libretro/libretro.c b/libretro/libretro.c index 3df19d2e..c4f570f3 100644 --- a/libretro/libretro.c +++ b/libretro/libretro.c @@ -72,6 +72,7 @@ #define ISHEXDEC ((codeLine[cursor]>='0') && (codeLine[cursor]<='9')) || ((codeLine[cursor]>='a') && (codeLine[cursor]<='f')) || ((codeLine[cursor]>='A') && (codeLine[cursor]<='F')) /* Forward declarations */ +void inputGetKeys_default_descriptor(void); #ifdef HAVE_THR_AL void angrylion_set_filtering(unsigned filter_type); void angrylion_set_vi_blur(unsigned value); @@ -756,15 +757,18 @@ void retro_deinit(void) void update_controllers() { - struct retro_variable pk1var = { CORE_NAME "-pak1" }; - if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &pk1var) && pk1var.value) + struct retro_variable var; + + var.key = CORE_NAME "-pak1"; + var.value = NULL; + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) { int p1_pak = PLUGIN_NONE; - if (!strcmp(pk1var.value, "rumble")) + if (!strcmp(var.value, "rumble")) p1_pak = PLUGIN_RAW; - else if (!strcmp(pk1var.value, "memory")) + else if (!strcmp(var.value, "memory")) p1_pak = PLUGIN_MEMPAK; - else if (!strcmp(pk1var.value, "transfer")) + else if (!strcmp(var.value, "transfer")) p1_pak = PLUGIN_TRANSFER_PAK; // If controller struct is not initialised yet, set pad_pak_types instead @@ -775,15 +779,16 @@ void update_controllers() pad_pak_types[0] = p1_pak; } - struct retro_variable pk2var = { CORE_NAME "-pak2" }; - if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &pk2var) && pk2var.value) + var.key = CORE_NAME "-pak2"; + var.value = NULL; + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) { int p2_pak = PLUGIN_NONE; - if (!strcmp(pk2var.value, "rumble")) + if (!strcmp(var.value, "rumble")) p2_pak = PLUGIN_RAW; - else if (!strcmp(pk2var.value, "memory")) + else if (!strcmp(var.value, "memory")) p2_pak = PLUGIN_MEMPAK; - else if (!strcmp(pk2var.value, "transfer")) + else if (!strcmp(var.value, "transfer")) p2_pak = PLUGIN_TRANSFER_PAK; if (controller[1].control) @@ -792,15 +797,16 @@ void update_controllers() pad_pak_types[1] = p2_pak; } - struct retro_variable pk3var = { CORE_NAME "-pak3" }; - if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &pk3var) && pk3var.value) + var.key = CORE_NAME "-pak3"; + var.value = NULL; + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) { int p3_pak = PLUGIN_NONE; - if (!strcmp(pk3var.value, "rumble")) + if (!strcmp(var.value, "rumble")) p3_pak = PLUGIN_RAW; - else if (!strcmp(pk3var.value, "memory")) + else if (!strcmp(var.value, "memory")) p3_pak = PLUGIN_MEMPAK; - else if (!strcmp(pk3var.value, "transfer")) + else if (!strcmp(var.value, "transfer")) p3_pak = PLUGIN_TRANSFER_PAK; if (controller[2].control) @@ -809,15 +815,16 @@ void update_controllers() pad_pak_types[2] = p3_pak; } - struct retro_variable pk4var = { CORE_NAME "-pak4" }; - if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &pk4var) && pk4var.value) + var.key = CORE_NAME "-pak4"; + var.value = NULL; + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) { int p4_pak = PLUGIN_NONE; - if (!strcmp(pk4var.value, "rumble")) + if (!strcmp(var.value, "rumble")) p4_pak = PLUGIN_RAW; - else if (!strcmp(pk4var.value, "memory")) + else if (!strcmp(var.value, "memory")) p4_pak = PLUGIN_MEMPAK; - else if (!strcmp(pk4var.value, "transfer")) + else if (!strcmp(var.value, "transfer")) p4_pak = PLUGIN_TRANSFER_PAK; if (controller[3].control) @@ -825,6 +832,83 @@ void update_controllers() else pad_pak_types[3] = p4_pak; } + + var.key = CORE_NAME "-astick-deadzone"; + var.value = NULL; + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) + astick_deadzone = (int)(atoi(var.value) * 0.01f * 0x8000); + + var.key = CORE_NAME "-astick-sensitivity"; + var.value = NULL; + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) + astick_sensitivity = atoi(var.value); + + var.key = CORE_NAME "-r-cbutton"; + var.value = NULL; + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) + { + if (!strcmp(var.value, "C1")) + r_cbutton = RETRO_DEVICE_ID_JOYPAD_A; + else if (!strcmp(var.value, "C2")) + r_cbutton = RETRO_DEVICE_ID_JOYPAD_Y; + else if (!strcmp(var.value, "C3")) + r_cbutton = RETRO_DEVICE_ID_JOYPAD_B; + else if (!strcmp(var.value, "C4")) + r_cbutton = RETRO_DEVICE_ID_JOYPAD_X; + } + + var.key = CORE_NAME "-l-cbutton"; + var.value = NULL; + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) + { + if (!strcmp(var.value, "C1")) + l_cbutton = RETRO_DEVICE_ID_JOYPAD_A; + else if (!strcmp(var.value, "C2")) + l_cbutton = RETRO_DEVICE_ID_JOYPAD_Y; + else if (!strcmp(var.value, "C3")) + l_cbutton = RETRO_DEVICE_ID_JOYPAD_B; + else if (!strcmp(var.value, "C4")) + l_cbutton = RETRO_DEVICE_ID_JOYPAD_X; + } + + var.key = CORE_NAME "-d-cbutton"; + var.value = NULL; + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) + { + if (!strcmp(var.value, "C1")) + d_cbutton = RETRO_DEVICE_ID_JOYPAD_A; + else if (!strcmp(var.value, "C2")) + d_cbutton = RETRO_DEVICE_ID_JOYPAD_Y; + else if (!strcmp(var.value, "C3")) + d_cbutton = RETRO_DEVICE_ID_JOYPAD_B; + else if (!strcmp(var.value, "C4")) + d_cbutton = RETRO_DEVICE_ID_JOYPAD_X; + } + + var.key = CORE_NAME "-u-cbutton"; + var.value = NULL; + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) + { + if (!strcmp(var.value, "C1")) + u_cbutton = RETRO_DEVICE_ID_JOYPAD_A; + else if (!strcmp(var.value, "C2")) + u_cbutton = RETRO_DEVICE_ID_JOYPAD_Y; + else if (!strcmp(var.value, "C3")) + u_cbutton = RETRO_DEVICE_ID_JOYPAD_B; + else if (!strcmp(var.value, "C4")) + u_cbutton = RETRO_DEVICE_ID_JOYPAD_X; + } + + var.key = CORE_NAME "-alt-map"; + var.value = NULL; + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) + { + bool alternate_mapping_prev = alternate_mapping; + alternate_mapping = !strcmp(var.value, "False") ? 0 : 1; + + if (alternate_mapping != alternate_mapping_prev) + inputGetKeys_default_descriptor(); + } } static void update_variables(bool startup) @@ -1374,16 +1458,6 @@ static void update_variables(bool startup) } #endif // HAVE_THR_AL - var.key = CORE_NAME "-astick-deadzone"; - var.value = NULL; - if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) - astick_deadzone = (int)(atoi(var.value) * 0.01f * 0x8000); - - var.key = CORE_NAME "-astick-sensitivity"; - var.value = NULL; - if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) - astick_sensitivity = atoi(var.value); - var.key = CORE_NAME "-CountPerOp"; var.value = NULL; if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) @@ -1414,62 +1488,6 @@ static void update_variables(bool startup) } #endif // HAVE_THR_AL - var.key = CORE_NAME "-r-cbutton"; - var.value = NULL; - if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) - { - if (!strcmp(var.value, "C1")) - r_cbutton = RETRO_DEVICE_ID_JOYPAD_A; - else if (!strcmp(var.value, "C2")) - r_cbutton = RETRO_DEVICE_ID_JOYPAD_Y; - else if (!strcmp(var.value, "C3")) - r_cbutton = RETRO_DEVICE_ID_JOYPAD_B; - else if (!strcmp(var.value, "C4")) - r_cbutton = RETRO_DEVICE_ID_JOYPAD_X; - } - - var.key = CORE_NAME "-l-cbutton"; - var.value = NULL; - if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) - { - if (!strcmp(var.value, "C1")) - l_cbutton = RETRO_DEVICE_ID_JOYPAD_A; - else if (!strcmp(var.value, "C2")) - l_cbutton = RETRO_DEVICE_ID_JOYPAD_Y; - else if (!strcmp(var.value, "C3")) - l_cbutton = RETRO_DEVICE_ID_JOYPAD_B; - else if (!strcmp(var.value, "C4")) - l_cbutton = RETRO_DEVICE_ID_JOYPAD_X; - } - - var.key = CORE_NAME "-d-cbutton"; - var.value = NULL; - if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) - { - if (!strcmp(var.value, "C1")) - d_cbutton = RETRO_DEVICE_ID_JOYPAD_A; - else if (!strcmp(var.value, "C2")) - d_cbutton = RETRO_DEVICE_ID_JOYPAD_Y; - else if (!strcmp(var.value, "C3")) - d_cbutton = RETRO_DEVICE_ID_JOYPAD_B; - else if (!strcmp(var.value, "C4")) - d_cbutton = RETRO_DEVICE_ID_JOYPAD_X; - } - - var.key = CORE_NAME "-u-cbutton"; - var.value = NULL; - if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) - { - if (!strcmp(var.value, "C1")) - u_cbutton = RETRO_DEVICE_ID_JOYPAD_A; - else if (!strcmp(var.value, "C2")) - u_cbutton = RETRO_DEVICE_ID_JOYPAD_Y; - else if (!strcmp(var.value, "C3")) - u_cbutton = RETRO_DEVICE_ID_JOYPAD_B; - else if (!strcmp(var.value, "C4")) - u_cbutton = RETRO_DEVICE_ID_JOYPAD_X; - } - var.key = CORE_NAME "-EnableOverscan"; var.value = NULL; if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) @@ -1505,13 +1523,6 @@ static void update_variables(bool startup) OverscanBottom = atoi(var.value); } - var.key = CORE_NAME "-alt-map"; - var.value = NULL; - if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) - { - alternate_mapping = !strcmp(var.value, "False") ? 0 : 1; - } - var.key = CORE_NAME "-ForceDisableExtraMem"; var.value = NULL; if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) @@ -2025,10 +2036,8 @@ void retro_run (void) libretro_swap_buffer = false; static bool updated = false; - if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated) { + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated) update_variables(false); - update_controllers(); - } if(current_rdp_type == RDP_PLUGIN_GLIDEN64) { From 7c354c2f1b111b823e6784a3df7d637f8d6b8537 Mon Sep 17 00:00:00 2001 From: Coolcord Date: Sun, 1 Jun 2025 02:43:18 -0700 Subject: [PATCH 39/58] Added 5120x1440 and 7680x2160 as 32:9 widescreen resolutions to libretro --- libretro/libretro_core_options.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libretro/libretro_core_options.h b/libretro/libretro_core_options.h index 39a93ec3..229709a8 100644 --- a/libretro/libretro_core_options.h +++ b/libretro/libretro_core_options.h @@ -128,9 +128,11 @@ struct retro_core_option_v2_definition option_defs_us[] = { {"3414x1440", "3414x1440 (64:27)"}, {"3840x2160", "3840x2160 (16:9)"}, {"4096x2160", "4096x2160 (17:9)"}, + {"5120x1440", "5120x1440 (32:9)"}, {"5120x2160", "5120x2160 (64:27)"}, {"7680x3240", "7680x3240 (64:27)"}, {"7680x4320", "7680x4320 (16:9)"}, + {"7680x2160", "7680x2160 (32:9)"}, {"10240x4320", "10240x4320 (64:27)"}, { NULL, NULL }, }, From 0346d3ef26016bb4de604cf918d004bd791447ee Mon Sep 17 00:00:00 2001 From: Bobby Smith <33353403+bslenul@users.noreply.github.com> Date: Wed, 22 Jan 2025 16:53:42 +0100 Subject: [PATCH 40/58] Hide "EnableCopyColorFromRDRAM" for non-GLideN64 plugins --- libretro/libretro_core_options.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libretro/libretro_core_options.h b/libretro/libretro_core_options.h index 229709a8..fb39aa98 100644 --- a/libretro/libretro_core_options.h +++ b/libretro/libretro_core_options.h @@ -365,8 +365,8 @@ struct retro_core_option_v2_definition option_defs_us[] = { CORE_NAME "-EnableCopyColorFromRDRAM", "Enable color buffer copy from RDRAM", NULL, - NULL, - NULL, + "(GLN64) Enable color buffer copy from RDRAM.", + "Enable color buffer copy from RDRAM.", "gliden64", { {"False", NULL}, From 1b693cdac7c42979f6ef53ffe260a76454f0cf45 Mon Sep 17 00:00:00 2001 From: m4xw Date: Tue, 1 Jul 2025 18:04:53 +0200 Subject: [PATCH 41/58] Temp Backport Disable dual-source blending --- GLideN64/src/Graphics/OpenGLContext/opengl_GLInfo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GLideN64/src/Graphics/OpenGLContext/opengl_GLInfo.cpp b/GLideN64/src/Graphics/OpenGLContext/opengl_GLInfo.cpp index 55e42ddb..abbd382c 100644 --- a/GLideN64/src/Graphics/OpenGLContext/opengl_GLInfo.cpp +++ b/GLideN64/src/Graphics/OpenGLContext/opengl_GLInfo.cpp @@ -202,7 +202,7 @@ void GLInfo::init() { eglImage = (Utils::isEGLExtensionSupported("EGL_KHR_image_base") || Utils::isEGLExtensionSupported("EGL_KHR_image")); ext_fetch_arm = Utils::isExtensionSupported(*this, "GL_ARM_shader_framebuffer_fetch") && !ext_fetch; - dual_source_blending = !isGLESX || (Utils::isExtensionSupported(*this, "GL_EXT_blend_func_extended") && !isAnyAdreno); + dual_source_blending = !isGLESX || ((!isGLES2) && (Utils::isExtensionSupported(*this, "GL_EXT_blend_func_extended") && !isAnyAdreno)); anisotropic_filtering = Utils::isExtensionSupported(*this, "GL_EXT_texture_filter_anisotropic"); #ifdef OS_ANDROID From 2d923939db32aad01e633010fac71f860c943a6d Mon Sep 17 00:00:00 2001 From: JoshuaEagles Date: Tue, 29 Jul 2025 10:27:38 -0400 Subject: [PATCH 42/58] Add Memory Map Setup (#545) * add memory map setup * move retroarch memory map setup to device.c * update retroarch memory map setup to map address space properly * add memory map flags for the retroarch memory maps * refactor to map the entirety of KSEG0 and KSEG1 * finish mapping the whole KSEG1 address space and improve code readability * remove the -1 for the romsize mapping * set dd_rom_size when setting the pointer for it * fix pointers to dd regs and rom * adjust retroarch memory mapping to use the existing struct from init_device * revise use of the other mappings struct and fix broken pointers --- mupen64plus-core/src/api/m64p_types.h | 2 + mupen64plus-core/src/device/device.c | 159 +++++++++++++++++--- mupen64plus-core/src/device/memory/memory.h | 6 + 3 files changed, 145 insertions(+), 22 deletions(-) diff --git a/mupen64plus-core/src/api/m64p_types.h b/mupen64plus-core/src/api/m64p_types.h index 088f0117..805d083d 100644 --- a/mupen64plus-core/src/api/m64p_types.h +++ b/mupen64plus-core/src/api/m64p_types.h @@ -332,6 +332,8 @@ typedef enum { M64P_MEM_PI, M64P_MEM_RI, M64P_MEM_SI, + M64P_MEM_DDREG, + M64P_MEM_DDROM, M64P_MEM_FLASHRAMSTAT, M64P_MEM_ROM, M64P_MEM_PIF, diff --git a/mupen64plus-core/src/device/device.c b/mupen64plus-core/src/device/device.c index f3d61282..5d857338 100644 --- a/mupen64plus-core/src/device/device.c +++ b/mupen64plus-core/src/device/device.c @@ -21,6 +21,12 @@ #include "device.h" +#include + +#ifdef __LIBRETRO__ +#include +#endif // __LIBRETRO__ + #include "memory/memory.h" #include "pif/pif.h" #include "r4300/r4300_core.h" @@ -77,6 +83,113 @@ static void get_pi_dma_handler(struct cart* cart, struct dd_controller* dd, uint #undef RW } +void setup_retroarch_memory_map(const struct mem_mapping mappings[], size_t mappings_count, struct device* dev) { + // We allocate space for the number of mappings passed as arguments, and two additional slots since we map RDRAM and PIF as two separate mappings each. + // There will be a few unused slots at the end of the array, but they will just get ignored by Retroarch + struct retro_memory_descriptor descs[mappings_count + 2]; + + struct retro_memory_map retromap; + + memset(descs, 0, sizeof(descs)); + + int mapped_regions_count = 0; + + for (int i = 0; i < mappings_count; i++) { + const struct mem_mapping mapping = mappings[i]; + + if (mapping.type == M64P_MEM_RDRAM) { + // RDRAM is accessible cached, map to KSEG0 as well as KSEG1 + // RDRAM sets its pointer after the mappings struct is created, so we need to get it from the dev struct + descs[mapped_regions_count].ptr = dev->rdram.dram; + descs[mapped_regions_count].start = R4300_KSEG0 | mapping.begin; + descs[mapped_regions_count].len = mapping.retroarch_mapping.len; + descs[mapped_regions_count].flags = mapping.retroarch_mapping.flags; + descs[mapped_regions_count].select = 0x20000000; + descs[mapped_regions_count].disconnect = 0xC0000000; + mapped_regions_count++; + + descs[mapped_regions_count].ptr = dev->rdram.dram; + descs[mapped_regions_count].start = R4300_KSEG1 | mapping.begin; + descs[mapped_regions_count].len = mapping.retroarch_mapping.len; + descs[mapped_regions_count].flags = mapping.retroarch_mapping.flags; + mapped_regions_count++; + } + else if (mapping.type == M64P_MEM_RSPMEM) { + // RSPMEM sets its pointer after the mappings struct is created, so we need to get it from the dev struct + descs[mapped_regions_count].ptr = dev->sp.mem; + descs[mapped_regions_count].start = R4300_KSEG1 | mapping.begin; + descs[mapped_regions_count].len = mapping.retroarch_mapping.len; + descs[mapped_regions_count].flags = mapping.retroarch_mapping.flags; + mapped_regions_count++; + } + else if (mapping.type == M64P_MEM_FLASHRAMSTAT) { + // Handle save data as a special case, can use two different pointers + if (dev->cart.use_flashram == -1) { + descs[mapped_regions_count].ptr = &dev->cart.sram; + } + else { + descs[mapped_regions_count].ptr = &dev->cart.flashram; + } + + descs[mapped_regions_count].start = R4300_KSEG1 | mapping.begin; + descs[mapped_regions_count].len = mapping.retroarch_mapping.len; + descs[mapped_regions_count].flags = mapping.retroarch_mapping.flags; + mapped_regions_count++; + } + else if (mapping.type == M64P_MEM_ROM) { + // Cart rom sets its pointer after the mappings struct is created, so we need to get it from the dev struct + descs[mapped_regions_count].ptr = dev->cart.cart_rom.rom; + descs[mapped_regions_count].start = R4300_KSEG1 | mapping.begin; + descs[mapped_regions_count].len = mapping.retroarch_mapping.len; + descs[mapped_regions_count].flags = mapping.retroarch_mapping.flags; + mapped_regions_count++; + } + else if (mapping.type == M64P_MEM_DDREG) { + // DD Regs sets its pointer after the mappings struct is created, so we need to get it from the dev struct + descs[mapped_regions_count].ptr = dev->dd.regs; + descs[mapped_regions_count].start = R4300_KSEG1 | mapping.begin; + descs[mapped_regions_count].len = mapping.retroarch_mapping.len; + descs[mapped_regions_count].flags = mapping.retroarch_mapping.flags; + mapped_regions_count++; + } + else if (mapping.type == M64P_MEM_DDROM) { + // DD rom sets its pointer after the mappings struct is created, so we need to get it from the dev struct + descs[mapped_regions_count].ptr = (void*)dev->dd.rom; + descs[mapped_regions_count].start = R4300_KSEG1 | mapping.begin; + descs[mapped_regions_count].len = mapping.retroarch_mapping.len; + descs[mapped_regions_count].flags = mapping.retroarch_mapping.flags; + mapped_regions_count++; + } + else if (mapping.type == M64P_MEM_PIF) { + // Map PIF as two regions to allow making the PIF ROM read only + descs[mapped_regions_count].ptr = dev->pif.base; + descs[mapped_regions_count].start = R4300_KSEG1 | mapping.begin; + descs[mapped_regions_count].len = PIF_ROM_SIZE; + descs[mapped_regions_count].flags = RETRO_MEMDESC_CONST; + mapped_regions_count++; + + descs[mapped_regions_count].ptr = dev->pif.ram; + descs[mapped_regions_count].start = R4300_KSEG1 | mapping.begin + PIF_ROM_SIZE; + descs[mapped_regions_count].len = PIF_RAM_SIZE; + descs[mapped_regions_count].flags = 0; + mapped_regions_count++; + } + else if (mapping.retroarch_mapping.ptr != NULL) { + // Map memory regions that don't need special handling + descs[mapped_regions_count].ptr = mapping.retroarch_mapping.ptr; + descs[mapped_regions_count].start = R4300_KSEG1 | mapping.begin; + descs[mapped_regions_count].len = mapping.retroarch_mapping.len; + descs[mapped_regions_count].flags = mapping.retroarch_mapping.flags; + mapped_regions_count++; + } + } + + retromap.descriptors = descs; + retromap.num_descriptors = mapped_regions_count; + + environ_cb(RETRO_ENVIRONMENT_SET_MEMORY_MAPS, &retromap); +} + void init_device(struct device* dev, /* memory */ void* base, @@ -136,33 +249,33 @@ void init_device(struct device* dev, #define A(x,m) (x), (x) | (m) struct mem_mapping mappings[] = { /* clear mappings */ - { 0x00000000, 0xffffffff, M64P_MEM_NOTHING, { NULL, RW(open_bus) } }, + { 0x00000000, 0xffffffff, M64P_MEM_NOTHING, { NULL, RW(open_bus) }, { NULL, 0, 0 } }, /* memory map */ - { A(MM_RDRAM_DRAM, 0x3efffff), M64P_MEM_RDRAM, { &dev->rdram, RW(rdram_dram) } }, - { A(MM_RDRAM_REGS, 0xfffff), M64P_MEM_RDRAMREG, { &dev->rdram, RW(rdram_regs) } }, - { A(MM_RSP_MEM, 0xffff), M64P_MEM_RSPMEM, { &dev->sp, RW(rsp_mem) } }, - { A(MM_RSP_REGS, 0xffff), M64P_MEM_RSPREG, { &dev->sp, RW(rsp_regs) } }, - { A(MM_RSP_REGS2, 0xffff), M64P_MEM_RSP, { &dev->sp, RW(rsp_regs2) } }, - { A(MM_DPC_REGS, 0xffff), M64P_MEM_DP, { &dev->dp, RW(dpc_regs) } }, - { A(MM_DPS_REGS, 0xffff), M64P_MEM_DPS, { &dev->dp, RW(dps_regs) } }, - { A(MM_MI_REGS, 0xffff), M64P_MEM_MI, { &dev->mi, RW(mi_regs) } }, - { A(MM_VI_REGS, 0xffff), M64P_MEM_VI, { &dev->vi, RW(vi_regs) } }, - { A(MM_AI_REGS, 0xffff), M64P_MEM_AI, { &dev->ai, RW(ai_regs) } }, - { A(MM_PI_REGS, 0xffff), M64P_MEM_PI, { &dev->pi, RW(pi_regs) } }, - { A(MM_RI_REGS, 0xffff), M64P_MEM_RI, { &dev->ri, RW(ri_regs) } }, - { A(MM_SI_REGS, 0xffff), M64P_MEM_SI, { &dev->si, RW(si_regs) } }, - { A(MM_DOM2_ADDR1, 0xffffff), M64P_MEM_NOTHING, { NULL, RW(open_bus) } }, - { A(MM_DD_ROM, 0x1ffffff), M64P_MEM_NOTHING, { NULL, RW(open_bus) } }, - { A(MM_DOM2_ADDR2, 0x1ffff), M64P_MEM_FLASHRAMSTAT, { &dev->cart, RW(cart_dom2) } }, - { A(MM_IS_VIEWER, 0xfff), M64P_MEM_NOTHING, { &dev->is, RW(is_viewer) } }, - { A(MM_CART_ROM, rom_size-1), M64P_MEM_ROM, { &dev->cart.cart_rom, RW(cart_rom) } }, - { A(MM_PIF_MEM, 0xffff), M64P_MEM_PIF, { &dev->pif, RW(pif_mem) } } + { A(MM_RDRAM_DRAM, 0x3efffff), M64P_MEM_RDRAM, { &dev->rdram, RW(rdram_dram) }, {NULL, dram_size, RETRO_MEMDESC_SYSTEM_RAM } }, + { A(MM_RDRAM_REGS, 0xfffff), M64P_MEM_RDRAMREG, { &dev->rdram, RW(rdram_regs) }, { dev->rdram.regs, 0xfffff, 0 } }, + { A(MM_RSP_MEM, 0xffff), M64P_MEM_RSPMEM, { &dev->sp, RW(rsp_mem) }, { NULL, 0xffff, 0 } }, + { A(MM_RSP_REGS, 0xffff), M64P_MEM_RSPREG, { &dev->sp, RW(rsp_regs) }, { dev->sp.regs, 0xffff, 0 } }, + { A(MM_RSP_REGS2, 0xffff), M64P_MEM_RSP, { &dev->sp, RW(rsp_regs2) }, { dev->sp.regs2, 0xffff, 0 } }, + { A(MM_DPC_REGS, 0xffff), M64P_MEM_DP, { &dev->dp, RW(dpc_regs) }, { dev->dp.dpc_regs, 0xffff, 0 } }, + { A(MM_DPS_REGS, 0xffff), M64P_MEM_DPS, { &dev->dp, RW(dps_regs) }, { dev->dp.dps_regs, 0xffff, 0 } }, + { A(MM_MI_REGS, 0xffff), M64P_MEM_MI, { &dev->mi, RW(mi_regs) }, { dev->mi.regs, 0xffff, 0 } }, + { A(MM_VI_REGS, 0xffff), M64P_MEM_VI, { &dev->vi, RW(vi_regs) }, { dev->vi.regs, 0xffff, 0} }, + { A(MM_AI_REGS, 0xffff), M64P_MEM_AI, { &dev->ai, RW(ai_regs) }, { dev->ai.regs, 0xffff, 0} }, + { A(MM_PI_REGS, 0xffff), M64P_MEM_PI, { &dev->pi, RW(pi_regs) }, { dev->pi.regs, 0xffff, 0} }, + { A(MM_RI_REGS, 0xffff), M64P_MEM_RI, { &dev->ri, RW(ri_regs) }, { dev->ri.regs, 0xffff, 0} }, + { A(MM_SI_REGS, 0xffff), M64P_MEM_SI, { &dev->si, RW(si_regs) }, { dev->si.regs, 0xffff, 0} }, + { A(MM_DOM2_ADDR1, 0xffffff), M64P_MEM_NOTHING, { NULL, RW(open_bus) }, { NULL, 0xffffff, 0 } }, + { A(MM_DD_ROM, 0x1ffffff), M64P_MEM_NOTHING, { NULL, RW(open_bus) }, { NULL, 0x1ffffff, 0 } }, + { A(MM_DOM2_ADDR2, 0x1ffff), M64P_MEM_FLASHRAMSTAT, { &dev->cart, RW(cart_dom2) }, { NULL, 0x1ffff, 0} }, + { A(MM_IS_VIEWER, 0xfff), M64P_MEM_NOTHING, { &dev->is, RW(is_viewer) }, { NULL, 0xfff, 0 } }, + { A(MM_CART_ROM, rom_size-1), M64P_MEM_ROM, { &dev->cart.cart_rom, RW(cart_rom) }, { NULL, rom_size-1, RETRO_MEMDESC_CONST } }, + { A(MM_PIF_MEM, 0xffff), M64P_MEM_PIF, { &dev->pif, RW(pif_mem) }, { NULL, 0xffff, 0 } } }; /* init and map DD if present */ if (dd_rom_size > 0) { - mappings[14] = (struct mem_mapping){ A(MM_DOM2_ADDR1, 0xffffff), M64P_MEM_NOTHING, { &dev->dd, RW(dd_regs) } }; - mappings[15] = (struct mem_mapping){ A(MM_DD_ROM, dd_rom_size-1), M64P_MEM_NOTHING, { &dev->dd, RW(dd_rom) } }; + mappings[14] = (struct mem_mapping){ A(MM_DOM2_ADDR1, 0xffffff), M64P_MEM_DDREG, { &dev->dd, RW(dd_regs) }, { NULL, 0xffffff, 0 } }; + mappings[15] = (struct mem_mapping){ A(MM_DD_ROM, dd_rom_size-1), M64P_MEM_DDROM, { &dev->dd, RW(dd_rom) }, { NULL, dd_rom_size-1, RETRO_MEMDESC_CONST } }; init_dd(&dev->dd, dd_rtc_clock, dd_rtc_iclock, @@ -220,6 +333,8 @@ void init_device(struct device* dev, flashram_type, flashram_storage, iflashram_storage, (const uint8_t*)dev->rdram.dram, sram_storage, isram_storage); + + setup_retroarch_memory_map(mappings, ARRAY_SIZE(mappings), dev); } void poweron_device(struct device* dev) diff --git a/mupen64plus-core/src/device/memory/memory.h b/mupen64plus-core/src/device/memory/memory.h index 7cb02e25..bd0dd63f 100644 --- a/mupen64plus-core/src/device/memory/memory.h +++ b/mupen64plus-core/src/device/memory/memory.h @@ -41,12 +41,18 @@ struct mem_handler write32fn write32; }; +struct retroarch_mem_mapping { + void* ptr; + size_t len; + uint64_t flags; +}; struct mem_mapping { uint32_t begin; uint32_t end; /* inclusive */ int type; struct mem_handler handler; + struct retroarch_mem_mapping retroarch_mapping; }; struct memory From 222acbd3f98391458a047874d0372fe78e14fe94 Mon Sep 17 00:00:00 2001 From: M4xw Date: Wed, 20 Aug 2025 16:42:10 +0200 Subject: [PATCH 43/58] Add mediump precision specification for fragment shaders --- .../OpenGLContext/GLSL/glsl_CombinerProgramBuilderCommon.cpp | 2 ++ .../Graphics/OpenGLContext/GLSL/glsl_SpecialShadersFactory.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderCommon.cpp b/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderCommon.cpp index 35161d33..a4c52ed6 100644 --- a/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderCommon.cpp +++ b/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderCommon.cpp @@ -30,6 +30,7 @@ class VertexShaderHeader : public ShaderPart "# define IN attribute \n" "# define OUT varying \n" "#ifndef GL_FRAGMENT_PRECISION_HIGH \n" + "precision mediump float;\n" "# define highp mediump \n" "#endif \n" "#endif // __VERSION \n" @@ -202,6 +203,7 @@ class FragmentShaderHeader : public ShaderPart "# define IN varying \n" "# define OUT \n" "#ifndef GL_FRAGMENT_PRECISION_HIGH \n" + "precision mediump float;\n" "# define highp mediump \n" "#endif \n" "#endif // __VERSION __ \n" diff --git a/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_SpecialShadersFactory.cpp b/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_SpecialShadersFactory.cpp index 839054c4..90e5c4f6 100644 --- a/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_SpecialShadersFactory.cpp +++ b/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_SpecialShadersFactory.cpp @@ -149,6 +149,7 @@ namespace glsl { "# define IN varying \n" "# define OUT \n" "#ifndef GL_FRAGMENT_PRECISION_HIGH \n" + "precision mediump float; \n" "# define highp mediump \n" "#endif \n" "#endif // __VERSION __ \n" @@ -222,6 +223,7 @@ namespace glsl { "# define IN varying \n" "# define OUT \n" "#ifndef GL_FRAGMENT_PRECISION_HIGH \n" + "precision mediump float; \n" "# define highp mediump \n" "#endif \n" "#endif // __VERSION __ \n" From 680e033fc8ed1a49df7b156d97164e0050ee13bc Mon Sep 17 00:00:00 2001 From: m4xw Date: Fri, 14 Nov 2025 16:55:40 +0100 Subject: [PATCH 44/58] Disable dual source blending in OpenGL context --- GLideN64/src/Graphics/OpenGLContext/opengl_GLInfo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GLideN64/src/Graphics/OpenGLContext/opengl_GLInfo.cpp b/GLideN64/src/Graphics/OpenGLContext/opengl_GLInfo.cpp index abbd382c..c11ccad8 100644 --- a/GLideN64/src/Graphics/OpenGLContext/opengl_GLInfo.cpp +++ b/GLideN64/src/Graphics/OpenGLContext/opengl_GLInfo.cpp @@ -202,7 +202,7 @@ void GLInfo::init() { eglImage = (Utils::isEGLExtensionSupported("EGL_KHR_image_base") || Utils::isEGLExtensionSupported("EGL_KHR_image")); ext_fetch_arm = Utils::isExtensionSupported(*this, "GL_ARM_shader_framebuffer_fetch") && !ext_fetch; - dual_source_blending = !isGLESX || ((!isGLES2) && (Utils::isExtensionSupported(*this, "GL_EXT_blend_func_extended") && !isAnyAdreno)); + dual_source_blending = false; //!isGLESX || ((!isGLES2) && (Utils::isExtensionSupported(*this, "GL_EXT_blend_func_extended") && !isAnyAdreno)); anisotropic_filtering = Utils::isExtensionSupported(*this, "GL_EXT_texture_filter_anisotropic"); #ifdef OS_ANDROID From b45d82c70e75c11bd35f16e6ee7e7590f009b8ce Mon Sep 17 00:00:00 2001 From: Eric Warmenhoven Date: Sat, 10 Jan 2026 17:46:15 -0500 Subject: [PATCH 45/58] apple: use audiotoolbox for resampling --- Makefile | 3 + .../audio_libretro/audio_backend_libretro.c | 181 +++++++++++++++++- 2 files changed, 179 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 481e8b92..97c16094 100644 --- a/Makefile +++ b/Makefile @@ -406,6 +406,7 @@ else ifneq (,$(findstring osx,$(platform))) PLATCFLAGS += -D__MACOSX__ -DOSX -DOS_MAC_OS_X -DHAVE_UNISTD_H=1 -DHAVE_POSIX_MEMALIGN -DNO_ASM -DGL_SILENCE_DEPRECATION=1 GL_LIB := -framework OpenGL + LDFLAGS += -framework AudioToolbox # Target Dynarec WITH_DYNAREC = @@ -474,6 +475,7 @@ else ifneq (,$(findstring ios,$(platform))) endif LDFLAGS += -dynamiclib GL_LIB := -framework OpenGLES + LDFLAGS += -framework AudioToolbox # tvOS else ifneq (,$(findstring tvos,$(platform))) ifeq ($(TVOSSDK),) @@ -507,6 +509,7 @@ else ifneq (,$(findstring tvos,$(platform))) LDFLAGS += -dynamiclib GL_LIB := -framework OpenGLES + LDFLAGS += -framework AudioToolbox # Android else ifneq (,$(findstring android,$(platform))) ANDROID = 1 diff --git a/custom/mupen64plus-core/plugin/audio_libretro/audio_backend_libretro.c b/custom/mupen64plus-core/plugin/audio_libretro/audio_backend_libretro.c index c3154208..f9014bd8 100644 --- a/custom/mupen64plus-core/plugin/audio_libretro/audio_backend_libretro.c +++ b/custom/mupen64plus-core/plugin/audio_libretro/audio_backend_libretro.c @@ -36,15 +36,20 @@ #include #include +#ifdef __APPLE__ +#include +#else #include