From c3ba8887c064f26e2c9ca3ba38fbab29f88e43b7 Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Wed, 5 Apr 2023 17:16:55 -0700 Subject: [PATCH 01/79] Remove bad contact information I think I was ending up in people's spam also professionalism lol --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 97d3bc78..802d5327 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ Try to follow the Unreal Engine coding standards at least in the Unreal based so ## Contact -You should [post an issue](https://github.com/N7Alpha/UnrealLibretro/issues) if you have a problem or discover a bug. If you have general questions about the project or want to make a big contribution and you need to talk through the project structure with me you can email me at rehbein@cock.li +You should [post an issue](https://github.com/N7Alpha/UnrealLibretro/issues) if you have a problem or discover a bug. If that is too intimidating or for more basic troubleshooting you can post in the support channel of the [Discord](https://discord.gg/nSTy2jyJmh). For anything else you can just email me at john.k.rehbein@gmail.com ## License From 9af5a2163c797beb59d803e0ec8ff71d828102c8 Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Thu, 6 Apr 2023 18:23:33 -0700 Subject: [PATCH 02/79] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 802d5327..8eee61aa 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ Navigate to UnrealLibretro's content folder in the Unreal Editor content browser ## Contributing -Try to follow the Unreal Engine coding standards at least in the Unreal based source files. Mainly what needs to be worked on is Libretro core compatibility and probably fleshing out ```ULibretroCoreInstance``` to incorporate more of the API ```libretro.h``` exposes. I'm mainly just developing this for a project I'm working on myself, so there also might be some oversights in the API that should be rectified. More information about contributing can be found [here](CONTRIBUTING.md). +Mainly what needs to be worked on is Libretro Core compatibility and probably fleshing out ```ULibretroCoreInstance``` to incorporate more of the API ```libretro.h``` exposes. More information about contributing can be found [here](CONTRIBUTING.md). ## Contact From adc678c2240e29688b1c92d498f72da3a8db87ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Mendon=C3=A7a=20Ferreira?= Date: Tue, 11 Apr 2023 03:00:14 -0300 Subject: [PATCH 03/79] Add load_cfg_file() function to load libretro core options from a .cfg file (#20) * Added function declaration for load_cfg_file() * Added load_cfg_file() to load core options load_cfg_file() loads a [core].cfg file with libretro core options, and is called between the load() and load_game() functions. --- Source/UnrealLibretro/Private/sdlarch.cpp | 52 +++++++++++++++++++++++ Source/UnrealLibretro/Private/sdlarch.h | 1 + 2 files changed, 53 insertions(+) diff --git a/Source/UnrealLibretro/Private/sdlarch.cpp b/Source/UnrealLibretro/Private/sdlarch.cpp index 6d1c8cb1..1938c429 100644 --- a/Source/UnrealLibretro/Private/sdlarch.cpp +++ b/Source/UnrealLibretro/Private/sdlarch.cpp @@ -11,6 +11,9 @@ extern "C" #include "LambdaRunnable.h" #include "HAL/FileManager.h" +#include +#include +#include #if PLATFORM_APPLE #include @@ -920,6 +923,52 @@ void LibretroContext::load_game(const char* filename) { video_configure(&core.av.geometry); } +void LibretroContext::load_cfg_file(const std::string& core_path) { + auto cfg_path = core_path.substr(0, core_path.find_last_of('.')) + ".cfg"; +#if PLATFORM_WINDOWS + std::replace(cfg_path.begin(), cfg_path.end(), '/', '\\'); +#endif + core_log(RETRO_LOG_DEBUG, "load_cfg_file(): cfg_path == %s", cfg_path.c_str()); + std::ifstream file(cfg_path); + std::string line; + std::string section; + + try { + while (std::getline(file, line)) { + // Remove leading and trailing whitespace from line + auto first = line.find_first_not_of(" \t"); + auto last = line.find_last_not_of(" \t"); + if (first == std::string::npos || last == std::string::npos) { + continue; // Skip empty lines + } + line = line.substr(first, last - first + 1); + + // Check if line is a key-value pair + if (line.front() != '#') { + auto delimiter = line.find('='); + if (delimiter == std::string::npos) { + continue; // Skip lines without '=' delimiter + } + auto key = line.substr(0, delimiter); + auto value = line.substr(delimiter + 1); + // Remove leading and trailing whitespace from key and value + key = key.substr(key.find_first_not_of(" \t"), key.find_last_not_of(" \t") + 1); + if (!value.empty()) { + value = value.substr(value.find_first_not_of(" \t"), value.find_last_not_of(" \t") + 1); + // Add key-value pair to settings + core.settings[key] = value; + } + } + } + } + catch (const std::exception& e) { + core_log(RETRO_LOG_ERROR, "load_cfg_file(): caught exception: %s", e.what()); + } + catch (...) { + core_log(RETRO_LOG_ERROR, "load_cfg_file(): caught unknown exception"); + } +} + LibretroContext* LibretroContext::Launch(FString core, FString game, UTextureRenderTarget2D* RenderTarget, URawAudioSoundWave* SoundBuffer, TUniqueFunction LoadedCallback) { @@ -985,6 +1034,9 @@ LibretroContext* LibretroContext::Launch(FString core, FString game, UTextureRen // Loads the dll and its function pointers into libretro_api l->load(TCHAR_TO_ANSI(*InstancedCorePath)); + // Loads a core configuration file, if it exists, from the same directory as the core + l->load_cfg_file(TCHAR_TO_ANSI(*core)); + // This does load the game but does many other things as well. If hardware rendering is needed it loads OpenGL resources from the OS and this also initializes the unreal engine resources for audio and video. l->load_game(TCHAR_TO_UTF8(*game)); diff --git a/Source/UnrealLibretro/Private/sdlarch.h b/Source/UnrealLibretro/Private/sdlarch.h index 4fe616e0..2443c182 100644 --- a/Source/UnrealLibretro/Private/sdlarch.h +++ b/Source/UnrealLibretro/Private/sdlarch.h @@ -242,4 +242,5 @@ struct LibretroContext { void load(const char* sofile); void load_game(const char* filename); + void load_cfg_file(const std::string& core_path); }; \ No newline at end of file From 5b7298a3c2566d2d4559f16f99f6a6c31d4abb14 Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Sun, 4 Jun 2023 20:40:55 -0700 Subject: [PATCH 04/79] Revert "load libretro core options from... file" This reverts commit adc678c. - I replaced this with an in editor method of setting core options as well as a GlobalCoreOptions map that can be configured in an .ini check out UnrealLibretro.ini for an example - I feel bad immediately reverting the first contribution however I do think this new method is more intuitive and more in line with Unreal Engine conventions --- Source/UnrealLibretro/Private/sdlarch.cpp | 52 ----------------------- Source/UnrealLibretro/Private/sdlarch.h | 1 - 2 files changed, 53 deletions(-) diff --git a/Source/UnrealLibretro/Private/sdlarch.cpp b/Source/UnrealLibretro/Private/sdlarch.cpp index 1938c429..6d1c8cb1 100644 --- a/Source/UnrealLibretro/Private/sdlarch.cpp +++ b/Source/UnrealLibretro/Private/sdlarch.cpp @@ -11,9 +11,6 @@ extern "C" #include "LambdaRunnable.h" #include "HAL/FileManager.h" -#include -#include -#include #if PLATFORM_APPLE #include @@ -923,52 +920,6 @@ void LibretroContext::load_game(const char* filename) { video_configure(&core.av.geometry); } -void LibretroContext::load_cfg_file(const std::string& core_path) { - auto cfg_path = core_path.substr(0, core_path.find_last_of('.')) + ".cfg"; -#if PLATFORM_WINDOWS - std::replace(cfg_path.begin(), cfg_path.end(), '/', '\\'); -#endif - core_log(RETRO_LOG_DEBUG, "load_cfg_file(): cfg_path == %s", cfg_path.c_str()); - std::ifstream file(cfg_path); - std::string line; - std::string section; - - try { - while (std::getline(file, line)) { - // Remove leading and trailing whitespace from line - auto first = line.find_first_not_of(" \t"); - auto last = line.find_last_not_of(" \t"); - if (first == std::string::npos || last == std::string::npos) { - continue; // Skip empty lines - } - line = line.substr(first, last - first + 1); - - // Check if line is a key-value pair - if (line.front() != '#') { - auto delimiter = line.find('='); - if (delimiter == std::string::npos) { - continue; // Skip lines without '=' delimiter - } - auto key = line.substr(0, delimiter); - auto value = line.substr(delimiter + 1); - // Remove leading and trailing whitespace from key and value - key = key.substr(key.find_first_not_of(" \t"), key.find_last_not_of(" \t") + 1); - if (!value.empty()) { - value = value.substr(value.find_first_not_of(" \t"), value.find_last_not_of(" \t") + 1); - // Add key-value pair to settings - core.settings[key] = value; - } - } - } - } - catch (const std::exception& e) { - core_log(RETRO_LOG_ERROR, "load_cfg_file(): caught exception: %s", e.what()); - } - catch (...) { - core_log(RETRO_LOG_ERROR, "load_cfg_file(): caught unknown exception"); - } -} - LibretroContext* LibretroContext::Launch(FString core, FString game, UTextureRenderTarget2D* RenderTarget, URawAudioSoundWave* SoundBuffer, TUniqueFunction LoadedCallback) { @@ -1034,9 +985,6 @@ LibretroContext* LibretroContext::Launch(FString core, FString game, UTextureRen // Loads the dll and its function pointers into libretro_api l->load(TCHAR_TO_ANSI(*InstancedCorePath)); - // Loads a core configuration file, if it exists, from the same directory as the core - l->load_cfg_file(TCHAR_TO_ANSI(*core)); - // This does load the game but does many other things as well. If hardware rendering is needed it loads OpenGL resources from the OS and this also initializes the unreal engine resources for audio and video. l->load_game(TCHAR_TO_UTF8(*game)); diff --git a/Source/UnrealLibretro/Private/sdlarch.h b/Source/UnrealLibretro/Private/sdlarch.h index 2443c182..4fe616e0 100644 --- a/Source/UnrealLibretro/Private/sdlarch.h +++ b/Source/UnrealLibretro/Private/sdlarch.h @@ -242,5 +242,4 @@ struct LibretroContext { void load(const char* sofile); void load_game(const char* filename); - void load_cfg_file(const std::string& core_path); }; \ No newline at end of file From a6890e7f54b921d6f681fa32b460b7661c92ae36 Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Tue, 20 Dec 2022 19:14:29 -0800 Subject: [PATCH 05/79] Move retro-common code to public folder --- .../libretro/gfx/scaler/pixconv.c | 0 .../libretro/include/clamping.h | 0 .../libretro/include/gfx/scaler/pixconv.h | 0 .../libretro/include/retro_inline.h | 0 .../{Private => Public}/libretro/libretro.h | 0 Source/UnrealLibretro/UnrealLibretro.Build.cs | 118 +++++++++--------- 6 files changed, 59 insertions(+), 59 deletions(-) rename Source/UnrealLibretro/{Private => Public}/libretro/gfx/scaler/pixconv.c (100%) rename Source/UnrealLibretro/{Private => Public}/libretro/include/clamping.h (100%) rename Source/UnrealLibretro/{Private => Public}/libretro/include/gfx/scaler/pixconv.h (100%) rename Source/UnrealLibretro/{Private => Public}/libretro/include/retro_inline.h (100%) rename Source/UnrealLibretro/{Private => Public}/libretro/libretro.h (100%) diff --git a/Source/UnrealLibretro/Private/libretro/gfx/scaler/pixconv.c b/Source/UnrealLibretro/Public/libretro/gfx/scaler/pixconv.c similarity index 100% rename from Source/UnrealLibretro/Private/libretro/gfx/scaler/pixconv.c rename to Source/UnrealLibretro/Public/libretro/gfx/scaler/pixconv.c diff --git a/Source/UnrealLibretro/Private/libretro/include/clamping.h b/Source/UnrealLibretro/Public/libretro/include/clamping.h similarity index 100% rename from Source/UnrealLibretro/Private/libretro/include/clamping.h rename to Source/UnrealLibretro/Public/libretro/include/clamping.h diff --git a/Source/UnrealLibretro/Private/libretro/include/gfx/scaler/pixconv.h b/Source/UnrealLibretro/Public/libretro/include/gfx/scaler/pixconv.h similarity index 100% rename from Source/UnrealLibretro/Private/libretro/include/gfx/scaler/pixconv.h rename to Source/UnrealLibretro/Public/libretro/include/gfx/scaler/pixconv.h diff --git a/Source/UnrealLibretro/Private/libretro/include/retro_inline.h b/Source/UnrealLibretro/Public/libretro/include/retro_inline.h similarity index 100% rename from Source/UnrealLibretro/Private/libretro/include/retro_inline.h rename to Source/UnrealLibretro/Public/libretro/include/retro_inline.h diff --git a/Source/UnrealLibretro/Private/libretro/libretro.h b/Source/UnrealLibretro/Public/libretro/libretro.h similarity index 100% rename from Source/UnrealLibretro/Private/libretro/libretro.h rename to Source/UnrealLibretro/Public/libretro/libretro.h diff --git a/Source/UnrealLibretro/UnrealLibretro.Build.cs b/Source/UnrealLibretro/UnrealLibretro.Build.cs index 5f6c12b3..a96018d7 100644 --- a/Source/UnrealLibretro/UnrealLibretro.Build.cs +++ b/Source/UnrealLibretro/UnrealLibretro.Build.cs @@ -1,59 +1,59 @@ -// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. - -using System; -using UnrealBuildTool; - -public class UnrealLibretro : ModuleRules -{ - public UnrealLibretro(ReadOnlyTargetRules Target) : base(Target) - { - PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; - - PrivateIncludePaths.Add("UnrealLibretro/Private/libretro/include"); - - RuntimeDependencies.Add("$(PluginDir)/MyRoms/*" ); - RuntimeDependencies.Add("$(PluginDir)/Saves/*" ); - RuntimeDependencies.Add("$(PluginDir)/System/*" ); - - - if (Target.Platform.Equals(UnrealTargetPlatform.Mac)) - { - RuntimeDependencies.Add("$(PluginDir)/MyCores/Mac/*"); - RuntimeDependencies.Add("$(PluginDir)/Binaries/Mac/ThirdParty/libSDL2*.dylib"); // globbed because libSDL2.dylib is an alias - PublicAdditionalLibraries.Add("$(PluginDir)/Binaries/Mac/ThirdParty/libSDL2.dylib"); - } - else if (Target.Platform.Equals(UnrealTargetPlatform.Win64)) - { - RuntimeDependencies.Add("$(PluginDir)/MyCores/Win64/*"); - PublicAdditionalLibraries.Add("$(PluginDir)/Binaries/Win64/ThirdParty/SDL2.lib"); - RuntimeDependencies.Add("$(PluginDir)/Binaries/Win64/ThirdParty/SDL2.dll"); - RuntimeDependencies.Add("$(PluginDir)/Binaries/Win64/ThirdParty/libretro/*"); - PublicDelayLoadDLLs.Add("SDL2.dll"); - } - else if (Target.Platform.Equals(UnrealTargetPlatform.Android)) - { - RuntimeDependencies.Add("$(PluginDir)/MyCores/Android/armeabi-v7a/*"); - RuntimeDependencies.Add("$(PluginDir)/MyCores/Android/arm64-v8a/*"); - } - - PublicDependencyModuleNames.AddRange( - new string[] - { - "Core", - "RenderCore", - "RHI", - "CoreUObject", - "Engine", - "InputCore", - "Slate", - "Projects", - } - ); - - if ( Target.Version.MajorVersion > 4 - || Target.Version.MinorVersion >= 26) - { - PublicDependencyModuleNames.Add("DeveloperSettings"); // Was moved into its own module in 4.26 - } - } -} +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. + +using System; +using UnrealBuildTool; + +public class UnrealLibretro : ModuleRules +{ + public UnrealLibretro(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + + PrivateIncludePaths.Add("UnrealLibretro/Public/libretro/include"); + + RuntimeDependencies.Add("$(PluginDir)/MyRoms/*" ); + RuntimeDependencies.Add("$(PluginDir)/Saves/*" ); + RuntimeDependencies.Add("$(PluginDir)/System/*" ); + + + if (Target.Platform.Equals(UnrealTargetPlatform.Mac)) + { + RuntimeDependencies.Add("$(PluginDir)/MyCores/Mac/*"); + RuntimeDependencies.Add("$(PluginDir)/Binaries/Mac/ThirdParty/libSDL2*.dylib"); // globbed because libSDL2.dylib is an alias + PublicAdditionalLibraries.Add("$(PluginDir)/Binaries/Mac/ThirdParty/libSDL2.dylib"); + } + else if (Target.Platform.Equals(UnrealTargetPlatform.Win64)) + { + RuntimeDependencies.Add("$(PluginDir)/MyCores/Win64/*"); + PublicAdditionalLibraries.Add("$(PluginDir)/Binaries/Win64/ThirdParty/SDL2.lib"); + RuntimeDependencies.Add("$(PluginDir)/Binaries/Win64/ThirdParty/SDL2.dll"); + RuntimeDependencies.Add("$(PluginDir)/Binaries/Win64/ThirdParty/libretro/*"); + PublicDelayLoadDLLs.Add("SDL2.dll"); + } + else if (Target.Platform.Equals(UnrealTargetPlatform.Android)) + { + RuntimeDependencies.Add("$(PluginDir)/MyCores/Android/armeabi-v7a/*"); + RuntimeDependencies.Add("$(PluginDir)/MyCores/Android/arm64-v8a/*"); + } + + PublicDependencyModuleNames.AddRange( + new string[] + { + "Core", + "RenderCore", + "RHI", + "CoreUObject", + "Engine", + "InputCore", + "Slate", + "Projects", + } + ); + + if ( Target.Version.MajorVersion > 4 + || Target.Version.MinorVersion >= 26) + { + PublicDependencyModuleNames.Add("DeveloperSettings"); // Was moved into its own module in 4.26 + } + } +} From fb82e25580064243529675166a7ab1980803759f Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Tue, 20 Dec 2022 19:19:33 -0800 Subject: [PATCH 06/79] Add [miniz](https://github.com/richgel999/miniz) --- Source/UnrealLibretroEditor/miniz/LICENSE | 22 + Source/UnrealLibretroEditor/miniz/miniz.c | 7833 +++++++++++++++++++++ Source/UnrealLibretroEditor/miniz/miniz.h | 1422 ++++ 3 files changed, 9277 insertions(+) create mode 100644 Source/UnrealLibretroEditor/miniz/LICENSE create mode 100644 Source/UnrealLibretroEditor/miniz/miniz.c create mode 100644 Source/UnrealLibretroEditor/miniz/miniz.h diff --git a/Source/UnrealLibretroEditor/miniz/LICENSE b/Source/UnrealLibretroEditor/miniz/LICENSE new file mode 100644 index 00000000..b6ff45a3 --- /dev/null +++ b/Source/UnrealLibretroEditor/miniz/LICENSE @@ -0,0 +1,22 @@ +Copyright 2013-2014 RAD Game Tools and Valve Software +Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + +All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Source/UnrealLibretroEditor/miniz/miniz.c b/Source/UnrealLibretroEditor/miniz/miniz.c new file mode 100644 index 00000000..c197c181 --- /dev/null +++ b/Source/UnrealLibretroEditor/miniz/miniz.c @@ -0,0 +1,7833 @@ +#include "miniz.h" +/************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + + + +typedef unsigned char mz_validate_uint16[sizeof(mz_uint16) == 2 ? 1 : -1]; +typedef unsigned char mz_validate_uint32[sizeof(mz_uint32) == 4 ? 1 : -1]; +typedef unsigned char mz_validate_uint64[sizeof(mz_uint64) == 8 ? 1 : -1]; + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- zlib-style API's */ + +mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len) +{ + mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); + size_t block_len = buf_len % 5552; + if (!ptr) + return MZ_ADLER32_INIT; + while (buf_len) + { + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) + { + s1 += ptr[0], s2 += s1; + s1 += ptr[1], s2 += s1; + s1 += ptr[2], s2 += s1; + s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; + s1 += ptr[5], s2 += s1; + s1 += ptr[6], s2 += s1; + s1 += ptr[7], s2 += s1; + } + for (; i < block_len; ++i) + s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; + buf_len -= block_len; + block_len = 5552; + } + return (s2 << 16) + s1; +} + +/* Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/ */ +#if 0 + mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) + { + static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, + 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; + mz_uint32 crcu32 = (mz_uint32)crc; + if (!ptr) + return MZ_CRC32_INIT; + crcu32 = ~crcu32; + while (buf_len--) + { + mz_uint8 b = *ptr++; + crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; + crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; + } + return ~crcu32; + } +#elif defined(USE_EXTERNAL_MZCRC) +/* If USE_EXTERNAL_CRC is defined, an external module will export the + * mz_crc32() symbol for us to use, e.g. an SSE-accelerated version. + * Depending on the impl, it may be necessary to ~ the input/output crc values. + */ +mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len); +#else +/* Faster, but larger CPU cache footprint. + */ +mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) +{ + static const mz_uint32 s_crc_table[256] = + { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, + 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, + 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, + 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, + 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, + 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, + 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, + 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, + 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, + 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, + 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, + 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, + 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, + 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, + 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, + 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, + 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, + 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, + 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, + 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, + 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, + 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, + 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, + 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, + 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, + 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, + 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, + 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, + 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, + 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, + 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + }; + + mz_uint32 crc32 = (mz_uint32)crc ^ 0xFFFFFFFF; + const mz_uint8 *pByte_buf = (const mz_uint8 *)ptr; + + while (buf_len >= 4) + { + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[1]) & 0xFF]; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[2]) & 0xFF]; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[3]) & 0xFF]; + pByte_buf += 4; + buf_len -= 4; + } + + while (buf_len) + { + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; + ++pByte_buf; + --buf_len; + } + + return ~crc32; +} +#endif + +void mz_free(void *p) +{ + MZ_FREE(p); +} + +MINIZ_EXPORT void *miniz_def_alloc_func(void *opaque, size_t items, size_t size) +{ + (void)opaque, (void)items, (void)size; + return MZ_MALLOC(items * size); +} +MINIZ_EXPORT void miniz_def_free_func(void *opaque, void *address) +{ + (void)opaque, (void)address; + MZ_FREE(address); +} +MINIZ_EXPORT void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size) +{ + (void)opaque, (void)address, (void)items, (void)size; + return MZ_REALLOC(address, items * size); +} + +const char *mz_version(void) +{ + return MZ_VERSION; +} + +#ifndef MINIZ_NO_ZLIB_APIS + +#ifndef MINIZ_NO_DEFLATE_APIS + +int mz_deflateInit(mz_streamp pStream, int level) +{ + return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY); +} + +int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy) +{ + tdefl_compressor *pComp; + mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy); + + if (!pStream) + return MZ_STREAM_ERROR; + if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))) + return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = MZ_ADLER32_INIT; + pStream->msg = NULL; + pStream->reserved = 0; + pStream->total_in = 0; + pStream->total_out = 0; + if (!pStream->zalloc) + pStream->zalloc = miniz_def_alloc_func; + if (!pStream->zfree) + pStream->zfree = miniz_def_free_func; + + pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor)); + if (!pComp) + return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *)pComp; + + if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY) + { + mz_deflateEnd(pStream); + return MZ_PARAM_ERROR; + } + + return MZ_OK; +} + +int mz_deflateReset(mz_streamp pStream) +{ + if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) + return MZ_STREAM_ERROR; + pStream->total_in = pStream->total_out = 0; + tdefl_init((tdefl_compressor *)pStream->state, NULL, NULL, ((tdefl_compressor *)pStream->state)->m_flags); + return MZ_OK; +} + +int mz_deflate(mz_streamp pStream, int flush) +{ + size_t in_bytes, out_bytes; + mz_ulong orig_total_in, orig_total_out; + int mz_status = MZ_OK; + + if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) + return MZ_STREAM_ERROR; + if (!pStream->avail_out) + return MZ_BUF_ERROR; + + if (flush == MZ_PARTIAL_FLUSH) + flush = MZ_SYNC_FLUSH; + + if (((tdefl_compressor *)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE) + return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR; + + orig_total_in = pStream->total_in; + orig_total_out = pStream->total_out; + for (;;) + { + tdefl_status defl_status; + in_bytes = pStream->avail_in; + out_bytes = pStream->avail_out; + + defl_status = tdefl_compress((tdefl_compressor *)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush); + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tdefl_get_adler32((tdefl_compressor *)pStream->state); + + pStream->next_out += (mz_uint)out_bytes; + pStream->avail_out -= (mz_uint)out_bytes; + pStream->total_out += (mz_uint)out_bytes; + + if (defl_status < 0) + { + mz_status = MZ_STREAM_ERROR; + break; + } + else if (defl_status == TDEFL_STATUS_DONE) + { + mz_status = MZ_STREAM_END; + break; + } + else if (!pStream->avail_out) + break; + else if ((!pStream->avail_in) && (flush != MZ_FINISH)) + { + if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out)) + break; + return MZ_BUF_ERROR; /* Can't make forward progress without some input. + */ + } + } + return mz_status; +} + +int mz_deflateEnd(mz_streamp pStream) +{ + if (!pStream) + return MZ_STREAM_ERROR; + if (pStream->state) + { + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} + +mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) +{ + (void)pStream; + /* This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.) */ + return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); +} + +int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level) +{ + int status; + mz_stream stream; + memset(&stream, 0, sizeof(stream)); + + /* In case mz_ulong is 64-bits (argh I hate longs). */ + if ((mz_uint64)(source_len | *pDest_len) > 0xFFFFFFFFU) + return MZ_PARAM_ERROR; + + stream.next_in = pSource; + stream.avail_in = (mz_uint32)source_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; + + status = mz_deflateInit(&stream, level); + if (status != MZ_OK) + return status; + + status = mz_deflate(&stream, MZ_FINISH); + if (status != MZ_STREAM_END) + { + mz_deflateEnd(&stream); + return (status == MZ_OK) ? MZ_BUF_ERROR : status; + } + + *pDest_len = stream.total_out; + return mz_deflateEnd(&stream); +} + +int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) +{ + return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION); +} + +mz_ulong mz_compressBound(mz_ulong source_len) +{ + return mz_deflateBound(NULL, source_len); +} + +#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ + +#ifndef MINIZ_NO_INFLATE_APIS + +typedef struct +{ + tinfl_decompressor m_decomp; + mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; + int m_window_bits; + mz_uint8 m_dict[TINFL_LZ_DICT_SIZE]; + tinfl_status m_last_status; +} inflate_state; + +int mz_inflateInit2(mz_streamp pStream, int window_bits) +{ + inflate_state *pDecomp; + if (!pStream) + return MZ_STREAM_ERROR; + if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) + return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = 0; + pStream->msg = NULL; + pStream->total_in = 0; + pStream->total_out = 0; + pStream->reserved = 0; + if (!pStream->zalloc) + pStream->zalloc = miniz_def_alloc_func; + if (!pStream->zfree) + pStream->zfree = miniz_def_free_func; + + pDecomp = (inflate_state *)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state)); + if (!pDecomp) + return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *)pDecomp; + + tinfl_init(&pDecomp->m_decomp); + pDecomp->m_dict_ofs = 0; + pDecomp->m_dict_avail = 0; + pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; + pDecomp->m_first_call = 1; + pDecomp->m_has_flushed = 0; + pDecomp->m_window_bits = window_bits; + + return MZ_OK; +} + +int mz_inflateInit(mz_streamp pStream) +{ + return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS); +} + +int mz_inflateReset(mz_streamp pStream) +{ + inflate_state *pDecomp; + if (!pStream) + return MZ_STREAM_ERROR; + + pStream->data_type = 0; + pStream->adler = 0; + pStream->msg = NULL; + pStream->total_in = 0; + pStream->total_out = 0; + pStream->reserved = 0; + + pDecomp = (inflate_state *)pStream->state; + + tinfl_init(&pDecomp->m_decomp); + pDecomp->m_dict_ofs = 0; + pDecomp->m_dict_avail = 0; + pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; + pDecomp->m_first_call = 1; + pDecomp->m_has_flushed = 0; + /* pDecomp->m_window_bits = window_bits */; + + return MZ_OK; +} + +int mz_inflate(mz_streamp pStream, int flush) +{ + inflate_state *pState; + mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32; + size_t in_bytes, out_bytes, orig_avail_in; + tinfl_status status; + + if ((!pStream) || (!pStream->state)) + return MZ_STREAM_ERROR; + if (flush == MZ_PARTIAL_FLUSH) + flush = MZ_SYNC_FLUSH; + if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) + return MZ_STREAM_ERROR; + + pState = (inflate_state *)pStream->state; + if (pState->m_window_bits > 0) + decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER; + orig_avail_in = pStream->avail_in; + + first_call = pState->m_first_call; + pState->m_first_call = 0; + if (pState->m_last_status < 0) + return MZ_DATA_ERROR; + + if (pState->m_has_flushed && (flush != MZ_FINISH)) + return MZ_STREAM_ERROR; + pState->m_has_flushed |= (flush == MZ_FINISH); + + if ((flush == MZ_FINISH) && (first_call)) + { + /* MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file. */ + decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; + in_bytes = pStream->avail_in; + out_bytes = pStream->avail_out; + status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags); + pState->m_last_status = status; + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tinfl_get_adler32(&pState->m_decomp); + pStream->next_out += (mz_uint)out_bytes; + pStream->avail_out -= (mz_uint)out_bytes; + pStream->total_out += (mz_uint)out_bytes; + + if (status < 0) + return MZ_DATA_ERROR; + else if (status != TINFL_STATUS_DONE) + { + pState->m_last_status = TINFL_STATUS_FAILED; + return MZ_BUF_ERROR; + } + return MZ_STREAM_END; + } + /* flush != MZ_FINISH then we must assume there's more input. */ + if (flush != MZ_FINISH) + decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT; + + if (pState->m_dict_avail) + { + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; + pStream->avail_out -= n; + pStream->total_out += n; + pState->m_dict_avail -= n; + pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; + } + + for (;;) + { + in_bytes = pStream->avail_in; + out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs; + + status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags); + pState->m_last_status = status; + + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tinfl_get_adler32(&pState->m_decomp); + + pState->m_dict_avail = (mz_uint)out_bytes; + + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; + pStream->avail_out -= n; + pStream->total_out += n; + pState->m_dict_avail -= n; + pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + + if (status < 0) + return MZ_DATA_ERROR; /* Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well). */ + else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in)) + return MZ_BUF_ERROR; /* Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH. */ + else if (flush == MZ_FINISH) + { + /* The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH. */ + if (status == TINFL_STATUS_DONE) + return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END; + /* status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong. */ + else if (!pStream->avail_out) + return MZ_BUF_ERROR; + } + else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail)) + break; + } + + return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; +} + +int mz_inflateEnd(mz_streamp pStream) +{ + if (!pStream) + return MZ_STREAM_ERROR; + if (pStream->state) + { + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} +int mz_uncompress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong *pSource_len) +{ + mz_stream stream; + int status; + memset(&stream, 0, sizeof(stream)); + + /* In case mz_ulong is 64-bits (argh I hate longs). */ + if ((mz_uint64)(*pSource_len | *pDest_len) > 0xFFFFFFFFU) + return MZ_PARAM_ERROR; + + stream.next_in = pSource; + stream.avail_in = (mz_uint32)*pSource_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; + + status = mz_inflateInit(&stream); + if (status != MZ_OK) + return status; + + status = mz_inflate(&stream, MZ_FINISH); + *pSource_len = *pSource_len - stream.avail_in; + if (status != MZ_STREAM_END) + { + mz_inflateEnd(&stream); + return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status; + } + *pDest_len = stream.total_out; + + return mz_inflateEnd(&stream); +} + +int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) +{ + return mz_uncompress2(pDest, pDest_len, pSource, &source_len); +} + +#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ + +const char *mz_error(int err) +{ + static struct + { + int m_err; + const char *m_pDesc; + } s_error_descs[] = + { + { MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" }, { MZ_DATA_ERROR, "data error" }, { MZ_MEM_ERROR, "out of memory" }, { MZ_BUF_ERROR, "buf error" }, { MZ_VERSION_ERROR, "version error" }, { MZ_PARAM_ERROR, "parameter error" } + }; + mz_uint i; + for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) + if (s_error_descs[i].m_err == err) + return s_error_descs[i].m_pDesc; + return NULL; +} + +#endif /*MINIZ_NO_ZLIB_APIS */ + +#ifdef __cplusplus +} +#endif + +/* + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + For more information, please refer to +*/ +/************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + + + +#ifndef MINIZ_NO_DEFLATE_APIS + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- Low-level Compression (independent from all decompression API's) */ + +/* Purposely making these tables static for faster init and thread safety. */ +static const mz_uint16 s_tdefl_len_sym[256] = + { + 257, 258, 259, 260, 261, 262, 263, 264, 265, 265, 266, 266, 267, 267, 268, 268, 269, 269, 269, 269, 270, 270, 270, 270, 271, 271, 271, 271, 272, 272, 272, 272, + 273, 273, 273, 273, 273, 273, 273, 273, 274, 274, 274, 274, 274, 274, 274, 274, 275, 275, 275, 275, 275, 275, 275, 275, 276, 276, 276, 276, 276, 276, 276, 276, + 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, + 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, + 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, + 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, + 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, + 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 285 + }; + +static const mz_uint8 s_tdefl_len_extra[256] = + { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0 + }; + +static const mz_uint8 s_tdefl_small_dist_sym[512] = + { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17 + }; + +static const mz_uint8 s_tdefl_small_dist_extra[512] = + { + 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7 + }; + +static const mz_uint8 s_tdefl_large_dist_sym[128] = + { + 0, 0, 18, 19, 20, 20, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 + }; + +static const mz_uint8 s_tdefl_large_dist_extra[128] = + { + 0, 0, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13 + }; + +/* Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values. */ +typedef struct +{ + mz_uint16 m_key, m_sym_index; +} tdefl_sym_freq; +static tdefl_sym_freq *tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq *pSyms0, tdefl_sym_freq *pSyms1) +{ + mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; + tdefl_sym_freq *pCur_syms = pSyms0, *pNew_syms = pSyms1; + MZ_CLEAR_ARR(hist); + for (i = 0; i < num_syms; i++) + { + mz_uint freq = pSyms0[i].m_key; + hist[freq & 0xFF]++; + hist[256 + ((freq >> 8) & 0xFF)]++; + } + while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) + total_passes--; + for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8) + { + const mz_uint32 *pHist = &hist[pass << 8]; + mz_uint offsets[256], cur_ofs = 0; + for (i = 0; i < 256; i++) + { + offsets[i] = cur_ofs; + cur_ofs += pHist[i]; + } + for (i = 0; i < num_syms; i++) + pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i]; + { + tdefl_sym_freq *t = pCur_syms; + pCur_syms = pNew_syms; + pNew_syms = t; + } + } + return pCur_syms; +} + +/* tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. */ +static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n) +{ + int root, leaf, next, avbl, used, dpth; + if (n == 0) + return; + else if (n == 1) + { + A[0].m_key = 1; + return; + } + A[0].m_key += A[1].m_key; + root = 0; + leaf = 2; + for (next = 1; next < n - 1; next++) + { + if (leaf >= n || A[root].m_key < A[leaf].m_key) + { + A[next].m_key = A[root].m_key; + A[root++].m_key = (mz_uint16)next; + } + else + A[next].m_key = A[leaf++].m_key; + if (leaf >= n || (root < next && A[root].m_key < A[leaf].m_key)) + { + A[next].m_key = (mz_uint16)(A[next].m_key + A[root].m_key); + A[root++].m_key = (mz_uint16)next; + } + else + A[next].m_key = (mz_uint16)(A[next].m_key + A[leaf++].m_key); + } + A[n - 2].m_key = 0; + for (next = n - 3; next >= 0; next--) + A[next].m_key = A[A[next].m_key].m_key + 1; + avbl = 1; + used = dpth = 0; + root = n - 2; + next = n - 1; + while (avbl > 0) + { + while (root >= 0 && (int)A[root].m_key == dpth) + { + used++; + root--; + } + while (avbl > used) + { + A[next--].m_key = (mz_uint16)(dpth); + avbl--; + } + avbl = 2 * used; + dpth++; + used = 0; + } +} + +/* Limits canonical Huffman code table's max code size. */ +enum +{ + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 +}; +static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size) +{ + int i; + mz_uint32 total = 0; + if (code_list_len <= 1) + return; + for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) + pNum_codes[max_code_size] += pNum_codes[i]; + for (i = max_code_size; i > 0; i--) + total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i)); + while (total != (1UL << max_code_size)) + { + pNum_codes[max_code_size]--; + for (i = max_code_size - 1; i > 0; i--) + if (pNum_codes[i]) + { + pNum_codes[i]--; + pNum_codes[i + 1] += 2; + break; + } + total--; + } +} + +static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table) +{ + int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; + mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; + MZ_CLEAR_ARR(num_codes); + if (static_table) + { + for (i = 0; i < table_len; i++) + num_codes[d->m_huff_code_sizes[table_num][i]]++; + } + else + { + tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms; + int num_used_syms = 0; + const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0]; + for (i = 0; i < table_len; i++) + if (pSym_count[i]) + { + syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; + syms0[num_used_syms++].m_sym_index = (mz_uint16)i; + } + + pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); + tdefl_calculate_minimum_redundancy(pSyms, num_used_syms); + + for (i = 0; i < num_used_syms; i++) + num_codes[pSyms[i].m_key]++; + + tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit); + + MZ_CLEAR_ARR(d->m_huff_code_sizes[table_num]); + MZ_CLEAR_ARR(d->m_huff_codes[table_num]); + for (i = 1, j = num_used_syms; i <= code_size_limit; i++) + for (l = num_codes[i]; l > 0; l--) + d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i); + } + + next_code[1] = 0; + for (j = 0, i = 2; i <= code_size_limit; i++) + next_code[i] = j = ((j + num_codes[i - 1]) << 1); + + for (i = 0; i < table_len; i++) + { + mz_uint rev_code = 0, code, code_size; + if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) + continue; + code = next_code[code_size]++; + for (l = code_size; l > 0; l--, code >>= 1) + rev_code = (rev_code << 1) | (code & 1); + d->m_huff_codes[table_num][i] = (mz_uint16)rev_code; + } +} + +#define TDEFL_PUT_BITS(b, l) \ + do \ + { \ + mz_uint bits = b; \ + mz_uint len = l; \ + MZ_ASSERT(bits <= ((1U << len) - 1U)); \ + d->m_bit_buffer |= (bits << d->m_bits_in); \ + d->m_bits_in += len; \ + while (d->m_bits_in >= 8) \ + { \ + if (d->m_pOutput_buf < d->m_pOutput_buf_end) \ + *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \ + d->m_bit_buffer >>= 8; \ + d->m_bits_in -= 8; \ + } \ + } \ + MZ_MACRO_END + +#define TDEFL_RLE_PREV_CODE_SIZE() \ + { \ + if (rle_repeat_count) \ + { \ + if (rle_repeat_count < 3) \ + { \ + d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \ + while (rle_repeat_count--) \ + packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \ + } \ + else \ + { \ + d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 16; \ + packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_repeat_count - 3); \ + } \ + rle_repeat_count = 0; \ + } \ + } + +#define TDEFL_RLE_ZERO_CODE_SIZE() \ + { \ + if (rle_z_count) \ + { \ + if (rle_z_count < 3) \ + { \ + d->m_huff_count[2][0] = (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); \ + while (rle_z_count--) \ + packed_code_sizes[num_packed_code_sizes++] = 0; \ + } \ + else if (rle_z_count <= 10) \ + { \ + d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 17; \ + packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 3); \ + } \ + else \ + { \ + d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 18; \ + packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 11); \ + } \ + rle_z_count = 0; \ + } \ + } + +static const mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + +static void tdefl_start_dynamic_block(tdefl_compressor *d) +{ + int num_lit_codes, num_dist_codes, num_bit_lengths; + mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index; + mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF; + + d->m_huff_count[0][256] = 1; + + tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE); + tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE); + + for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) + if (d->m_huff_code_sizes[0][num_lit_codes - 1]) + break; + for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) + if (d->m_huff_code_sizes[1][num_dist_codes - 1]) + break; + + memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes); + memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes); + total_code_sizes_to_pack = num_lit_codes + num_dist_codes; + num_packed_code_sizes = 0; + rle_z_count = 0; + rle_repeat_count = 0; + + memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2); + for (i = 0; i < total_code_sizes_to_pack; i++) + { + mz_uint8 code_size = code_sizes_to_pack[i]; + if (!code_size) + { + TDEFL_RLE_PREV_CODE_SIZE(); + if (++rle_z_count == 138) + { + TDEFL_RLE_ZERO_CODE_SIZE(); + } + } + else + { + TDEFL_RLE_ZERO_CODE_SIZE(); + if (code_size != prev_code_size) + { + TDEFL_RLE_PREV_CODE_SIZE(); + d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1); + packed_code_sizes[num_packed_code_sizes++] = code_size; + } + else if (++rle_repeat_count == 6) + { + TDEFL_RLE_PREV_CODE_SIZE(); + } + } + prev_code_size = code_size; + } + if (rle_repeat_count) + { + TDEFL_RLE_PREV_CODE_SIZE(); + } + else + { + TDEFL_RLE_ZERO_CODE_SIZE(); + } + + tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE); + + TDEFL_PUT_BITS(2, 2); + + TDEFL_PUT_BITS(num_lit_codes - 257, 5); + TDEFL_PUT_BITS(num_dist_codes - 1, 5); + + for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) + if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) + break; + num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); + TDEFL_PUT_BITS(num_bit_lengths - 4, 4); + for (i = 0; (int)i < num_bit_lengths; i++) + TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3); + + for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes;) + { + mz_uint code = packed_code_sizes[packed_code_sizes_index++]; + MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2); + TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]); + if (code >= 16) + TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]); + } +} + +static void tdefl_start_static_block(tdefl_compressor *d) +{ + mz_uint i; + mz_uint8 *p = &d->m_huff_code_sizes[0][0]; + + for (i = 0; i <= 143; ++i) + *p++ = 8; + for (; i <= 255; ++i) + *p++ = 9; + for (; i <= 279; ++i) + *p++ = 7; + for (; i <= 287; ++i) + *p++ = 8; + + memset(d->m_huff_code_sizes[1], 5, 32); + + tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE); + tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE); + + TDEFL_PUT_BITS(1, 2); +} + +static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS +static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) +{ + mz_uint flags; + mz_uint8 *pLZ_codes; + mz_uint8 *pOutput_buf = d->m_pOutput_buf; + mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf; + mz_uint64 bit_buffer = d->m_bit_buffer; + mz_uint bits_in = d->m_bits_in; + +#define TDEFL_PUT_BITS_FAST(b, l) \ + { \ + bit_buffer |= (((mz_uint64)(b)) << bits_in); \ + bits_in += (l); \ + } + + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1) + { + if (flags == 1) + flags = *pLZ_codes++ | 0x100; + + if (flags & 1) + { + mz_uint s0, s1, n0, n1, sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0]; + mz_uint match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); + pLZ_codes += 3; + + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); + + /* This sequence coaxes MSVC into using cmov's vs. jmp's. */ + s0 = s_tdefl_small_dist_sym[match_dist & 511]; + n0 = s_tdefl_small_dist_extra[match_dist & 511]; + s1 = s_tdefl_large_dist_sym[match_dist >> 8]; + n1 = s_tdefl_large_dist_extra[match_dist >> 8]; + sym = (match_dist < 512) ? s0 : s1; + num_extra_bits = (match_dist < 512) ? n0 : n1; + + MZ_ASSERT(d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); + } + else + { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) + { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) + { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + } + } + } + + if (pOutput_buf >= d->m_pOutput_buf_end) + return MZ_FALSE; + + memcpy(pOutput_buf, &bit_buffer, sizeof(mz_uint64)); + pOutput_buf += (bits_in >> 3); + bit_buffer >>= (bits_in & ~7); + bits_in &= 7; + } + +#undef TDEFL_PUT_BITS_FAST + + d->m_pOutput_buf = pOutput_buf; + d->m_bits_in = 0; + d->m_bit_buffer = 0; + + while (bits_in) + { + mz_uint32 n = MZ_MIN(bits_in, 16); + TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n); + bit_buffer >>= n; + bits_in -= n; + } + + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); + + return (d->m_pOutput_buf < d->m_pOutput_buf_end); +} +#else +static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) +{ + mz_uint flags; + mz_uint8 *pLZ_codes; + + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1) + { + if (flags == 1) + flags = *pLZ_codes++ | 0x100; + if (flags & 1) + { + mz_uint sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); + pLZ_codes += 3; + + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); + + if (match_dist < 512) + { + sym = s_tdefl_small_dist_sym[match_dist]; + num_extra_bits = s_tdefl_small_dist_extra[match_dist]; + } + else + { + sym = s_tdefl_large_dist_sym[match_dist >> 8]; + num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8]; + } + MZ_ASSERT(d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); + } + else + { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + } + } + + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); + + return (d->m_pOutput_buf < d->m_pOutput_buf_end); +} +#endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS */ + +static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block) +{ + if (static_block) + tdefl_start_static_block(d); + else + tdefl_start_dynamic_block(d); + return tdefl_compress_lz_codes(d); +} + +static const mz_uint s_tdefl_num_probes[11]; + +static int tdefl_flush_block(tdefl_compressor *d, int flush) +{ + mz_uint saved_bit_buf, saved_bits_in; + mz_uint8 *pSaved_output_buf; + mz_bool comp_block_succeeded = MZ_FALSE; + int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size; + mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf; + + d->m_pOutput_buf = pOutput_buf_start; + d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16; + + MZ_ASSERT(!d->m_output_flush_remaining); + d->m_output_flush_ofs = 0; + d->m_output_flush_remaining = 0; + + *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left); + d->m_pLZ_code_buf -= (d->m_num_flags_left == 8); + + if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) + { + const mz_uint8 cmf = 0x78; + mz_uint8 flg, flevel = 3; + mz_uint header, i, mz_un = sizeof(s_tdefl_num_probes) / sizeof(mz_uint); + + /* Determine compression level by reversing the process in tdefl_create_comp_flags_from_zip_params() */ + for (i = 0; i < mz_un; i++) + if (s_tdefl_num_probes[i] == (d->m_flags & 0xFFF)) break; + + if (i < 2) + flevel = 0; + else if (i < 6) + flevel = 1; + else if (i == 6) + flevel = 2; + + header = cmf << 8 | (flevel << 6); + header += 31 - (header % 31); + flg = header & 0xFF; + + TDEFL_PUT_BITS(cmf, 8); + TDEFL_PUT_BITS(flg, 8); + } + + TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); + + pSaved_output_buf = d->m_pOutput_buf; + saved_bit_buf = d->m_bit_buffer; + saved_bits_in = d->m_bits_in; + + if (!use_raw_block) + comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48)); + + /* If the block gets expanded, forget the current contents of the output buffer and send a raw block instead. */ + if (((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) && + ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size)) + { + mz_uint i; + d->m_pOutput_buf = pSaved_output_buf; + d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + TDEFL_PUT_BITS(0, 2); + if (d->m_bits_in) + { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF) + { + TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16); + } + for (i = 0; i < d->m_total_lz_bytes; ++i) + { + TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8); + } + } + /* Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes. */ + else if (!comp_block_succeeded) + { + d->m_pOutput_buf = pSaved_output_buf; + d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + tdefl_compress_block(d, MZ_TRUE); + } + + if (flush) + { + if (flush == TDEFL_FINISH) + { + if (d->m_bits_in) + { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) + { + mz_uint i, a = d->m_adler32; + for (i = 0; i < 4; i++) + { + TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); + a <<= 8; + } + } + } + else + { + mz_uint i, z = 0; + TDEFL_PUT_BITS(0, 3); + if (d->m_bits_in) + { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + for (i = 2; i; --i, z ^= 0xFFFF) + { + TDEFL_PUT_BITS(z & 0xFFFF, 16); + } + } + } + + MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end); + + memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + + d->m_pLZ_code_buf = d->m_lz_code_buf + 1; + d->m_pLZ_flags = d->m_lz_code_buf; + d->m_num_flags_left = 8; + d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; + d->m_total_lz_bytes = 0; + d->m_block_index++; + + if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0) + { + if (d->m_pPut_buf_func) + { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; + if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user)) + return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED); + } + else if (pOutput_buf_start == d->m_output_buf) + { + int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs)); + memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy); + d->m_out_buf_ofs += bytes_to_copy; + if ((n -= bytes_to_copy) != 0) + { + d->m_output_flush_ofs = bytes_to_copy; + d->m_output_flush_remaining = n; + } + } + else + { + d->m_out_buf_ofs += n; + } + } + + return d->m_output_flush_remaining; +} + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES +#ifdef MINIZ_UNALIGNED_USE_MEMCPY +static mz_uint16 TDEFL_READ_UNALIGNED_WORD(const mz_uint8* p) +{ + mz_uint16 ret; + memcpy(&ret, p, sizeof(mz_uint16)); + return ret; +} +static mz_uint16 TDEFL_READ_UNALIGNED_WORD2(const mz_uint16* p) +{ + mz_uint16 ret; + memcpy(&ret, p, sizeof(mz_uint16)); + return ret; +} +#else +#define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16 *)(p) +#define TDEFL_READ_UNALIGNED_WORD2(p) *(const mz_uint16 *)(p) +#endif +static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) +{ + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint16 *s = (const mz_uint16 *)(d->m_dict + pos), *p, *q; + mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD2(s); + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); + if (max_match_len <= match_len) + return; + for (;;) + { + for (;;) + { + if (--num_probes_left == 0) + return; +#define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ + return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) \ + break; + TDEFL_PROBE; + TDEFL_PROBE; + TDEFL_PROBE; + } + if (!dist) + break; + q = (const mz_uint16 *)(d->m_dict + probe_pos); + if (TDEFL_READ_UNALIGNED_WORD2(q) != s01) + continue; + p = s; + probe_len = 32; + do + { + } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && + (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0)); + if (!probe_len) + { + *pMatch_dist = dist; + *pMatch_len = MZ_MIN(max_match_len, (mz_uint)TDEFL_MAX_MATCH_LEN); + break; + } + else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q)) > match_len) + { + *pMatch_dist = dist; + if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) + break; + c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]); + } + } +} +#else +static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) +{ + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint8 *s = d->m_dict + pos, *p, *q; + mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1]; + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); + if (max_match_len <= match_len) + return; + for (;;) + { + for (;;) + { + if (--num_probes_left == 0) + return; +#define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ + return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) \ + break; + TDEFL_PROBE; + TDEFL_PROBE; + TDEFL_PROBE; + } + if (!dist) + break; + p = s; + q = d->m_dict + probe_pos; + for (probe_len = 0; probe_len < max_match_len; probe_len++) + if (*p++ != *q++) + break; + if (probe_len > match_len) + { + *pMatch_dist = dist; + if ((*pMatch_len = match_len = probe_len) == max_match_len) + return; + c0 = d->m_dict[pos + match_len]; + c1 = d->m_dict[pos + match_len - 1]; + } + } +} +#endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES */ + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN +#ifdef MINIZ_UNALIGNED_USE_MEMCPY +static mz_uint32 TDEFL_READ_UNALIGNED_WORD32(const mz_uint8* p) +{ + mz_uint32 ret; + memcpy(&ret, p, sizeof(mz_uint32)); + return ret; +} +#else +#define TDEFL_READ_UNALIGNED_WORD32(p) *(const mz_uint32 *)(p) +#endif +static mz_bool tdefl_compress_fast(tdefl_compressor *d) +{ + /* Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio. */ + mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left; + mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags; + mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; + + while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) + { + const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096; + mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; + mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size); + d->m_src_buf_left -= num_bytes_to_process; + lookahead_size += num_bytes_to_process; + + while (num_bytes_to_process) + { + mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process); + memcpy(d->m_dict + dst_pos, d->m_pSrc, n); + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos)); + d->m_pSrc += n; + dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK; + num_bytes_to_process -= n; + } + + dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size); + if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) + break; + + while (lookahead_size >= 4) + { + mz_uint cur_match_dist, cur_match_len = 1; + mz_uint8 *pCur_dict = d->m_dict + cur_pos; + mz_uint first_trigram = TDEFL_READ_UNALIGNED_WORD32(pCur_dict) & 0xFFFFFF; + mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK; + mz_uint probe_pos = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)lookahead_pos; + + if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((TDEFL_READ_UNALIGNED_WORD32(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram)) + { + const mz_uint16 *p = (const mz_uint16 *)pCur_dict; + const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos); + mz_uint32 probe_len = 32; + do + { + } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && + (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0)); + cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q); + if (!probe_len) + cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0; + + if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U))) + { + cur_match_len = 1; + *pLZ_code_buf++ = (mz_uint8)first_trigram; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + d->m_huff_count[0][(mz_uint8)first_trigram]++; + } + else + { + mz_uint32 s0, s1; + cur_match_len = MZ_MIN(cur_match_len, lookahead_size); + + MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE)); + + cur_match_dist--; + + pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN); +#ifdef MINIZ_UNALIGNED_USE_MEMCPY + memcpy(&pLZ_code_buf[1], &cur_match_dist, sizeof(cur_match_dist)); +#else + *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist; +#endif + pLZ_code_buf += 3; + *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80); + + s0 = s_tdefl_small_dist_sym[cur_match_dist & 511]; + s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8]; + d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++; + + d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++; + } + } + else + { + *pLZ_code_buf++ = (mz_uint8)first_trigram; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + d->m_huff_count[0][(mz_uint8)first_trigram]++; + } + + if (--num_flags_left == 0) + { + num_flags_left = 8; + pLZ_flags = pLZ_code_buf++; + } + + total_lz_bytes += cur_match_len; + lookahead_pos += cur_match_len; + dict_size = MZ_MIN(dict_size + cur_match_len, (mz_uint)TDEFL_LZ_DICT_SIZE); + cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK; + MZ_ASSERT(lookahead_size >= cur_match_len); + lookahead_size -= cur_match_len; + + if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) + { + int n; + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + total_lz_bytes = d->m_total_lz_bytes; + pLZ_code_buf = d->m_pLZ_code_buf; + pLZ_flags = d->m_pLZ_flags; + num_flags_left = d->m_num_flags_left; + } + } + + while (lookahead_size) + { + mz_uint8 lit = d->m_dict[cur_pos]; + + total_lz_bytes++; + *pLZ_code_buf++ = lit; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + if (--num_flags_left == 0) + { + num_flags_left = 8; + pLZ_flags = pLZ_code_buf++; + } + + d->m_huff_count[0][lit]++; + + lookahead_pos++; + dict_size = MZ_MIN(dict_size + 1, (mz_uint)TDEFL_LZ_DICT_SIZE); + cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; + lookahead_size--; + + if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) + { + int n; + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + total_lz_bytes = d->m_total_lz_bytes; + pLZ_code_buf = d->m_pLZ_code_buf; + pLZ_flags = d->m_pLZ_flags; + num_flags_left = d->m_num_flags_left; + } + } + } + + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + return MZ_TRUE; +} +#endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ + +static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit) +{ + d->m_total_lz_bytes++; + *d->m_pLZ_code_buf++ = lit; + *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); + if (--d->m_num_flags_left == 0) + { + d->m_num_flags_left = 8; + d->m_pLZ_flags = d->m_pLZ_code_buf++; + } + d->m_huff_count[0][lit]++; +} + +static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist) +{ + mz_uint32 s0, s1; + + MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE)); + + d->m_total_lz_bytes += match_len; + + d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN); + + match_dist -= 1; + d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF); + d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); + d->m_pLZ_code_buf += 3; + + *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); + if (--d->m_num_flags_left == 0) + { + d->m_num_flags_left = 8; + d->m_pLZ_flags = d->m_pLZ_code_buf++; + } + + s0 = s_tdefl_small_dist_sym[match_dist & 511]; + s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127]; + d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++; + d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; +} + +static mz_bool tdefl_compress_normal(tdefl_compressor *d) +{ + const mz_uint8 *pSrc = d->m_pSrc; + size_t src_buf_left = d->m_src_buf_left; + tdefl_flush flush = d->m_flush; + + while ((src_buf_left) || ((flush) && (d->m_lookahead_size))) + { + mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos; + /* Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN. */ + if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1)) + { + mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2; + mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK]; + mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); + const mz_uint8 *pSrc_end = pSrc ? pSrc + num_bytes_to_process : NULL; + src_buf_left -= num_bytes_to_process; + d->m_lookahead_size += num_bytes_to_process; + while (pSrc != pSrc_end) + { + mz_uint8 c = *pSrc++; + d->m_dict[dst_pos] = c; + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; + hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)(ins_pos); + dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; + ins_pos++; + } + } + else + { + while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) + { + mz_uint8 c = *pSrc++; + mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; + src_buf_left--; + d->m_dict[dst_pos] = c; + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; + if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN) + { + mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2; + mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)(ins_pos); + } + } + } + d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size); + if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) + break; + + /* Simple lazy/greedy parsing state machine. */ + len_to_move = 1; + cur_match_dist = 0; + cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); + cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; + if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS)) + { + if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) + { + mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK]; + cur_match_len = 0; + while (cur_match_len < d->m_lookahead_size) + { + if (d->m_dict[cur_pos + cur_match_len] != c) + break; + cur_match_len++; + } + if (cur_match_len < TDEFL_MIN_MATCH_LEN) + cur_match_len = 0; + else + cur_match_dist = 1; + } + } + else + { + tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len); + } + if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5))) + { + cur_match_dist = cur_match_len = 0; + } + if (d->m_saved_match_len) + { + if (cur_match_len > d->m_saved_match_len) + { + tdefl_record_literal(d, (mz_uint8)d->m_saved_lit); + if (cur_match_len >= 128) + { + tdefl_record_match(d, cur_match_len, cur_match_dist); + d->m_saved_match_len = 0; + len_to_move = cur_match_len; + } + else + { + d->m_saved_lit = d->m_dict[cur_pos]; + d->m_saved_match_dist = cur_match_dist; + d->m_saved_match_len = cur_match_len; + } + } + else + { + tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist); + len_to_move = d->m_saved_match_len - 1; + d->m_saved_match_len = 0; + } + } + else if (!cur_match_dist) + tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]); + else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128)) + { + tdefl_record_match(d, cur_match_len, cur_match_dist); + len_to_move = cur_match_len; + } + else + { + d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; + d->m_saved_match_dist = cur_match_dist; + d->m_saved_match_len = cur_match_len; + } + /* Move the lookahead forward by len_to_move bytes. */ + d->m_lookahead_pos += len_to_move; + MZ_ASSERT(d->m_lookahead_size >= len_to_move); + d->m_lookahead_size -= len_to_move; + d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, (mz_uint)TDEFL_LZ_DICT_SIZE); + /* Check if it's time to flush the current LZ codes to the internal output buffer. */ + if ((d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) || + ((d->m_total_lz_bytes > 31 * 1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS)))) + { + int n; + d->m_pSrc = pSrc; + d->m_src_buf_left = src_buf_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + } + } + + d->m_pSrc = pSrc; + d->m_src_buf_left = src_buf_left; + return MZ_TRUE; +} + +static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d) +{ + if (d->m_pIn_buf_size) + { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; + } + + if (d->m_pOut_buf_size) + { + size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining); + memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n); + d->m_output_flush_ofs += (mz_uint)n; + d->m_output_flush_remaining -= (mz_uint)n; + d->m_out_buf_ofs += n; + + *d->m_pOut_buf_size = d->m_out_buf_ofs; + } + + return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY; +} + +tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush) +{ + if (!d) + { + if (pIn_buf_size) + *pIn_buf_size = 0; + if (pOut_buf_size) + *pOut_buf_size = 0; + return TDEFL_STATUS_BAD_PARAM; + } + + d->m_pIn_buf = pIn_buf; + d->m_pIn_buf_size = pIn_buf_size; + d->m_pOut_buf = pOut_buf; + d->m_pOut_buf_size = pOut_buf_size; + d->m_pSrc = (const mz_uint8 *)(pIn_buf); + d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0; + d->m_out_buf_ofs = 0; + d->m_flush = flush; + + if (((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) || + (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf)) + { + if (pIn_buf_size) + *pIn_buf_size = 0; + if (pOut_buf_size) + *pOut_buf_size = 0; + return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM); + } + d->m_wants_to_finish |= (flush == TDEFL_FINISH); + + if ((d->m_output_flush_remaining) || (d->m_finished)) + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) && + ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) && + ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0)) + { + if (!tdefl_compress_fast(d)) + return d->m_prev_return_status; + } + else +#endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ + { + if (!tdefl_compress_normal(d)) + return d->m_prev_return_status; + } + + if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf)) + d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf); + + if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining)) + { + if (tdefl_flush_block(d, flush) < 0) + return d->m_prev_return_status; + d->m_finished = (flush == TDEFL_FINISH); + if (flush == TDEFL_FULL_FLUSH) + { + MZ_CLEAR_ARR(d->m_hash); + MZ_CLEAR_ARR(d->m_next); + d->m_dict_size = 0; + } + } + + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); +} + +tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush) +{ + MZ_ASSERT(d->m_pPut_buf_func); + return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush); +} + +tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + d->m_pPut_buf_func = pPut_buf_func; + d->m_pPut_buf_user = pPut_buf_user; + d->m_flags = (mz_uint)(flags); + d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; + d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0; + d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; + if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) + MZ_CLEAR_ARR(d->m_hash); + d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0; + d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0; + d->m_pLZ_code_buf = d->m_lz_code_buf + 1; + d->m_pLZ_flags = d->m_lz_code_buf; + *d->m_pLZ_flags = 0; + d->m_num_flags_left = 8; + d->m_pOutput_buf = d->m_output_buf; + d->m_pOutput_buf_end = d->m_output_buf; + d->m_prev_return_status = TDEFL_STATUS_OKAY; + d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; + d->m_adler32 = 1; + d->m_pIn_buf = NULL; + d->m_pOut_buf = NULL; + d->m_pIn_buf_size = NULL; + d->m_pOut_buf_size = NULL; + d->m_flush = TDEFL_NO_FLUSH; + d->m_pSrc = NULL; + d->m_src_buf_left = 0; + d->m_out_buf_ofs = 0; + if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) + MZ_CLEAR_ARR(d->m_dict); + memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + return TDEFL_STATUS_OKAY; +} + +tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d) +{ + return d->m_prev_return_status; +} + +mz_uint32 tdefl_get_adler32(tdefl_compressor *d) +{ + return d->m_adler32; +} + +mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + tdefl_compressor *pComp; + mz_bool succeeded; + if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) + return MZ_FALSE; + pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); + if (!pComp) + return MZ_FALSE; + succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY); + succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE); + MZ_FREE(pComp); + return succeeded; +} + +typedef struct +{ + size_t m_size, m_capacity; + mz_uint8 *m_pBuf; + mz_bool m_expandable; +} tdefl_output_buffer; + +static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser) +{ + tdefl_output_buffer *p = (tdefl_output_buffer *)pUser; + size_t new_size = p->m_size + len; + if (new_size > p->m_capacity) + { + size_t new_capacity = p->m_capacity; + mz_uint8 *pNew_buf; + if (!p->m_expandable) + return MZ_FALSE; + do + { + new_capacity = MZ_MAX(128U, new_capacity << 1U); + } while (new_size > new_capacity); + pNew_buf = (mz_uint8 *)MZ_REALLOC(p->m_pBuf, new_capacity); + if (!pNew_buf) + return MZ_FALSE; + p->m_pBuf = pNew_buf; + p->m_capacity = new_capacity; + } + memcpy((mz_uint8 *)p->m_pBuf + p->m_size, pBuf, len); + p->m_size = new_size; + return MZ_TRUE; +} + +void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) +{ + tdefl_output_buffer out_buf; + MZ_CLEAR_OBJ(out_buf); + if (!pOut_len) + return MZ_FALSE; + else + *pOut_len = 0; + out_buf.m_expandable = MZ_TRUE; + if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) + return NULL; + *pOut_len = out_buf.m_size; + return out_buf.m_pBuf; +} + +size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) +{ + tdefl_output_buffer out_buf; + MZ_CLEAR_OBJ(out_buf); + if (!pOut_buf) + return 0; + out_buf.m_pBuf = (mz_uint8 *)pOut_buf; + out_buf.m_capacity = out_buf_len; + if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) + return 0; + return out_buf.m_size; +} + +static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; + +/* level may actually range from [0,10] (10 is a "hidden" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files). */ +mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy) +{ + mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); + if (window_bits > 0) + comp_flags |= TDEFL_WRITE_ZLIB_HEADER; + + if (!level) + comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; + else if (strategy == MZ_FILTERED) + comp_flags |= TDEFL_FILTER_MATCHES; + else if (strategy == MZ_HUFFMAN_ONLY) + comp_flags &= ~TDEFL_MAX_PROBES_MASK; + else if (strategy == MZ_FIXED) + comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS; + else if (strategy == MZ_RLE) + comp_flags |= TDEFL_RLE_MATCHES; + + return comp_flags; +} + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4204) /* nonstandard extension used : non-constant aggregate initializer (also supported by GNU C and C99, so no big deal) */ +#endif + +/* Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at + http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/. + This is actually a modification of Alex's original code so PNG files generated by this function pass pngcheck. */ +void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip) +{ + /* Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was defined. */ + static const mz_uint s_tdefl_png_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; + tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); + tdefl_output_buffer out_buf; + int i, bpl = w * num_chans, y, z; + mz_uint32 c; + *pLen_out = 0; + if (!pComp) + return NULL; + MZ_CLEAR_OBJ(out_buf); + out_buf.m_expandable = MZ_TRUE; + out_buf.m_capacity = 57 + MZ_MAX(64, (1 + bpl) * h); + if (NULL == (out_buf.m_pBuf = (mz_uint8 *)MZ_MALLOC(out_buf.m_capacity))) + { + MZ_FREE(pComp); + return NULL; + } + /* write dummy header */ + for (z = 41; z; --z) + tdefl_output_buffer_putter(&z, 1, &out_buf); + /* compress image data */ + tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER); + for (y = 0; y < h; ++y) + { + tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); + tdefl_compress_buffer(pComp, (mz_uint8 *)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); + } + if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) + { + MZ_FREE(pComp); + MZ_FREE(out_buf.m_pBuf); + return NULL; + } + /* write real header */ + *pLen_out = out_buf.m_size - 41; + { + static const mz_uint8 chans[] = { 0x00, 0x00, 0x04, 0x02, 0x06 }; + mz_uint8 pnghdr[41] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, + 0x0a, 0x1a, 0x0a, 0x00, 0x00, + 0x00, 0x0d, 0x49, 0x48, 0x44, + 0x52, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x49, 0x44, 0x41, + 0x54 }; + pnghdr[18] = (mz_uint8)(w >> 8); + pnghdr[19] = (mz_uint8)w; + pnghdr[22] = (mz_uint8)(h >> 8); + pnghdr[23] = (mz_uint8)h; + pnghdr[25] = chans[num_chans]; + pnghdr[33] = (mz_uint8)(*pLen_out >> 24); + pnghdr[34] = (mz_uint8)(*pLen_out >> 16); + pnghdr[35] = (mz_uint8)(*pLen_out >> 8); + pnghdr[36] = (mz_uint8)*pLen_out; + c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, pnghdr + 12, 17); + for (i = 0; i < 4; ++i, c <<= 8) + ((mz_uint8 *)(pnghdr + 29))[i] = (mz_uint8)(c >> 24); + memcpy(out_buf.m_pBuf, pnghdr, 41); + } + /* write footer (IDAT CRC-32, followed by IEND chunk) */ + if (!tdefl_output_buffer_putter("\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) + { + *pLen_out = 0; + MZ_FREE(pComp); + MZ_FREE(out_buf.m_pBuf); + return NULL; + } + c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, out_buf.m_pBuf + 41 - 4, *pLen_out + 4); + for (i = 0; i < 4; ++i, c <<= 8) + (out_buf.m_pBuf + out_buf.m_size - 16)[i] = (mz_uint8)(c >> 24); + /* compute final size of file, grab compressed data buffer and return */ + *pLen_out += 57; + MZ_FREE(pComp); + return out_buf.m_pBuf; +} +void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out) +{ + /* Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's where #defined out) */ + return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, pLen_out, 6, MZ_FALSE); +} + +#ifndef MINIZ_NO_MALLOC +/* Allocate the tdefl_compressor and tinfl_decompressor structures in C so that */ +/* non-C language bindings to tdefL_ and tinfl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ +tdefl_compressor *tdefl_compressor_alloc(void) +{ + return (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); +} + +void tdefl_compressor_free(tdefl_compressor *pComp) +{ + MZ_FREE(pComp); +} +#endif + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ + /************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + + + +#ifndef MINIZ_NO_INFLATE_APIS + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- Low-level Decompression (completely independent from all compression API's) */ + +#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l) +#define TINFL_MEMSET(p, c, l) memset(p, c, l) + +#define TINFL_CR_BEGIN \ + switch (r->m_state) \ + { \ + case 0: +#define TINFL_CR_RETURN(state_index, result) \ + do \ + { \ + status = result; \ + r->m_state = state_index; \ + goto common_exit; \ + case state_index:; \ + } \ + MZ_MACRO_END +#define TINFL_CR_RETURN_FOREVER(state_index, result) \ + do \ + { \ + for (;;) \ + { \ + TINFL_CR_RETURN(state_index, result); \ + } \ + } \ + MZ_MACRO_END +#define TINFL_CR_FINISH } + +#define TINFL_GET_BYTE(state_index, c) \ + do \ + { \ + while (pIn_buf_cur >= pIn_buf_end) \ + { \ + TINFL_CR_RETURN(state_index, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); \ + } \ + c = *pIn_buf_cur++; \ + } \ + MZ_MACRO_END + +#define TINFL_NEED_BITS(state_index, n) \ + do \ + { \ + mz_uint c; \ + TINFL_GET_BYTE(state_index, c); \ + bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ + num_bits += 8; \ + } while (num_bits < (mz_uint)(n)) +#define TINFL_SKIP_BITS(state_index, n) \ + do \ + { \ + if (num_bits < (mz_uint)(n)) \ + { \ + TINFL_NEED_BITS(state_index, n); \ + } \ + bit_buf >>= (n); \ + num_bits -= (n); \ + } \ + MZ_MACRO_END +#define TINFL_GET_BITS(state_index, b, n) \ + do \ + { \ + if (num_bits < (mz_uint)(n)) \ + { \ + TINFL_NEED_BITS(state_index, n); \ + } \ + b = bit_buf & ((1 << (n)) - 1); \ + bit_buf >>= (n); \ + num_bits -= (n); \ + } \ + MZ_MACRO_END + +/* TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. */ +/* It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a */ +/* Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the */ +/* bit buffer contains >=15 bits (deflate's max. Huffman code size). */ +#define TINFL_HUFF_BITBUF_FILL(state_index, pLookUp, pTree) \ + do \ + { \ + temp = pLookUp[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ + if (temp >= 0) \ + { \ + code_len = temp >> 9; \ + if ((code_len) && (num_bits >= code_len)) \ + break; \ + } \ + else if (num_bits > TINFL_FAST_LOOKUP_BITS) \ + { \ + code_len = TINFL_FAST_LOOKUP_BITS; \ + do \ + { \ + temp = pTree[~temp + ((bit_buf >> code_len++) & 1)]; \ + } while ((temp < 0) && (num_bits >= (code_len + 1))); \ + if (temp >= 0) \ + break; \ + } \ + TINFL_GET_BYTE(state_index, c); \ + bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ + num_bits += 8; \ + } while (num_bits < 15); + +/* TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read */ +/* beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully */ +/* decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32. */ +/* The slow path is only executed at the very end of the input buffer. */ +/* v1.16: The original macro handled the case at the very end of the passed-in input buffer, but we also need to handle the case where the user passes in 1+zillion bytes */ +/* following the deflate data and our non-conservative read-ahead path won't kick in here on this code. This is much trickier. */ +#define TINFL_HUFF_DECODE(state_index, sym, pLookUp, pTree) \ + do \ + { \ + int temp; \ + mz_uint code_len, c; \ + if (num_bits < 15) \ + { \ + if ((pIn_buf_end - pIn_buf_cur) < 2) \ + { \ + TINFL_HUFF_BITBUF_FILL(state_index, pLookUp, pTree); \ + } \ + else \ + { \ + bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); \ + pIn_buf_cur += 2; \ + num_bits += 16; \ + } \ + } \ + if ((temp = pLookUp[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ + code_len = temp >> 9, temp &= 511; \ + else \ + { \ + code_len = TINFL_FAST_LOOKUP_BITS; \ + do \ + { \ + temp = pTree[~temp + ((bit_buf >> code_len++) & 1)]; \ + } while (temp < 0); \ + } \ + sym = temp; \ + bit_buf >>= code_len; \ + num_bits -= code_len; \ + } \ + MZ_MACRO_END + +static void tinfl_clear_tree(tinfl_decompressor *r) +{ + if (r->m_type == 0) + MZ_CLEAR_ARR(r->m_tree_0); + else if (r->m_type == 1) + MZ_CLEAR_ARR(r->m_tree_1); + else + MZ_CLEAR_ARR(r->m_tree_2); +} + +tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags) +{ + static const mz_uint16 s_length_base[31] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 }; + static const mz_uint8 s_length_extra[31] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0 }; + static const mz_uint16 s_dist_base[32] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0 }; + static const mz_uint8 s_dist_extra[32] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 }; + static const mz_uint8 s_length_dezigzag[19] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + static const mz_uint16 s_min_table_sizes[3] = { 257, 1, 4 }; + + mz_int16 *pTrees[3]; + mz_uint8 *pCode_sizes[3]; + + tinfl_status status = TINFL_STATUS_FAILED; + mz_uint32 num_bits, dist, counter, num_extra; + tinfl_bit_buf_t bit_buf; + const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; + mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next ? pOut_buf_next + *pOut_buf_size : NULL; + size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start; + + /* Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). */ + if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) + { + *pIn_buf_size = *pOut_buf_size = 0; + return TINFL_STATUS_BAD_PARAM; + } + + pTrees[0] = r->m_tree_0; + pTrees[1] = r->m_tree_1; + pTrees[2] = r->m_tree_2; + pCode_sizes[0] = r->m_code_size_0; + pCode_sizes[1] = r->m_code_size_1; + pCode_sizes[2] = r->m_code_size_2; + + num_bits = r->m_num_bits; + bit_buf = r->m_bit_buf; + dist = r->m_dist; + counter = r->m_counter; + num_extra = r->m_num_extra; + dist_from_out_buf_start = r->m_dist_from_out_buf_start; + TINFL_CR_BEGIN + + bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; + r->m_z_adler32 = r->m_check_adler32 = 1; + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) + { + TINFL_GET_BYTE(1, r->m_zhdr0); + TINFL_GET_BYTE(2, r->m_zhdr1); + counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); + if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) + counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)((size_t)1 << (8U + (r->m_zhdr0 >> 4))))); + if (counter) + { + TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); + } + } + + do + { + TINFL_GET_BITS(3, r->m_final, 3); + r->m_type = r->m_final >> 1; + if (r->m_type == 0) + { + TINFL_SKIP_BITS(5, num_bits & 7); + for (counter = 0; counter < 4; ++counter) + { + if (num_bits) + TINFL_GET_BITS(6, r->m_raw_header[counter], 8); + else + TINFL_GET_BYTE(7, r->m_raw_header[counter]); + } + if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) + { + TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); + } + while ((counter) && (num_bits)) + { + TINFL_GET_BITS(51, dist, 8); + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = (mz_uint8)dist; + counter--; + } + while (counter) + { + size_t n; + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); + } + while (pIn_buf_cur >= pIn_buf_end) + { + TINFL_CR_RETURN(38, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); + } + n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter); + TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); + pIn_buf_cur += n; + pOut_buf_cur += n; + counter -= (mz_uint)n; + } + } + else if (r->m_type == 3) + { + TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); + } + else + { + if (r->m_type == 1) + { + mz_uint8 *p = r->m_code_size_0; + mz_uint i; + r->m_table_sizes[0] = 288; + r->m_table_sizes[1] = 32; + TINFL_MEMSET(r->m_code_size_1, 5, 32); + for (i = 0; i <= 143; ++i) + *p++ = 8; + for (; i <= 255; ++i) + *p++ = 9; + for (; i <= 279; ++i) + *p++ = 7; + for (; i <= 287; ++i) + *p++ = 8; + } + else + { + for (counter = 0; counter < 3; counter++) + { + TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); + r->m_table_sizes[counter] += s_min_table_sizes[counter]; + } + MZ_CLEAR_ARR(r->m_code_size_2); + for (counter = 0; counter < r->m_table_sizes[2]; counter++) + { + mz_uint s; + TINFL_GET_BITS(14, s, 3); + r->m_code_size_2[s_length_dezigzag[counter]] = (mz_uint8)s; + } + r->m_table_sizes[2] = 19; + } + for (; (int)r->m_type >= 0; r->m_type--) + { + int tree_next, tree_cur; + mz_int16 *pLookUp; + mz_int16 *pTree; + mz_uint8 *pCode_size; + mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; + pLookUp = r->m_look_up[r->m_type]; + pTree = pTrees[r->m_type]; + pCode_size = pCode_sizes[r->m_type]; + MZ_CLEAR_ARR(total_syms); + TINFL_MEMSET(pLookUp, 0, sizeof(r->m_look_up[0])); + tinfl_clear_tree(r); + for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) + total_syms[pCode_size[i]]++; + used_syms = 0, total = 0; + next_code[0] = next_code[1] = 0; + for (i = 1; i <= 15; ++i) + { + used_syms += total_syms[i]; + next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); + } + if ((65536 != total) && (used_syms > 1)) + { + TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); + } + for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) + { + mz_uint rev_code = 0, l, cur_code, code_size = pCode_size[sym_index]; + if (!code_size) + continue; + cur_code = next_code[code_size]++; + for (l = code_size; l > 0; l--, cur_code >>= 1) + rev_code = (rev_code << 1) | (cur_code & 1); + if (code_size <= TINFL_FAST_LOOKUP_BITS) + { + mz_int16 k = (mz_int16)((code_size << 9) | sym_index); + while (rev_code < TINFL_FAST_LOOKUP_SIZE) + { + pLookUp[rev_code] = k; + rev_code += (1 << code_size); + } + continue; + } + if (0 == (tree_cur = pLookUp[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) + { + pLookUp[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; + tree_cur = tree_next; + tree_next -= 2; + } + rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); + for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) + { + tree_cur -= ((rev_code >>= 1) & 1); + if (!pTree[-tree_cur - 1]) + { + pTree[-tree_cur - 1] = (mz_int16)tree_next; + tree_cur = tree_next; + tree_next -= 2; + } + else + tree_cur = pTree[-tree_cur - 1]; + } + tree_cur -= ((rev_code >>= 1) & 1); + pTree[-tree_cur - 1] = (mz_int16)sym_index; + } + if (r->m_type == 2) + { + for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]);) + { + mz_uint s; + TINFL_HUFF_DECODE(16, dist, r->m_look_up[2], r->m_tree_2); + if (dist < 16) + { + r->m_len_codes[counter++] = (mz_uint8)dist; + continue; + } + if ((dist == 16) && (!counter)) + { + TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); + } + num_extra = "\02\03\07"[dist - 16]; + TINFL_GET_BITS(18, s, num_extra); + s += "\03\03\013"[dist - 16]; + TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); + counter += s; + } + if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) + { + TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); + } + TINFL_MEMCPY(r->m_code_size_0, r->m_len_codes, r->m_table_sizes[0]); + TINFL_MEMCPY(r->m_code_size_1, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); + } + } + for (;;) + { + mz_uint8 *pSrc; + for (;;) + { + if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) + { + TINFL_HUFF_DECODE(23, counter, r->m_look_up[0], r->m_tree_0); + if (counter >= 256) + break; + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = (mz_uint8)counter; + } + else + { + int sym2; + mz_uint code_len; +#if TINFL_USE_64BIT_BITBUF + if (num_bits < 30) + { + bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 4; + num_bits += 32; + } +#else + if (num_bits < 15) + { + bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 2; + num_bits += 16; + } +#endif + if ((sym2 = r->m_look_up[0][bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + code_len = sym2 >> 9; + else + { + code_len = TINFL_FAST_LOOKUP_BITS; + do + { + sym2 = r->m_tree_0[~sym2 + ((bit_buf >> code_len++) & 1)]; + } while (sym2 < 0); + } + counter = sym2; + bit_buf >>= code_len; + num_bits -= code_len; + if (counter & 256) + break; + +#if !TINFL_USE_64BIT_BITBUF + if (num_bits < 15) + { + bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 2; + num_bits += 16; + } +#endif + if ((sym2 = r->m_look_up[0][bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + code_len = sym2 >> 9; + else + { + code_len = TINFL_FAST_LOOKUP_BITS; + do + { + sym2 = r->m_tree_0[~sym2 + ((bit_buf >> code_len++) & 1)]; + } while (sym2 < 0); + } + bit_buf >>= code_len; + num_bits -= code_len; + + pOut_buf_cur[0] = (mz_uint8)counter; + if (sym2 & 256) + { + pOut_buf_cur++; + counter = sym2; + break; + } + pOut_buf_cur[1] = (mz_uint8)sym2; + pOut_buf_cur += 2; + } + } + if ((counter &= 511) == 256) + break; + + num_extra = s_length_extra[counter - 257]; + counter = s_length_base[counter - 257]; + if (num_extra) + { + mz_uint extra_bits; + TINFL_GET_BITS(25, extra_bits, num_extra); + counter += extra_bits; + } + + TINFL_HUFF_DECODE(26, dist, r->m_look_up[1], r->m_tree_1); + num_extra = s_dist_extra[dist]; + dist = s_dist_base[dist]; + if (num_extra) + { + mz_uint extra_bits; + TINFL_GET_BITS(27, extra_bits, num_extra); + dist += extra_bits; + } + + dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; + if ((dist == 0 || dist > dist_from_out_buf_start || dist_from_out_buf_start == 0) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) + { + TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); + } + + pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask); + + if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) + { + while (counter--) + { + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask]; + } + continue; + } +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES + else if ((counter >= 9) && (counter <= dist)) + { + const mz_uint8 *pSrc_end = pSrc + (counter & ~7); + do + { +#ifdef MINIZ_UNALIGNED_USE_MEMCPY + memcpy(pOut_buf_cur, pSrc, sizeof(mz_uint32)*2); +#else + ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0]; + ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1]; +#endif + pOut_buf_cur += 8; + } while ((pSrc += 8) < pSrc_end); + if ((counter &= 7) < 3) + { + if (counter) + { + pOut_buf_cur[0] = pSrc[0]; + if (counter > 1) + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + continue; + } + } +#endif + while(counter>2) + { + pOut_buf_cur[0] = pSrc[0]; + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur[2] = pSrc[2]; + pOut_buf_cur += 3; + pSrc += 3; + counter -= 3; + } + if (counter > 0) + { + pOut_buf_cur[0] = pSrc[0]; + if (counter > 1) + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + } + } + } while (!(r->m_final & 1)); + + /* Ensure byte alignment and put back any bytes from the bitbuf if we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */ + /* I'm being super conservative here. A number of simplifications can be made to the byte alignment part, and the Adler32 check shouldn't ever need to worry about reading from the bitbuf now. */ + TINFL_SKIP_BITS(32, num_bits & 7); + while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8)) + { + --pIn_buf_cur; + num_bits -= 8; + } + bit_buf &= ~(~(tinfl_bit_buf_t)0 << num_bits); + MZ_ASSERT(!num_bits); /* if this assert fires then we've read beyond the end of non-deflate/zlib streams with following data (such as gzip streams). */ + + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) + { + for (counter = 0; counter < 4; ++counter) + { + mz_uint s; + if (num_bits) + TINFL_GET_BITS(41, s, 8); + else + TINFL_GET_BYTE(42, s); + r->m_z_adler32 = (r->m_z_adler32 << 8) | s; + } + } + TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); + + TINFL_CR_FINISH + +common_exit: + /* As long as we aren't telling the caller that we NEED more input to make forward progress: */ + /* Put back any bytes from the bitbuf in case we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */ + /* We need to be very careful here to NOT push back any bytes we definitely know we need to make forward progress, though, or we'll lock the caller up into an inf loop. */ + if ((status != TINFL_STATUS_NEEDS_MORE_INPUT) && (status != TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS)) + { + while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8)) + { + --pIn_buf_cur; + num_bits -= 8; + } + } + r->m_num_bits = num_bits; + r->m_bit_buf = bit_buf & ~(~(tinfl_bit_buf_t)0 << num_bits); + r->m_dist = dist; + r->m_counter = counter; + r->m_num_extra = num_extra; + r->m_dist_from_out_buf_start = dist_from_out_buf_start; + *pIn_buf_size = pIn_buf_cur - pIn_buf_next; + *pOut_buf_size = pOut_buf_cur - pOut_buf_next; + if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0)) + { + const mz_uint8 *ptr = pOut_buf_next; + size_t buf_len = *pOut_buf_size; + mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; + size_t block_len = buf_len % 5552; + while (buf_len) + { + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) + { + s1 += ptr[0], s2 += s1; + s1 += ptr[1], s2 += s1; + s1 += ptr[2], s2 += s1; + s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; + s1 += ptr[5], s2 += s1; + s1 += ptr[6], s2 += s1; + s1 += ptr[7], s2 += s1; + } + for (; i < block_len; ++i) + s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; + buf_len -= block_len; + block_len = 5552; + } + r->m_check_adler32 = (s2 << 16) + s1; + if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) + status = TINFL_STATUS_ADLER32_MISMATCH; + } + return status; +} + +/* Higher level helper functions. */ +void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) +{ + tinfl_decompressor decomp; + void *pBuf = NULL, *pNew_buf; + size_t src_buf_ofs = 0, out_buf_capacity = 0; + *pOut_len = 0; + tinfl_init(&decomp); + for (;;) + { + size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; + tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8 *)pBuf, pBuf ? (mz_uint8 *)pBuf + *pOut_len : NULL, &dst_buf_size, + (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); + if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) + { + MZ_FREE(pBuf); + *pOut_len = 0; + return NULL; + } + src_buf_ofs += src_buf_size; + *pOut_len += dst_buf_size; + if (status == TINFL_STATUS_DONE) + break; + new_out_buf_capacity = out_buf_capacity * 2; + if (new_out_buf_capacity < 128) + new_out_buf_capacity = 128; + pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); + if (!pNew_buf) + { + MZ_FREE(pBuf); + *pOut_len = 0; + return NULL; + } + pBuf = pNew_buf; + out_buf_capacity = new_out_buf_capacity; + } + return pBuf; +} + +size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) +{ + tinfl_decompressor decomp; + tinfl_status status; + tinfl_init(&decomp); + status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf, &src_buf_len, (mz_uint8 *)pOut_buf, (mz_uint8 *)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); + return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len; +} + +int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + int result = 0; + tinfl_decompressor decomp; + mz_uint8 *pDict = (mz_uint8 *)MZ_MALLOC(TINFL_LZ_DICT_SIZE); + size_t in_buf_ofs = 0, dict_ofs = 0; + if (!pDict) + return TINFL_STATUS_FAILED; + memset(pDict,0,TINFL_LZ_DICT_SIZE); + tinfl_init(&decomp); + for (;;) + { + size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; + tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, + (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); + in_buf_ofs += in_buf_size; + if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) + break; + if (status != TINFL_STATUS_HAS_MORE_OUTPUT) + { + result = (status == TINFL_STATUS_DONE); + break; + } + dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); + } + MZ_FREE(pDict); + *pIn_buf_size = in_buf_ofs; + return result; +} + +#ifndef MINIZ_NO_MALLOC +tinfl_decompressor *tinfl_decompressor_alloc(void) +{ + tinfl_decompressor *pDecomp = (tinfl_decompressor *)MZ_MALLOC(sizeof(tinfl_decompressor)); + if (pDecomp) + tinfl_init(pDecomp); + return pDecomp; +} + +void tinfl_decompressor_free(tinfl_decompressor *pDecomp) +{ + MZ_FREE(pDecomp); +} +#endif + +#ifdef __cplusplus +} +#endif + +#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ + /************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * Copyright 2016 Martin Raiber + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + + +#ifndef MINIZ_NO_ARCHIVE_APIS + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- .ZIP archive reading */ + +#ifdef MINIZ_NO_STDIO +#define MZ_FILE void * +#else +#include + +#if defined(_MSC_VER) || defined(__MINGW64__) + +#define WIN32_LEAN_AND_MEAN +#include + +static WCHAR* mz_utf8z_to_widechar(const char* str) +{ + int reqChars = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0); + WCHAR* wStr = (WCHAR*)malloc(reqChars * sizeof(WCHAR)); + MultiByteToWideChar(CP_UTF8, 0, str, -1, wStr, sizeof(WCHAR) * reqChars); + return wStr; +} + +static FILE *mz_fopen(const char *pFilename, const char *pMode) +{ + WCHAR* wFilename = mz_utf8z_to_widechar(pFilename); + WCHAR* wMode = mz_utf8z_to_widechar(pMode); + FILE* pFile = NULL; + errno_t err = _wfopen_s(&pFile, wFilename, wMode); + free(wFilename); + free(wMode); + return err ? NULL : pFile; +} + +static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) +{ + WCHAR* wPath = mz_utf8z_to_widechar(pPath); + WCHAR* wMode = mz_utf8z_to_widechar(pMode); + FILE* pFile = NULL; + errno_t err = _wfreopen_s(&pFile, wPath, wMode, pStream); + free(wPath); + free(wMode); + return err ? NULL : pFile; +} + +static int mz_stat64(const char *path, struct __stat64 *buffer) +{ + WCHAR* wPath = mz_utf8z_to_widechar(path); + int res = _wstat64(wPath, buffer); + free(wPath); + return res; +} + +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN mz_fopen +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 _ftelli64 +#define MZ_FSEEK64 _fseeki64 +#define MZ_FILE_STAT_STRUCT _stat64 +#define MZ_FILE_STAT mz_stat64 +#define MZ_FFLUSH fflush +#define MZ_FREOPEN mz_freopen +#define MZ_DELETE_FILE remove + +#elif defined(__MINGW32__) || defined(__WATCOMC__) +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 _ftelli64 +#define MZ_FSEEK64 _fseeki64 +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_DELETE_FILE remove + +#elif defined(__TINYC__) +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftell +#define MZ_FSEEK64 fseek +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_DELETE_FILE remove + +#elif defined(__USE_LARGEFILE64) /* gcc, clang */ +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen64(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftello64 +#define MZ_FSEEK64 fseeko64 +#define MZ_FILE_STAT_STRUCT stat64 +#define MZ_FILE_STAT stat64 +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(p, m, s) freopen64(p, m, s) +#define MZ_DELETE_FILE remove + +#elif defined(__APPLE__) || defined(__FreeBSD__) +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftello +#define MZ_FSEEK64 fseeko +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(p, m, s) freopen(p, m, s) +#define MZ_DELETE_FILE remove + +#else +#pragma message("Using fopen, ftello, fseeko, stat() etc. path for file I/O - this path may not support large files.") +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#ifdef __STRICT_ANSI__ +#define MZ_FTELL64 ftell +#define MZ_FSEEK64 fseek +#else +#define MZ_FTELL64 ftello +#define MZ_FSEEK64 fseeko +#endif +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_DELETE_FILE remove +#endif /* #ifdef _MSC_VER */ +#endif /* #ifdef MINIZ_NO_STDIO */ + +#define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c)) + +/* Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform endian issues, miniz.c doesn't use structs for any of this stuff. */ +enum +{ + /* ZIP archive identifiers and record sizes */ + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, + MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, + MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50, + MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22, + + /* ZIP64 archive identifier and record sizes */ + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06064b50, + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG = 0x07064b50, + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE = 56, + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE = 20, + MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID = 0x0001, + MZ_ZIP_DATA_DESCRIPTOR_ID = 0x08074b50, + MZ_ZIP_DATA_DESCRIPTER_SIZE64 = 24, + MZ_ZIP_DATA_DESCRIPTER_SIZE32 = 16, + + /* Central directory header record offsets */ + MZ_ZIP_CDH_SIG_OFS = 0, + MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, + MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, + MZ_ZIP_CDH_BIT_FLAG_OFS = 8, + MZ_ZIP_CDH_METHOD_OFS = 10, + MZ_ZIP_CDH_FILE_TIME_OFS = 12, + MZ_ZIP_CDH_FILE_DATE_OFS = 14, + MZ_ZIP_CDH_CRC32_OFS = 16, + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, + MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, + MZ_ZIP_CDH_EXTRA_LEN_OFS = 30, + MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, + MZ_ZIP_CDH_DISK_START_OFS = 34, + MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, + MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42, + + /* Local directory header offsets */ + MZ_ZIP_LDH_SIG_OFS = 0, + MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, + MZ_ZIP_LDH_BIT_FLAG_OFS = 6, + MZ_ZIP_LDH_METHOD_OFS = 8, + MZ_ZIP_LDH_FILE_TIME_OFS = 10, + MZ_ZIP_LDH_FILE_DATE_OFS = 12, + MZ_ZIP_LDH_CRC32_OFS = 14, + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22, + MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, + MZ_ZIP_LDH_EXTRA_LEN_OFS = 28, + MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR = 1 << 3, + + /* End of central directory offsets */ + MZ_ZIP_ECDH_SIG_OFS = 0, + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8, + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, + MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, + MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, + MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20, + + /* ZIP64 End of central directory locator offsets */ + MZ_ZIP64_ECDL_SIG_OFS = 0, /* 4 bytes */ + MZ_ZIP64_ECDL_NUM_DISK_CDIR_OFS = 4, /* 4 bytes */ + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS = 8, /* 8 bytes */ + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS = 16, /* 4 bytes */ + + /* ZIP64 End of central directory header offsets */ + MZ_ZIP64_ECDH_SIG_OFS = 0, /* 4 bytes */ + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS = 4, /* 8 bytes */ + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS = 12, /* 2 bytes */ + MZ_ZIP64_ECDH_VERSION_NEEDED_OFS = 14, /* 2 bytes */ + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS = 16, /* 4 bytes */ + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS = 20, /* 4 bytes */ + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 24, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS = 32, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_SIZE_OFS = 40, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_OFS_OFS = 48, /* 8 bytes */ + MZ_ZIP_VERSION_MADE_BY_DOS_FILESYSTEM_ID = 0, + MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG = 0x10, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED = 1, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG = 32, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION = 64, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED = 8192, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 = 1 << 11 +}; + +typedef struct +{ + void *m_p; + size_t m_size, m_capacity; + mz_uint m_element_size; +} mz_zip_array; + +struct mz_zip_internal_state_tag +{ + mz_zip_array m_central_dir; + mz_zip_array m_central_dir_offsets; + mz_zip_array m_sorted_central_dir_offsets; + + /* The flags passed in when the archive is initially opened. */ + mz_uint32 m_init_flags; + + /* MZ_TRUE if the archive has a zip64 end of central directory headers, etc. */ + mz_bool m_zip64; + + /* MZ_TRUE if we found zip64 extended info in the central directory (m_zip64 will also be slammed to true too, even if we didn't find a zip64 end of central dir header, etc.) */ + mz_bool m_zip64_has_extended_info_fields; + + /* These fields are used by the file, FILE, memory, and memory/heap read/write helpers. */ + MZ_FILE *m_pFile; + mz_uint64 m_file_archive_start_ofs; + + void *m_pMem; + size_t m_mem_size; + size_t m_mem_capacity; +}; + +#define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size + +#if defined(DEBUG) || defined(_DEBUG) +static MZ_FORCEINLINE mz_uint mz_zip_array_range_check(const mz_zip_array *pArray, mz_uint index) +{ + MZ_ASSERT(index < pArray->m_size); + return index; +} +#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[mz_zip_array_range_check(array_ptr, index)] +#else +#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index] +#endif + +static MZ_FORCEINLINE void mz_zip_array_init(mz_zip_array *pArray, mz_uint32 element_size) +{ + memset(pArray, 0, sizeof(mz_zip_array)); + pArray->m_element_size = element_size; +} + +static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray) +{ + pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p); + memset(pArray, 0, sizeof(mz_zip_array)); +} + +static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing) +{ + void *pNew_p; + size_t new_capacity = min_new_capacity; + MZ_ASSERT(pArray->m_element_size); + if (pArray->m_capacity >= min_new_capacity) + return MZ_TRUE; + if (growing) + { + new_capacity = MZ_MAX(1, pArray->m_capacity); + while (new_capacity < min_new_capacity) + new_capacity *= 2; + } + if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) + return MZ_FALSE; + pArray->m_p = pNew_p; + pArray->m_capacity = new_capacity; + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing) +{ + if (new_capacity > pArray->m_capacity) + { + if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) + return MZ_FALSE; + } + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing) +{ + if (new_size > pArray->m_capacity) + { + if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) + return MZ_FALSE; + } + pArray->m_size = new_size; + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n) +{ + return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE); +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n) +{ + size_t orig_size = pArray->m_size; + if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) + return MZ_FALSE; + if (n > 0) + memcpy((mz_uint8 *)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size); + return MZ_TRUE; +} + +#ifndef MINIZ_NO_TIME +static MZ_TIME_T mz_zip_dos_to_time_t(int dos_time, int dos_date) +{ + struct tm tm; + memset(&tm, 0, sizeof(tm)); + tm.tm_isdst = -1; + tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; + tm.tm_mon = ((dos_date >> 5) & 15) - 1; + tm.tm_mday = dos_date & 31; + tm.tm_hour = (dos_time >> 11) & 31; + tm.tm_min = (dos_time >> 5) & 63; + tm.tm_sec = (dos_time << 1) & 62; + return mktime(&tm); +} + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS +static void mz_zip_time_t_to_dos_time(MZ_TIME_T time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) +{ +#ifdef _MSC_VER + struct tm tm_struct; + struct tm *tm = &tm_struct; + errno_t err = localtime_s(tm, &time); + if (err) + { + *pDOS_date = 0; + *pDOS_time = 0; + return; + } +#else + struct tm *tm = localtime(&time); +#endif /* #ifdef _MSC_VER */ + + *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1)); + *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday); +} +#endif /* MINIZ_NO_ARCHIVE_WRITING_APIS */ + +#ifndef MINIZ_NO_STDIO +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS +static mz_bool mz_zip_get_file_modified_time(const char *pFilename, MZ_TIME_T *pTime) +{ + struct MZ_FILE_STAT_STRUCT file_stat; + + /* On Linux with x86 glibc, this call will fail on large files (I think >= 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh. */ + if (MZ_FILE_STAT(pFilename, &file_stat) != 0) + return MZ_FALSE; + + *pTime = file_stat.st_mtime; + + return MZ_TRUE; +} +#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS*/ + +static mz_bool mz_zip_set_file_times(const char *pFilename, MZ_TIME_T access_time, MZ_TIME_T modified_time) +{ + struct utimbuf t; + + memset(&t, 0, sizeof(t)); + t.actime = access_time; + t.modtime = modified_time; + + return !utime(pFilename, &t); +} +#endif /* #ifndef MINIZ_NO_STDIO */ +#endif /* #ifndef MINIZ_NO_TIME */ + +static MZ_FORCEINLINE mz_bool mz_zip_set_error(mz_zip_archive *pZip, mz_zip_error err_num) +{ + if (pZip) + pZip->m_last_error = err_num; + return MZ_FALSE; +} + +static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint flags) +{ + (void)flags; + if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!pZip->m_pAlloc) + pZip->m_pAlloc = miniz_def_alloc_func; + if (!pZip->m_pFree) + pZip->m_pFree = miniz_def_free_func; + if (!pZip->m_pRealloc) + pZip->m_pRealloc = miniz_def_realloc_func; + + pZip->m_archive_size = 0; + pZip->m_central_directory_file_ofs = 0; + pZip->m_total_files = 0; + pZip->m_last_error = MZ_ZIP_NO_ERROR; + + if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); + pZip->m_pState->m_init_flags = flags; + pZip->m_pState->m_zip64 = MZ_FALSE; + pZip->m_pState->m_zip64_has_extended_info_fields = MZ_FALSE; + + pZip->m_zip_mode = MZ_ZIP_MODE_READING; + + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index) +{ + const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; + const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index)); + mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS); + mz_uint8 l = 0, r = 0; + pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pE = pL + MZ_MIN(l_len, r_len); + while (pL < pE) + { + if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) + break; + pL++; + pR++; + } + return (pL == pE) ? (l_len < r_len) : (l < r); +} + +#define MZ_SWAP_UINT32(a, b) \ + do \ + { \ + mz_uint32 t = a; \ + a = b; \ + b = t; \ + } \ + MZ_MACRO_END + +/* Heap sort of lowercased filenames, used to help accelerate plain central directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), but it could allocate memory.) */ +static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) +{ + mz_zip_internal_state *pState = pZip->m_pState; + const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; + const mz_zip_array *pCentral_dir = &pState->m_central_dir; + mz_uint32 *pIndices; + mz_uint32 start, end; + const mz_uint32 size = pZip->m_total_files; + + if (size <= 1U) + return; + + pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); + + start = (size - 2U) >> 1U; + for (;;) + { + mz_uint64 child, root = start; + for (;;) + { + if ((child = (root << 1U) + 1U) >= size) + break; + child += (((child + 1U) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U]))); + if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) + break; + MZ_SWAP_UINT32(pIndices[root], pIndices[child]); + root = child; + } + if (!start) + break; + start--; + } + + end = size - 1; + while (end > 0) + { + mz_uint64 child, root = 0; + MZ_SWAP_UINT32(pIndices[end], pIndices[0]); + for (;;) + { + if ((child = (root << 1U) + 1U) >= end) + break; + child += (((child + 1U) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U])); + if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) + break; + MZ_SWAP_UINT32(pIndices[root], pIndices[child]); + root = child; + } + end--; + } +} + +static mz_bool mz_zip_reader_locate_header_sig(mz_zip_archive *pZip, mz_uint32 record_sig, mz_uint32 record_size, mz_int64 *pOfs) +{ + mz_int64 cur_file_ofs; + mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; + mz_uint8 *pBuf = (mz_uint8 *)buf_u32; + + /* Basic sanity checks - reject files which are too small */ + if (pZip->m_archive_size < record_size) + return MZ_FALSE; + + /* Find the record by scanning the file from the end towards the beginning. */ + cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0); + for (;;) + { + int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs); + + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n) + return MZ_FALSE; + + for (i = n - 4; i >= 0; --i) + { + mz_uint s = MZ_READ_LE32(pBuf + i); + if (s == record_sig) + { + if ((pZip->m_archive_size - (cur_file_ofs + i)) >= record_size) + break; + } + } + + if (i >= 0) + { + cur_file_ofs += i; + break; + } + + /* Give up if we've searched the entire file, or we've gone back "too far" (~64kb) */ + if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (MZ_UINT16_MAX + record_size))) + return MZ_FALSE; + + cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); + } + + *pOfs = cur_file_ofs; + return MZ_TRUE; +} + +static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint flags) +{ + mz_uint cdir_size = 0, cdir_entries_on_this_disk = 0, num_this_disk = 0, cdir_disk_index = 0; + mz_uint64 cdir_ofs = 0; + mz_int64 cur_file_ofs = 0; + const mz_uint8 *p; + + mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; + mz_uint8 *pBuf = (mz_uint8 *)buf_u32; + mz_bool sort_central_dir = ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0); + mz_uint32 zip64_end_of_central_dir_locator_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pZip64_locator = (mz_uint8 *)zip64_end_of_central_dir_locator_u32; + + mz_uint32 zip64_end_of_central_dir_header_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pZip64_end_of_central_dir = (mz_uint8 *)zip64_end_of_central_dir_header_u32; + + mz_uint64 zip64_end_of_central_dir_ofs = 0; + + /* Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file to make sure a local header is there. */ + if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (!mz_zip_reader_locate_header_sig(pZip, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE, &cur_file_ofs)) + return mz_zip_set_error(pZip, MZ_ZIP_FAILED_FINDING_CENTRAL_DIR); + + /* Read and verify the end of central directory record. */ + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (cur_file_ofs >= (MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) + { + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs - MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE, pZip64_locator, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) + { + if (MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG) + { + zip64_end_of_central_dir_ofs = MZ_READ_LE64(pZip64_locator + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS); + if (zip64_end_of_central_dir_ofs > (pZip->m_archive_size - MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (pZip->m_pRead(pZip->m_pIO_opaque, zip64_end_of_central_dir_ofs, pZip64_end_of_central_dir, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) + { + if (MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG) + { + pZip->m_pState->m_zip64 = MZ_TRUE; + } + } + } + } + } + + pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS); + cdir_entries_on_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); + num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS); + cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS); + cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS); + cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS); + + if (pZip->m_pState->m_zip64) + { + mz_uint32 zip64_total_num_of_disks = MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS); + mz_uint64 zip64_cdir_total_entries = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS); + mz_uint64 zip64_cdir_total_entries_on_this_disk = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); + mz_uint64 zip64_size_of_end_of_central_dir_record = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS); + mz_uint64 zip64_size_of_central_directory = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_SIZE_OFS); + + if (zip64_size_of_end_of_central_dir_record < (MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - 12)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (zip64_total_num_of_disks != 1U) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + /* Check for miniz's practical limits */ + if (zip64_cdir_total_entries > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + pZip->m_total_files = (mz_uint32)zip64_cdir_total_entries; + + if (zip64_cdir_total_entries_on_this_disk > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + cdir_entries_on_this_disk = (mz_uint32)zip64_cdir_total_entries_on_this_disk; + + /* Check for miniz's current practical limits (sorry, this should be enough for millions of files) */ + if (zip64_size_of_central_directory > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + cdir_size = (mz_uint32)zip64_size_of_central_directory; + + num_this_disk = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS); + + cdir_disk_index = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS); + + cdir_ofs = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_OFS_OFS); + } + + if (pZip->m_total_files != cdir_entries_on_this_disk) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1))) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (cdir_size < (mz_uint64)pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pZip->m_central_directory_file_ofs = cdir_ofs; + + if (pZip->m_total_files) + { + mz_uint i, n; + /* Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and possibly another to hold the sorted indices. */ + if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) || + (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if (sort_central_dir) + { + if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + /* Now create an index into the central directory file records, do some basic sanity checking on each record */ + p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p; + for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) + { + mz_uint total_header_size, disk_index, bit_flags, filename_size, ext_data_size; + mz_uint64 comp_size, decomp_size, local_header_ofs; + + if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p); + + if (sort_central_dir) + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i; + + comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + filename_size = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + ext_data_size = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); + + if ((!pZip->m_pState->m_zip64_has_extended_info_fields) && + (ext_data_size) && + (MZ_MAX(MZ_MAX(comp_size, decomp_size), local_header_ofs) == MZ_UINT32_MAX)) + { + /* Attempt to find zip64 extended information field in the entry's extra data */ + mz_uint32 extra_size_remaining = ext_data_size; + + if (extra_size_remaining) + { + const mz_uint8 *pExtra_data; + void* buf = NULL; + + if (MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + ext_data_size > n) + { + buf = MZ_MALLOC(ext_data_size); + if(buf==NULL) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size, buf, ext_data_size) != ext_data_size) + { + MZ_FREE(buf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + pExtra_data = (mz_uint8*)buf; + } + else + { + pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size; + } + + do + { + mz_uint32 field_id; + mz_uint32 field_data_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + { + MZ_FREE(buf); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + + if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining) + { + MZ_FREE(buf); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + /* Ok, the archive didn't have any zip64 headers but it uses a zip64 extended information field so mark it as zip64 anyway (this can occur with infozip's zip util when it reads compresses files from stdin). */ + pZip->m_pState->m_zip64 = MZ_TRUE; + pZip->m_pState->m_zip64_has_extended_info_fields = MZ_TRUE; + break; + } + + pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; + extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; + } while (extra_size_remaining); + + MZ_FREE(buf); + } + } + + /* I've seen archives that aren't marked as zip64 that uses zip64 ext data, argh */ + if ((comp_size != MZ_UINT32_MAX) && (decomp_size != MZ_UINT32_MAX)) + { + if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS); + if ((disk_index == MZ_UINT16_MAX) || ((disk_index != num_this_disk) && (disk_index != 1))) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (comp_size != MZ_UINT32_MAX) + { + if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + bit_flags = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + if (bit_flags & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + n -= total_header_size; + p += total_header_size; + } + } + + if (sort_central_dir) + mz_zip_reader_sort_central_dir_offsets_by_filename(pZip); + + return MZ_TRUE; +} + +void mz_zip_zero_struct(mz_zip_archive *pZip) +{ + if (pZip) + MZ_CLEAR_PTR(pZip); +} + +static mz_bool mz_zip_reader_end_internal(mz_zip_archive *pZip, mz_bool set_last_error) +{ + mz_bool status = MZ_TRUE; + + if (!pZip) + return MZ_FALSE; + + if ((!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + { + if (set_last_error) + pZip->m_last_error = MZ_ZIP_INVALID_PARAMETER; + + return MZ_FALSE; + } + + if (pZip->m_pState) + { + mz_zip_internal_state *pState = pZip->m_pState; + pZip->m_pState = NULL; + + mz_zip_array_clear(pZip, &pState->m_central_dir); + mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); + mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +#ifndef MINIZ_NO_STDIO + if (pState->m_pFile) + { + if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) + { + if (MZ_FCLOSE(pState->m_pFile) == EOF) + { + if (set_last_error) + pZip->m_last_error = MZ_ZIP_FILE_CLOSE_FAILED; + status = MZ_FALSE; + } + } + pState->m_pFile = NULL; + } +#endif /* #ifndef MINIZ_NO_STDIO */ + + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + } + pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + + return status; +} + +mz_bool mz_zip_reader_end(mz_zip_archive *pZip) +{ + return mz_zip_reader_end_internal(pZip, MZ_TRUE); +} +mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags) +{ + if ((!pZip) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_USER; + pZip->m_archive_size = size; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n); + memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s); + return s; +} + +mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags) +{ + if (!pMem) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_MEMORY; + pZip->m_archive_size = size; + pZip->m_pRead = mz_zip_mem_read_func; + pZip->m_pIO_opaque = pZip; + pZip->m_pNeeds_keepalive = NULL; + +#ifdef __cplusplus + pZip->m_pState->m_pMem = const_cast(pMem); +#else + pZip->m_pState->m_pMem = (void *)pMem; +#endif + + pZip->m_pState->m_mem_size = size; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + + file_ofs += pZip->m_pState->m_file_archive_start_ofs; + + if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) + return 0; + + return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile); +} + +mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags) +{ + return mz_zip_reader_init_file_v2(pZip, pFilename, flags, 0, 0); +} + +mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size) +{ + mz_uint64 file_size; + MZ_FILE *pFile; + + if ((!pZip) || (!pFilename) || ((archive_size) && (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pFile = MZ_FOPEN(pFilename, "rb"); + if (!pFile) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + file_size = archive_size; + if (!file_size) + { + if (MZ_FSEEK64(pFile, 0, SEEK_END)) + { + MZ_FCLOSE(pFile); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); + } + + file_size = MZ_FTELL64(pFile); + } + + /* TODO: Better sanity check archive_size and the # of actual remaining bytes */ + + if (file_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + { + MZ_FCLOSE(pFile); + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + } + + if (!mz_zip_reader_init_internal(pZip, flags)) + { + MZ_FCLOSE(pFile); + return MZ_FALSE; + } + + pZip->m_zip_type = MZ_ZIP_TYPE_FILE; + pZip->m_pRead = mz_zip_file_read_func; + pZip->m_pIO_opaque = pZip; + pZip->m_pState->m_pFile = pFile; + pZip->m_archive_size = file_size; + pZip->m_pState->m_file_archive_start_ofs = file_start_ofs; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags) +{ + mz_uint64 cur_file_ofs; + + if ((!pZip) || (!pFile)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + cur_file_ofs = MZ_FTELL64(pFile); + + if (!archive_size) + { + if (MZ_FSEEK64(pFile, 0, SEEK_END)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); + + archive_size = MZ_FTELL64(pFile) - cur_file_ofs; + + if (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + } + + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_CFILE; + pZip->m_pRead = mz_zip_file_read_func; + + pZip->m_pIO_opaque = pZip; + pZip->m_pState->m_pFile = pFile; + pZip->m_archive_size = archive_size; + pZip->m_pState->m_file_archive_start_ofs = cur_file_ofs; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +#endif /* #ifndef MINIZ_NO_STDIO */ + +static MZ_FORCEINLINE const mz_uint8 *mz_zip_get_cdh(mz_zip_archive *pZip, mz_uint file_index) +{ + if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files)) + return NULL; + return &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); +} + +mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint m_bit_flag; + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + return (m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) != 0; +} + +mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint bit_flag; + mz_uint method; + + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); + bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + + if ((method != 0) && (method != MZ_DEFLATED)) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + return MZ_FALSE; + } + + if (bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + return MZ_FALSE; + } + + if (bit_flag & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint filename_len, attribute_mapping_id, external_attr; + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + if (filename_len) + { + if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/') + return MZ_TRUE; + } + + /* Bugfix: This code was also checking if the internal attribute was non-zero, which wasn't correct. */ + /* Most/all zip writers (hopefully) set DOS file/directory attributes in the low 16-bits, so check for the DOS directory flag and ignore the source OS ID in the created by field. */ + /* FIXME: Remove this check? Is it necessary - we already check the filename. */ + attribute_mapping_id = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS) >> 8; + (void)attribute_mapping_id; + + external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + if ((external_attr & MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG) != 0) + { + return MZ_TRUE; + } + + return MZ_FALSE; +} + +static mz_bool mz_zip_file_stat_internal(mz_zip_archive *pZip, mz_uint file_index, const mz_uint8 *pCentral_dir_header, mz_zip_archive_file_stat *pStat, mz_bool *pFound_zip64_extra_data) +{ + mz_uint n; + const mz_uint8 *p = pCentral_dir_header; + + if (pFound_zip64_extra_data) + *pFound_zip64_extra_data = MZ_FALSE; + + if ((!p) || (!pStat)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Extract fields from the central directory record. */ + pStat->m_file_index = file_index; + pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index); + pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS); + pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS); + pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); +#ifndef MINIZ_NO_TIME + pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS)); +#endif + pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS); + pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); + pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + + /* Copy as much of the filename and comment as possible. */ + n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1); + memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); + pStat->m_filename[n] = '\0'; + + n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); + n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1); + pStat->m_comment_size = n; + memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n); + pStat->m_comment[n] = '\0'; + + /* Set some flags for convienance */ + pStat->m_is_directory = mz_zip_reader_is_file_a_directory(pZip, file_index); + pStat->m_is_encrypted = mz_zip_reader_is_file_encrypted(pZip, file_index); + pStat->m_is_supported = mz_zip_reader_is_file_supported(pZip, file_index); + + /* See if we need to read any zip64 extended information fields. */ + /* Confusingly, these zip64 fields can be present even on non-zip64 archives (Debian zip on a huge files from stdin piped to stdout creates them). */ + if (MZ_MAX(MZ_MAX(pStat->m_comp_size, pStat->m_uncomp_size), pStat->m_local_header_ofs) == MZ_UINT32_MAX) + { + /* Attempt to find zip64 extended information field in the entry's extra data */ + mz_uint32 extra_size_remaining = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); + + if (extra_size_remaining) + { + const mz_uint8 *pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + + do + { + mz_uint32 field_id; + mz_uint32 field_data_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + + if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + const mz_uint8 *pField_data = pExtra_data + sizeof(mz_uint16) * 2; + mz_uint32 field_data_remaining = field_data_size; + + if (pFound_zip64_extra_data) + *pFound_zip64_extra_data = MZ_TRUE; + + if (pStat->m_uncomp_size == MZ_UINT32_MAX) + { + if (field_data_remaining < sizeof(mz_uint64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pStat->m_uncomp_size = MZ_READ_LE64(pField_data); + pField_data += sizeof(mz_uint64); + field_data_remaining -= sizeof(mz_uint64); + } + + if (pStat->m_comp_size == MZ_UINT32_MAX) + { + if (field_data_remaining < sizeof(mz_uint64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pStat->m_comp_size = MZ_READ_LE64(pField_data); + pField_data += sizeof(mz_uint64); + field_data_remaining -= sizeof(mz_uint64); + } + + if (pStat->m_local_header_ofs == MZ_UINT32_MAX) + { + if (field_data_remaining < sizeof(mz_uint64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pStat->m_local_header_ofs = MZ_READ_LE64(pField_data); + pField_data += sizeof(mz_uint64); + field_data_remaining -= sizeof(mz_uint64); + } + + break; + } + + pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; + extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; + } while (extra_size_remaining); + } + } + + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags) +{ + mz_uint i; + if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE) + return 0 == memcmp(pA, pB, len); + for (i = 0; i < len; ++i) + if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i])) + return MZ_FALSE; + return MZ_TRUE; +} + +static MZ_FORCEINLINE int mz_zip_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len) +{ + const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; + mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS); + mz_uint8 l = 0, r = 0; + pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pE = pL + MZ_MIN(l_len, r_len); + while (pL < pE) + { + if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) + break; + pL++; + pR++; + } + return (pL == pE) ? (int)(l_len - r_len) : (l - r); +} + +static mz_bool mz_zip_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename, mz_uint32 *pIndex) +{ + mz_zip_internal_state *pState = pZip->m_pState; + const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; + const mz_zip_array *pCentral_dir = &pState->m_central_dir; + mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); + const mz_uint32 size = pZip->m_total_files; + const mz_uint filename_len = (mz_uint)strlen(pFilename); + + if (pIndex) + *pIndex = 0; + + if (size) + { + /* yes I could use uint32_t's, but then we would have to add some special case checks in the loop, argh, and */ + /* honestly the major expense here on 32-bit CPU's will still be the filename compare */ + mz_int64 l = 0, h = (mz_int64)size - 1; + + while (l <= h) + { + mz_int64 m = l + ((h - l) >> 1); + mz_uint32 file_index = pIndices[(mz_uint32)m]; + + int comp = mz_zip_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len); + if (!comp) + { + if (pIndex) + *pIndex = file_index; + return MZ_TRUE; + } + else if (comp < 0) + l = m + 1; + else + h = m - 1; + } + } + + return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); +} + +int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags) +{ + mz_uint32 index; + if (!mz_zip_reader_locate_file_v2(pZip, pName, pComment, flags, &index)) + return -1; + else + return (int)index; +} + +mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *pIndex) +{ + mz_uint file_index; + size_t name_len, comment_len; + + if (pIndex) + *pIndex = 0; + + if ((!pZip) || (!pZip->m_pState) || (!pName)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* See if we can use a binary search */ + if (((pZip->m_pState->m_init_flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0) && + (pZip->m_zip_mode == MZ_ZIP_MODE_READING) && + ((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size)) + { + return mz_zip_locate_file_binary_search(pZip, pName, pIndex); + } + + /* Locate the entry by scanning the entire central directory */ + name_len = strlen(pName); + if (name_len > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + comment_len = pComment ? strlen(pComment) : 0; + if (comment_len > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + for (file_index = 0; file_index < pZip->m_total_files; file_index++) + { + const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); + mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); + const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + if (filename_len < name_len) + continue; + if (comment_len) + { + mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS); + const char *pFile_comment = pFilename + filename_len + file_extra_len; + if ((file_comment_len != comment_len) || (!mz_zip_string_equal(pComment, pFile_comment, file_comment_len, flags))) + continue; + } + if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len)) + { + int ofs = filename_len - 1; + do + { + if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':')) + break; + } while (--ofs >= 0); + ofs++; + pFilename += ofs; + filename_len -= ofs; + } + if ((filename_len == name_len) && (mz_zip_string_equal(pName, pFilename, filename_len, flags))) + { + if (pIndex) + *pIndex = file_index; + return MZ_TRUE; + } + } + + return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); +} + +static +mz_bool mz_zip_reader_extract_to_mem_no_alloc1(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size, const mz_zip_archive_file_stat *st) +{ + int status = TINFL_STATUS_DONE; + mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail; + mz_zip_archive_file_stat file_stat; + void *pRead_buf; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + tinfl_decompressor inflator; + + if ((!pZip) || (!pZip->m_pState) || ((buf_size) && (!pBuf)) || ((user_read_buf_size) && (!pUser_read_buf)) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (st) { + file_stat = *st; + } else + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + /* A directory or zero length file */ + if ((file_stat.m_is_directory) || (!file_stat.m_comp_size)) + return MZ_TRUE; + + /* Encryption and patch files are not supported. */ + if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + /* This function only supports decompressing stored and deflate. */ + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + + /* Ensure supplied output buffer is large enough. */ + needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; + if (buf_size < needed_size) + return mz_zip_set_error(pZip, MZ_ZIP_BUF_TOO_SMALL); + + /* Read and parse the local directory entry. */ + cur_file_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) + { + /* The file is stored or the caller has requested the compressed data. */ + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) == 0) + { + if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32) + return mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED); + } +#endif + + return MZ_TRUE; + } + + /* Decompress the file either directly from memory or from a file input buffer. */ + tinfl_init(&inflator); + + if (pZip->m_pState->m_pMem) + { + /* Read directly from the archive in memory. */ + pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; + read_buf_size = read_buf_avail = file_stat.m_comp_size; + comp_remaining = 0; + } + else if (pUser_read_buf) + { + /* Use a user provided read buffer. */ + if (!user_read_buf_size) + return MZ_FALSE; + pRead_buf = (mz_uint8 *)pUser_read_buf; + read_buf_size = user_read_buf_size; + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + else + { + /* Temporarily allocate a read buffer. */ + read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); + if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + + do + { + /* The size_t cast here should be OK because we've verified that the output buffer is >= file_stat.m_uncomp_size above */ + size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs); + if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + status = TINFL_STATUS_FAILED; + mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); + break; + } + cur_file_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + read_buf_ofs = 0; + } + in_buf_size = (size_t)read_buf_avail; + status = tinfl_decompress(&inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0)); + read_buf_avail -= in_buf_size; + read_buf_ofs += in_buf_size; + out_buf_ofs += out_buf_size; + } while (status == TINFL_STATUS_NEEDS_MORE_INPUT); + + if (status == TINFL_STATUS_DONE) + { + /* Make sure the entire file was decompressed, and check its CRC. */ + if (out_buf_ofs != file_stat.m_uncomp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); + status = TINFL_STATUS_FAILED; + } +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + else if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32) + { + mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED); + status = TINFL_STATUS_FAILED; + } +#endif + } + + if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf)) + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + + return status == TINFL_STATUS_DONE; +} + +mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) +{ + return mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size, NULL); +} + +mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + return MZ_FALSE; + return mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size, NULL); +} + +mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags) +{ + return mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, buf_size, flags, NULL, 0, NULL); +} + +mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags) +{ + return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0); +} + +void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags) +{ + mz_zip_archive_file_stat file_stat; + mz_uint64 alloc_size; + void *pBuf; + + if (pSize) + *pSize = 0; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return NULL; + + alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; + if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) + { + mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + return NULL; + } + + if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + return NULL; + } + + if (!mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, (size_t)alloc_size, flags, NULL, 0, &file_stat)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return NULL; + } + + if (pSize) + *pSize = (size_t)alloc_size; + return pBuf; +} + +void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + { + if (pSize) + *pSize = 0; + return MZ_FALSE; + } + return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags); +} + +mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) +{ + int status = TINFL_STATUS_DONE; +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + mz_uint file_crc32 = MZ_CRC32_INIT; +#endif + mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs; + mz_zip_archive_file_stat file_stat; + void *pRead_buf = NULL; + void *pWrite_buf = NULL; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + + if ((!pZip) || (!pZip->m_pState) || (!pCallback) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + /* A directory or zero length file */ + if ((file_stat.m_is_directory) || (!file_stat.m_comp_size)) + return MZ_TRUE; + + /* Encryption and patch files are not supported. */ + if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + /* This function only supports decompressing stored and deflate. */ + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + + /* Read and do some minimal validation of the local directory entry (this doesn't crack the zip64 stuff, which we already have from the central dir) */ + cur_file_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + /* Decompress the file either directly from memory or from a file input buffer. */ + if (pZip->m_pState->m_pMem) + { + pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; + read_buf_size = read_buf_avail = file_stat.m_comp_size; + comp_remaining = 0; + } + else + { + read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) + { + /* The file is stored or the caller has requested the compressed data. */ + if (pZip->m_pState->m_pMem) + { + if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); + status = TINFL_STATUS_FAILED; + } + else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size); +#endif + } + + cur_file_ofs += file_stat.m_comp_size; + out_buf_ofs += file_stat.m_comp_size; + comp_remaining = 0; + } + else + { + while (comp_remaining) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { + file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail); + } +#endif + + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + + cur_file_ofs += read_buf_avail; + out_buf_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + } + } + } + else + { + tinfl_decompressor inflator; + tinfl_init(&inflator); + + if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + status = TINFL_STATUS_FAILED; + } + else + { + do + { + mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + cur_file_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + read_buf_ofs = 0; + } + + in_buf_size = (size_t)read_buf_avail; + status = tinfl_decompress(&inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); + read_buf_avail -= in_buf_size; + read_buf_ofs += in_buf_size; + + if (out_buf_size) + { + if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size) + { + mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size); +#endif + if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + } + } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT)); + } + } + + if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) + { + /* Make sure the entire file was decompressed, and check its CRC. */ + if (out_buf_ofs != file_stat.m_uncomp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); + status = TINFL_STATUS_FAILED; + } +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + else if (file_crc32 != file_stat.m_crc32) + { + mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); + status = TINFL_STATUS_FAILED; + } +#endif + } + + if (!pZip->m_pState->m_pMem) + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + + if (pWrite_buf) + pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf); + + return status == TINFL_STATUS_DONE; +} + +mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + return MZ_FALSE; + + return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags); +} + +mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags) +{ + mz_zip_reader_extract_iter_state *pState; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + + /* Argument sanity check */ + if ((!pZip) || (!pZip->m_pState)) + return NULL; + + /* Allocate an iterator status structure */ + pState = (mz_zip_reader_extract_iter_state*)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_reader_extract_iter_state)); + if (!pState) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + return NULL; + } + + /* Fetch file details */ + if (!mz_zip_reader_file_stat(pZip, file_index, &pState->file_stat)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* Encryption and patch files are not supported. */ + if (pState->file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* This function only supports decompressing stored and deflate. */ + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (pState->file_stat.m_method != 0) && (pState->file_stat.m_method != MZ_DEFLATED)) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* Init state - save args */ + pState->pZip = pZip; + pState->flags = flags; + + /* Init state - reset variables to defaults */ + pState->status = TINFL_STATUS_DONE; +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + pState->file_crc32 = MZ_CRC32_INIT; +#endif + pState->read_buf_ofs = 0; + pState->out_buf_ofs = 0; + pState->pRead_buf = NULL; + pState->pWrite_buf = NULL; + pState->out_blk_remain = 0; + + /* Read and parse the local directory entry. */ + pState->cur_file_ofs = pState->file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, pState->cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + pState->cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((pState->cur_file_ofs + pState->file_stat.m_comp_size) > pZip->m_archive_size) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* Decompress the file either directly from memory or from a file input buffer. */ + if (pZip->m_pState->m_pMem) + { + pState->pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + pState->cur_file_ofs; + pState->read_buf_size = pState->read_buf_avail = pState->file_stat.m_comp_size; + pState->comp_remaining = pState->file_stat.m_comp_size; + } + else + { + if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))) + { + /* Decompression required, therefore intermediate read buffer required */ + pState->read_buf_size = MZ_MIN(pState->file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); + if (NULL == (pState->pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)pState->read_buf_size))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + } + else + { + /* Decompression not required - we will be reading directly into user buffer, no temp buf required */ + pState->read_buf_size = 0; + } + pState->read_buf_avail = 0; + pState->comp_remaining = pState->file_stat.m_comp_size; + } + + if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))) + { + /* Decompression required, init decompressor */ + tinfl_init( &pState->inflator ); + + /* Allocate write buffer */ + if (NULL == (pState->pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + if (pState->pRead_buf) + pZip->m_pFree(pZip->m_pAlloc_opaque, pState->pRead_buf); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + } + + return pState; +} + +mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags) +{ + mz_uint32 file_index; + + /* Locate file index by name */ + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + return NULL; + + /* Construct iterator */ + return mz_zip_reader_extract_iter_new(pZip, file_index, flags); +} + +size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size) +{ + size_t copied_to_caller = 0; + + /* Argument sanity check */ + if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState) || (!pvBuf)) + return 0; + + if ((pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method)) + { + /* The file is stored or the caller has requested the compressed data, calc amount to return. */ + copied_to_caller = (size_t)MZ_MIN( buf_size, pState->comp_remaining ); + + /* Zip is in memory....or requires reading from a file? */ + if (pState->pZip->m_pState->m_pMem) + { + /* Copy data to caller's buffer */ + memcpy( pvBuf, pState->pRead_buf, copied_to_caller ); + pState->pRead_buf = ((mz_uint8*)pState->pRead_buf) + copied_to_caller; + } + else + { + /* Read directly into caller's buffer */ + if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pvBuf, copied_to_caller) != copied_to_caller) + { + /* Failed to read all that was asked for, flag failure and alert user */ + mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED); + pState->status = TINFL_STATUS_FAILED; + copied_to_caller = 0; + } + } + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + /* Compute CRC if not returning compressed data only */ + if (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, (const mz_uint8 *)pvBuf, copied_to_caller); +#endif + + /* Advance offsets, dec counters */ + pState->cur_file_ofs += copied_to_caller; + pState->out_buf_ofs += copied_to_caller; + pState->comp_remaining -= copied_to_caller; + } + else + { + do + { + /* Calc ptr to write buffer - given current output pos and block size */ + mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pState->pWrite_buf + (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + + /* Calc max output size - given current output pos and block size */ + size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + + if (!pState->out_blk_remain) + { + /* Read more data from file if none available (and reading from file) */ + if ((!pState->read_buf_avail) && (!pState->pZip->m_pState->m_pMem)) + { + /* Calc read size */ + pState->read_buf_avail = MZ_MIN(pState->read_buf_size, pState->comp_remaining); + if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pState->pRead_buf, (size_t)pState->read_buf_avail) != pState->read_buf_avail) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED); + pState->status = TINFL_STATUS_FAILED; + break; + } + + /* Advance offsets, dec counters */ + pState->cur_file_ofs += pState->read_buf_avail; + pState->comp_remaining -= pState->read_buf_avail; + pState->read_buf_ofs = 0; + } + + /* Perform decompression */ + in_buf_size = (size_t)pState->read_buf_avail; + pState->status = tinfl_decompress(&pState->inflator, (const mz_uint8 *)pState->pRead_buf + pState->read_buf_ofs, &in_buf_size, (mz_uint8 *)pState->pWrite_buf, pWrite_buf_cur, &out_buf_size, pState->comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); + pState->read_buf_avail -= in_buf_size; + pState->read_buf_ofs += in_buf_size; + + /* Update current output block size remaining */ + pState->out_blk_remain = out_buf_size; + } + + if (pState->out_blk_remain) + { + /* Calc amount to return. */ + size_t to_copy = MZ_MIN( (buf_size - copied_to_caller), pState->out_blk_remain ); + + /* Copy data to caller's buffer */ + memcpy( (mz_uint8*)pvBuf + copied_to_caller, pWrite_buf_cur, to_copy ); + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + /* Perform CRC */ + pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, pWrite_buf_cur, to_copy); +#endif + + /* Decrement data consumed from block */ + pState->out_blk_remain -= to_copy; + + /* Inc output offset, while performing sanity check */ + if ((pState->out_buf_ofs += to_copy) > pState->file_stat.m_uncomp_size) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED); + pState->status = TINFL_STATUS_FAILED; + break; + } + + /* Increment counter of data copied to caller */ + copied_to_caller += to_copy; + } + } while ( (copied_to_caller < buf_size) && ((pState->status == TINFL_STATUS_NEEDS_MORE_INPUT) || (pState->status == TINFL_STATUS_HAS_MORE_OUTPUT)) ); + } + + /* Return how many bytes were copied into user buffer */ + return copied_to_caller; +} + +mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState) +{ + int status; + + /* Argument sanity check */ + if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState)) + return MZ_FALSE; + + /* Was decompression completed and requested? */ + if ((pState->status == TINFL_STATUS_DONE) && (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) + { + /* Make sure the entire file was decompressed, and check its CRC. */ + if (pState->out_buf_ofs != pState->file_stat.m_uncomp_size) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); + pState->status = TINFL_STATUS_FAILED; + } +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + else if (pState->file_crc32 != pState->file_stat.m_crc32) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED); + pState->status = TINFL_STATUS_FAILED; + } +#endif + } + + /* Free buffers */ + if (!pState->pZip->m_pState->m_pMem) + pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pRead_buf); + if (pState->pWrite_buf) + pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pWrite_buf); + + /* Save status */ + status = pState->status; + + /* Free context */ + pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState); + + return status == TINFL_STATUS_DONE; +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n) +{ + (void)ofs; + + return MZ_FWRITE(pBuf, 1, n, (MZ_FILE *)pOpaque); +} + +mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags) +{ + mz_bool status; + mz_zip_archive_file_stat file_stat; + MZ_FILE *pFile; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + if ((file_stat.m_is_directory) || (!file_stat.m_is_supported)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + + pFile = MZ_FOPEN(pDst_filename, "wb"); + if (!pFile) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + status = mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); + + if (MZ_FCLOSE(pFile) == EOF) + { + if (status) + mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); + + status = MZ_FALSE; + } + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO) + if (status) + mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time); +#endif + + return status; +} + +mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index)) + return MZ_FALSE; + + return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags); +} + +mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *pFile, mz_uint flags) +{ + mz_zip_archive_file_stat file_stat; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + if ((file_stat.m_is_directory) || (!file_stat.m_is_supported)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + + return mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); +} + +mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index)) + return MZ_FALSE; + + return mz_zip_reader_extract_to_cfile(pZip, file_index, pFile, flags); +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +static size_t mz_zip_compute_crc32_callback(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_uint32 *p = (mz_uint32 *)pOpaque; + (void)file_ofs; + *p = (mz_uint32)mz_crc32(*p, (const mz_uint8 *)pBuf, n); + return n; +} + +mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags) +{ + mz_zip_archive_file_stat file_stat; + mz_zip_internal_state *pState; + const mz_uint8 *pCentral_dir_header; + mz_bool found_zip64_ext_data_in_cdir = MZ_FALSE; + mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + mz_uint64 local_header_ofs = 0; + mz_uint32 local_header_filename_len, local_header_extra_len, local_header_crc32; + mz_uint64 local_header_comp_size, local_header_uncomp_size; + mz_uint32 uncomp_crc32 = MZ_CRC32_INIT; + mz_bool has_data_descriptor; + mz_uint32 local_header_bit_flags; + + mz_zip_array file_data_array; + mz_zip_array_init(&file_data_array, 1); + + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (file_index > pZip->m_total_files) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + pCentral_dir_header = mz_zip_get_cdh(pZip, file_index); + + if (!mz_zip_file_stat_internal(pZip, file_index, pCentral_dir_header, &file_stat, &found_zip64_ext_data_in_cdir)) + return MZ_FALSE; + + /* A directory or zero length file */ + if ((file_stat.m_is_directory) || (!file_stat.m_uncomp_size)) + return MZ_TRUE; + + /* Encryption and patch files are not supported. */ + if (file_stat.m_is_encrypted) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + /* This function only supports stored and deflate. */ + if ((file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + + if (!file_stat.m_is_supported) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + + /* Read and parse the local directory entry. */ + local_header_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + local_header_filename_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS); + local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS); + local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS); + local_header_crc32 = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_CRC32_OFS); + local_header_bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); + has_data_descriptor = (local_header_bit_flags & 8) != 0; + + if (local_header_filename_len != strlen(file_stat.m_filename)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if ((local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (!mz_zip_array_resize(pZip, &file_data_array, MZ_MAX(local_header_filename_len, local_header_extra_len), MZ_FALSE)) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + goto handle_failure; + } + + if (local_header_filename_len) + { + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE, file_data_array.m_p, local_header_filename_len) != local_header_filename_len) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + goto handle_failure; + } + + /* I've seen 1 archive that had the same pathname, but used backslashes in the local dir and forward slashes in the central dir. Do we care about this? For now, this case will fail validation. */ + if (memcmp(file_stat.m_filename, file_data_array.m_p, local_header_filename_len) != 0) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + goto handle_failure; + } + } + + if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX))) + { + mz_uint32 extra_size_remaining = local_header_extra_len; + const mz_uint8 *pExtra_data = (const mz_uint8 *)file_data_array.m_p; + + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len, file_data_array.m_p, local_header_extra_len) != local_header_extra_len) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + goto handle_failure; + } + + do + { + mz_uint32 field_id, field_data_size, field_total_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + goto handle_failure; + } + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + field_total_size = field_data_size + sizeof(mz_uint16) * 2; + + if (field_total_size > extra_size_remaining) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + goto handle_failure; + } + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32); + + if (field_data_size < sizeof(mz_uint64) * 2) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + goto handle_failure; + } + + local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data); + local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64)); + + found_zip64_ext_data_in_ldir = MZ_TRUE; + break; + } + + pExtra_data += field_total_size; + extra_size_remaining -= field_total_size; + } while (extra_size_remaining); + } + + /* TODO: parse local header extra data when local_header_comp_size is 0xFFFFFFFF! (big_descriptor.zip) */ + /* I've seen zips in the wild with the data descriptor bit set, but proper local header values and bogus data descriptors */ + if ((has_data_descriptor) && (!local_header_comp_size) && (!local_header_crc32)) + { + mz_uint8 descriptor_buf[32]; + mz_bool has_id; + const mz_uint8 *pSrc; + mz_uint32 file_crc32; + mz_uint64 comp_size = 0, uncomp_size = 0; + + mz_uint32 num_descriptor_uint32s = ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) ? 6 : 4; + + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size, descriptor_buf, sizeof(mz_uint32) * num_descriptor_uint32s) != (sizeof(mz_uint32) * num_descriptor_uint32s)) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + goto handle_failure; + } + + has_id = (MZ_READ_LE32(descriptor_buf) == MZ_ZIP_DATA_DESCRIPTOR_ID); + pSrc = has_id ? (descriptor_buf + sizeof(mz_uint32)) : descriptor_buf; + + file_crc32 = MZ_READ_LE32(pSrc); + + if ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) + { + comp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32)); + uncomp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32) + sizeof(mz_uint64)); + } + else + { + comp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32)); + uncomp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32) + sizeof(mz_uint32)); + } + + if ((file_crc32 != file_stat.m_crc32) || (comp_size != file_stat.m_comp_size) || (uncomp_size != file_stat.m_uncomp_size)) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + goto handle_failure; + } + } + else + { + if ((local_header_crc32 != file_stat.m_crc32) || (local_header_comp_size != file_stat.m_comp_size) || (local_header_uncomp_size != file_stat.m_uncomp_size)) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + goto handle_failure; + } + } + + mz_zip_array_clear(pZip, &file_data_array); + + if ((flags & MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY) == 0) + { + if (!mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_compute_crc32_callback, &uncomp_crc32, 0)) + return MZ_FALSE; + + /* 1 more check to be sure, although the extract checks too. */ + if (uncomp_crc32 != file_stat.m_crc32) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + return MZ_FALSE; + } + } + + return MZ_TRUE; + +handle_failure: + mz_zip_array_clear(pZip, &file_data_array); + return MZ_FALSE; +} + +mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags) +{ + mz_zip_internal_state *pState; + mz_uint32 i; + + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + /* Basic sanity checks */ + if (!pState->m_zip64) + { + if (pZip->m_total_files > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + if (pZip->m_archive_size > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + } + else + { + if (pState->m_central_dir.m_size >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + } + + for (i = 0; i < pZip->m_total_files; i++) + { + if (MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG & flags) + { + mz_uint32 found_index; + mz_zip_archive_file_stat stat; + + if (!mz_zip_reader_file_stat(pZip, i, &stat)) + return MZ_FALSE; + + if (!mz_zip_reader_locate_file_v2(pZip, stat.m_filename, NULL, 0, &found_index)) + return MZ_FALSE; + + /* This check can fail if there are duplicate filenames in the archive (which we don't check for when writing - that's up to the user) */ + if (found_index != i) + return mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + } + + if (!mz_zip_validate_file(pZip, i, flags)) + return MZ_FALSE; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr) +{ + mz_bool success = MZ_TRUE; + mz_zip_archive zip; + mz_zip_error actual_err = MZ_ZIP_NO_ERROR; + + if ((!pMem) || (!size)) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + return MZ_FALSE; + } + + mz_zip_zero_struct(&zip); + + if (!mz_zip_reader_init_mem(&zip, pMem, size, flags)) + { + if (pErr) + *pErr = zip.m_last_error; + return MZ_FALSE; + } + + if (!mz_zip_validate_archive(&zip, flags)) + { + actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (!mz_zip_reader_end_internal(&zip, success)) + { + if (!actual_err) + actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (pErr) + *pErr = actual_err; + + return success; +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr) +{ + mz_bool success = MZ_TRUE; + mz_zip_archive zip; + mz_zip_error actual_err = MZ_ZIP_NO_ERROR; + + if (!pFilename) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + return MZ_FALSE; + } + + mz_zip_zero_struct(&zip); + + if (!mz_zip_reader_init_file_v2(&zip, pFilename, flags, 0, 0)) + { + if (pErr) + *pErr = zip.m_last_error; + return MZ_FALSE; + } + + if (!mz_zip_validate_archive(&zip, flags)) + { + actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (!mz_zip_reader_end_internal(&zip, success)) + { + if (!actual_err) + actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (pErr) + *pErr = actual_err; + + return success; +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +/* ------------------- .ZIP archive writing */ + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +static MZ_FORCEINLINE void mz_write_le16(mz_uint8 *p, mz_uint16 v) +{ + p[0] = (mz_uint8)v; + p[1] = (mz_uint8)(v >> 8); +} +static MZ_FORCEINLINE void mz_write_le32(mz_uint8 *p, mz_uint32 v) +{ + p[0] = (mz_uint8)v; + p[1] = (mz_uint8)(v >> 8); + p[2] = (mz_uint8)(v >> 16); + p[3] = (mz_uint8)(v >> 24); +} +static MZ_FORCEINLINE void mz_write_le64(mz_uint8 *p, mz_uint64 v) +{ + mz_write_le32(p, (mz_uint32)v); + mz_write_le32(p + sizeof(mz_uint32), (mz_uint32)(v >> 32)); +} + +#define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v)) +#define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v)) +#define MZ_WRITE_LE64(p, v) mz_write_le64((mz_uint8 *)(p), (mz_uint64)(v)) + +static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_zip_internal_state *pState = pZip->m_pState; + mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size); + + if (!n) + return 0; + + /* An allocation this big is likely to just fail on 32-bit systems, so don't even go there. */ + if ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF)) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); + return 0; + } + + if (new_size > pState->m_mem_capacity) + { + void *pNew_block; + size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); + + while (new_capacity < new_size) + new_capacity *= 2; + + if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + return 0; + } + + pState->m_pMem = pNew_block; + pState->m_mem_capacity = new_capacity; + } + memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n); + pState->m_mem_size = (size_t)new_size; + return n; +} + +static mz_bool mz_zip_writer_end_internal(mz_zip_archive *pZip, mz_bool set_last_error) +{ + mz_zip_internal_state *pState; + mz_bool status = MZ_TRUE; + + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED))) + { + if (set_last_error) + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + pState = pZip->m_pState; + pZip->m_pState = NULL; + mz_zip_array_clear(pZip, &pState->m_central_dir); + mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); + mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +#ifndef MINIZ_NO_STDIO + if (pState->m_pFile) + { + if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) + { + if (MZ_FCLOSE(pState->m_pFile) == EOF) + { + if (set_last_error) + mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); + status = MZ_FALSE; + } + } + + pState->m_pFile = NULL; + } +#endif /* #ifndef MINIZ_NO_STDIO */ + + if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem); + pState->m_pMem = NULL; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + return status; +} + +mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags) +{ + mz_bool zip64 = (flags & MZ_ZIP_FLAG_WRITE_ZIP64) != 0; + + if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + { + if (!pZip->m_pRead) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + if (pZip->m_file_offset_alignment) + { + /* Ensure user specified file offset alignment is a power of 2. */ + if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + if (!pZip->m_pAlloc) + pZip->m_pAlloc = miniz_def_alloc_func; + if (!pZip->m_pFree) + pZip->m_pFree = miniz_def_free_func; + if (!pZip->m_pRealloc) + pZip->m_pRealloc = miniz_def_realloc_func; + + pZip->m_archive_size = existing_size; + pZip->m_central_directory_file_ofs = 0; + pZip->m_total_files = 0; + + if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); + + pZip->m_pState->m_zip64 = zip64; + pZip->m_pState->m_zip64_has_extended_info_fields = zip64; + + pZip->m_zip_type = MZ_ZIP_TYPE_USER; + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size) +{ + return mz_zip_writer_init_v2(pZip, existing_size, 0); +} + +mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags) +{ + pZip->m_pWrite = mz_zip_heap_write_func; + pZip->m_pNeeds_keepalive = NULL; + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + pZip->m_pRead = mz_zip_mem_read_func; + + pZip->m_pIO_opaque = pZip; + + if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_HEAP; + + if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning))) + { + if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size))) + { + mz_zip_writer_end_internal(pZip, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + pZip->m_pState->m_mem_capacity = initial_allocation_size; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size) +{ + return mz_zip_writer_init_heap_v2(pZip, size_to_reserve_at_beginning, initial_allocation_size, 0); +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + + file_ofs += pZip->m_pState->m_file_archive_start_ofs; + + if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); + return 0; + } + + return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile); +} + +mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning) +{ + return mz_zip_writer_init_file_v2(pZip, pFilename, size_to_reserve_at_beginning, 0); +} + +mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags) +{ + MZ_FILE *pFile; + + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pNeeds_keepalive = NULL; + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + pZip->m_pRead = mz_zip_file_read_func; + + pZip->m_pIO_opaque = pZip; + + if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags)) + return MZ_FALSE; + + if (NULL == (pFile = MZ_FOPEN(pFilename, (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) ? "w+b" : "wb"))) + { + mz_zip_writer_end(pZip); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + } + + pZip->m_pState->m_pFile = pFile; + pZip->m_zip_type = MZ_ZIP_TYPE_FILE; + + if (size_to_reserve_at_beginning) + { + mz_uint64 cur_ofs = 0; + char buf[4096]; + + MZ_CLEAR_ARR(buf); + + do + { + size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n) + { + mz_zip_writer_end(pZip); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_ofs += n; + size_to_reserve_at_beginning -= n; + } while (size_to_reserve_at_beginning); + } + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags) +{ + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pNeeds_keepalive = NULL; + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + pZip->m_pRead = mz_zip_file_read_func; + + pZip->m_pIO_opaque = pZip; + + if (!mz_zip_writer_init_v2(pZip, 0, flags)) + return MZ_FALSE; + + pZip->m_pState->m_pFile = pFile; + pZip->m_pState->m_file_archive_start_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + pZip->m_zip_type = MZ_ZIP_TYPE_CFILE; + + return MZ_TRUE; +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags) +{ + mz_zip_internal_state *pState; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (flags & MZ_ZIP_FLAG_WRITE_ZIP64) + { + /* We don't support converting a non-zip64 file to zip64 - this seems like more trouble than it's worth. (What about the existing 32-bit data descriptors that could follow the compressed data?) */ + if (!pZip->m_pState->m_zip64) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + /* No sense in trying to write to an archive that's already at the support max size */ + if (pZip->m_pState->m_zip64) + { + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if (pZip->m_total_files == MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + if ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); + } + + pState = pZip->m_pState; + + if (pState->m_pFile) + { +#ifdef MINIZ_NO_STDIO + (void)pFilename; + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +#else + if (pZip->m_pIO_opaque != pZip) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) + { + if (!pFilename) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Archive is being read from stdio and was originally opened only for reading. Try to reopen as writable. */ + if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile))) + { + /* The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it. */ + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + } + } + + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pNeeds_keepalive = NULL; +#endif /* #ifdef MINIZ_NO_STDIO */ + } + else if (pState->m_pMem) + { + /* Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback. */ + if (pZip->m_pIO_opaque != pZip) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState->m_mem_capacity = pState->m_mem_size; + pZip->m_pWrite = mz_zip_heap_write_func; + pZip->m_pNeeds_keepalive = NULL; + } + /* Archive is being read via a user provided read function - make sure the user has specified a write function too. */ + else if (!pZip->m_pWrite) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Start writing new files at the archive's current central directory location. */ + /* TODO: We could add a flag that lets the user start writing immediately AFTER the existing central dir - this would be safer. */ + pZip->m_archive_size = pZip->m_central_directory_file_ofs; + pZip->m_central_directory_file_ofs = 0; + + /* Clear the sorted central dir offsets, they aren't useful or maintained now. */ + /* Even though we're now in write mode, files can still be extracted and verified, but file locates will be slow. */ + /* TODO: We could easily maintain the sorted central directory offsets. */ + mz_zip_array_clear(pZip, &pZip->m_pState->m_sorted_central_dir_offsets); + + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename) +{ + return mz_zip_writer_init_from_reader_v2(pZip, pFilename, 0); +} + +/* TODO: pArchive_name is a terrible name here! */ +mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags) +{ + return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0); +} + +typedef struct +{ + mz_zip_archive *m_pZip; + mz_uint64 m_cur_archive_file_ofs; + mz_uint64 m_comp_size; +} mz_zip_writer_add_state; + +static mz_bool mz_zip_writer_add_put_buf_callback(const void *pBuf, int len, void *pUser) +{ + mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser; + if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len) + return MZ_FALSE; + + pState->m_cur_archive_file_ofs += len; + pState->m_comp_size += len; + return MZ_TRUE; +} + +#define MZ_ZIP64_MAX_LOCAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 2) +#define MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 3) +static mz_uint32 mz_zip_writer_create_zip64_extra_data(mz_uint8 *pBuf, mz_uint64 *pUncomp_size, mz_uint64 *pComp_size, mz_uint64 *pLocal_header_ofs) +{ + mz_uint8 *pDst = pBuf; + mz_uint32 field_size = 0; + + MZ_WRITE_LE16(pDst + 0, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID); + MZ_WRITE_LE16(pDst + 2, 0); + pDst += sizeof(mz_uint16) * 2; + + if (pUncomp_size) + { + MZ_WRITE_LE64(pDst, *pUncomp_size); + pDst += sizeof(mz_uint64); + field_size += sizeof(mz_uint64); + } + + if (pComp_size) + { + MZ_WRITE_LE64(pDst, *pComp_size); + pDst += sizeof(mz_uint64); + field_size += sizeof(mz_uint64); + } + + if (pLocal_header_ofs) + { + MZ_WRITE_LE64(pDst, *pLocal_header_ofs); + pDst += sizeof(mz_uint64); + field_size += sizeof(mz_uint64); + } + + MZ_WRITE_LE16(pBuf + 2, field_size); + + return (mz_uint32)(pDst - pBuf); +} + +static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date) +{ + (void)pZip; + memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size); + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, + mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size, + mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, + mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, + mz_uint64 local_header_ofs, mz_uint32 ext_attributes) +{ + (void)pZip; + memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_MIN(local_header_ofs, MZ_UINT32_MAX)); + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_add_to_central_dir(mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, + const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size, + mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, + mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, + mz_uint64 local_header_ofs, mz_uint32 ext_attributes, + const char *user_extra_data, mz_uint user_extra_data_len) +{ + mz_zip_internal_state *pState = pZip->m_pState; + mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size; + size_t orig_central_dir_size = pState->m_central_dir.m_size; + mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + + if (!pZip->m_pState->m_zip64) + { + if (local_header_ofs > 0xFFFFFFFF) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); + } + + /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ + if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + user_extra_data_len + comment_size) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + if (!mz_zip_writer_create_central_dir_header(pZip, central_dir_header, filename_size, (mz_uint16)(extra_size + user_extra_data_len), comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, user_extra_data, user_extra_data_len)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, ¢ral_dir_ofs, 1))) + { + /* Try to resize the central directory array back into its original state. */ + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name) +{ + /* Basic ZIP archive filename validity checks: Valid filenames cannot start with a forward slash, cannot contain a drive letter, and cannot use DOS-style backward slashes. */ + if (*pArchive_name == '/') + return MZ_FALSE; + + /* Making sure the name does not contain drive letters or DOS style backward slashes is the responsibility of the program using miniz*/ + + return MZ_TRUE; +} + +static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip) +{ + mz_uint32 n; + if (!pZip->m_file_offset_alignment) + return 0; + n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1)); + return (mz_uint)((pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1)); +} + +static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n) +{ + char buf[4096]; + memset(buf, 0, MZ_MIN(sizeof(buf), n)); + while (n) + { + mz_uint32 s = MZ_MIN(sizeof(buf), n); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_file_ofs += s; + n -= s; + } + return MZ_TRUE; +} + +mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint64 uncomp_size, mz_uint32 uncomp_crc32) +{ + return mz_zip_writer_add_mem_ex_v2(pZip, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, uncomp_size, uncomp_crc32, NULL, NULL, 0, NULL, 0); +} + +mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, + mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, + const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) +{ + mz_uint16 method = 0, dos_time = 0, dos_date = 0; + mz_uint level, ext_attributes = 0, num_alignment_padding_bytes; + mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0; + size_t archive_name_size; + mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + tdefl_compressor *pComp = NULL; + mz_bool store_data_uncompressed; + mz_zip_internal_state *pState; + mz_uint8 *pExtra_data = NULL; + mz_uint32 extra_size = 0; + mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; + mz_uint16 bit_flags = 0; + + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + + if (uncomp_size || (buf_size && !(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) + bit_flags |= MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; + + if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) + bit_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; + + level = level_and_flags & 0xF; + store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)); + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + if (pState->m_zip64) + { + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if (pZip->m_total_files == MZ_UINT16_MAX) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ + } + if (((mz_uint64)buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + } + } + + if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + +#ifndef MINIZ_NO_TIME + if (last_modified != NULL) + { + mz_zip_time_t_to_dos_time(*last_modified, &dos_time, &dos_date); + } + else + { + MZ_TIME_T cur_time; + time(&cur_time); + mz_zip_time_t_to_dos_time(cur_time, &dos_time, &dos_date); + } +#endif /* #ifndef MINIZ_NO_TIME */ + + if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { + uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, buf_size); + uncomp_size = buf_size; + if (uncomp_size <= 3) + { + level = 0; + store_data_uncompressed = MZ_TRUE; + } + } + + archive_name_size = strlen(pArchive_name); + if (archive_name_size > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ + if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + if (!pState->m_zip64) + { + /* Bail early if the archive would obviously become too large */ + if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size + + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + user_extra_data_len + + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + user_extra_data_central_len + + MZ_ZIP_DATA_DESCRIPTER_SIZE32) > 0xFFFFFFFF) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + } + } + + if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/')) + { + /* Set DOS Subdirectory attribute bit. */ + ext_attributes |= MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG; + + /* Subdirectories cannot contain data. */ + if ((buf_size) || (uncomp_size)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + /* Try to do any allocations before writing to the archive, so if an allocation fails the file remains unmodified. (A good idea if we're doing an in-place modification.) */ + if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + (pState->m_zip64 ? MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE : 0))) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if ((!store_data_uncompressed) && (buf_size)) + { + if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + + local_dir_header_ofs += num_alignment_padding_bytes; + if (pZip->m_file_offset_alignment) + { + MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); + } + cur_archive_file_ofs += num_alignment_padding_bytes; + + MZ_CLEAR_ARR(local_dir_header); + + if (!store_data_uncompressed || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { + method = MZ_DEFLATED; + } + + if (pState->m_zip64) + { + if (uncomp_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) + { + pExtra_data = extra_data; + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len), 0, 0, 0, method, bit_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_archive_file_ofs += archive_name_size; + + if (pExtra_data != NULL) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += extra_size; + } + } + else + { + if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)user_extra_data_len, 0, 0, 0, method, bit_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_archive_file_ofs += archive_name_size; + } + + if (user_extra_data_len > 0) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += user_extra_data_len; + } + + if (store_data_uncompressed) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += buf_size; + comp_size = buf_size; + } + else if (buf_size) + { + mz_zip_writer_add_state state; + + state.m_pZip = pZip; + state.m_cur_archive_file_ofs = cur_archive_file_ofs; + state.m_comp_size = 0; + + if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) || + (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED); + } + + comp_size = state.m_comp_size; + cur_archive_file_ofs = state.m_cur_archive_file_ofs; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + pComp = NULL; + + if (uncomp_size) + { + mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; + mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; + + MZ_ASSERT(bit_flags & MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR); + + MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); + MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32); + if (pExtra_data == NULL) + { + if (comp_size > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + MZ_WRITE_LE32(local_dir_footer + 8, comp_size); + MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size); + } + else + { + MZ_WRITE_LE64(local_dir_footer + 8, comp_size); + MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size); + local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; + } + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size) + return MZ_FALSE; + + cur_archive_file_ofs += local_dir_footer_size; + } + + if (pExtra_data != NULL) + { + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, (mz_uint16)extra_size, pComment, + comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes, + user_extra_data_central, user_extra_data_central_len)) + return MZ_FALSE; + + pZip->m_total_files++; + pZip->m_archive_size = cur_archive_file_ofs; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void* callback_opaque, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) +{ + mz_uint16 gen_flags; + mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; + mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0; + mz_uint64 local_dir_header_ofs, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = 0, comp_size = 0; + size_t archive_name_size; + mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + mz_uint8 *pExtra_data = NULL; + mz_uint32 extra_size = 0; + mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; + mz_zip_internal_state *pState; + mz_uint64 file_ofs = 0, cur_archive_header_file_ofs; + + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + level = level_and_flags & 0xF; + + gen_flags = (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE) ? 0 : MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; + + if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) + gen_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; + + /* Sanity checks */ + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + if ((!pState->m_zip64) && (max_size > MZ_UINT32_MAX)) + { + /* Source file is too large for non-zip64 */ + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + pState->m_zip64 = MZ_TRUE; + } + + /* We could support this, but why? */ + if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + + if (pState->m_zip64) + { + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if (pZip->m_total_files == MZ_UINT16_MAX) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ + } + } + + archive_name_size = strlen(pArchive_name); + if (archive_name_size > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ + if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + if (!pState->m_zip64) + { + /* Bail early if the archive would obviously become too large */ + if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + + archive_name_size + comment_size + user_extra_data_len + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 1024 + + MZ_ZIP_DATA_DESCRIPTER_SIZE32 + user_extra_data_central_len) > 0xFFFFFFFF) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + } + } + +#ifndef MINIZ_NO_TIME + if (pFile_time) + { + mz_zip_time_t_to_dos_time(*pFile_time, &dos_time, &dos_date); + } +#endif + + if (max_size <= 3) + level = 0; + + if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes)) + { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += num_alignment_padding_bytes; + local_dir_header_ofs = cur_archive_file_ofs; + + if (pZip->m_file_offset_alignment) + { + MZ_ASSERT((cur_archive_file_ofs & (pZip->m_file_offset_alignment - 1)) == 0); + } + + if (max_size && level) + { + method = MZ_DEFLATED; + } + + MZ_CLEAR_ARR(local_dir_header); + if (pState->m_zip64) + { + if (max_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) + { + pExtra_data = extra_data; + if (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE) + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (max_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (max_size >= MZ_UINT32_MAX) ? &comp_size : NULL, + (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + else + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, NULL, + NULL, + (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len), 0, 0, 0, method, gen_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += archive_name_size; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += extra_size; + } + else + { + if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)user_extra_data_len, 0, 0, 0, method, gen_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += archive_name_size; + } + + if (user_extra_data_len > 0) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += user_extra_data_len; + } + + if (max_size) + { + void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE); + if (!pRead_buf) + { + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!level) + { + while (1) + { + size_t n = read_callback(callback_opaque, file_ofs, pRead_buf, MZ_ZIP_MAX_IO_BUF_SIZE); + if (n == 0) + break; + + if ((n > MZ_ZIP_MAX_IO_BUF_SIZE) || (file_ofs + n > max_size)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + file_ofs += n; + uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); + cur_archive_file_ofs += n; + } + uncomp_size = file_ofs; + comp_size = uncomp_size; + } + else + { + mz_bool result = MZ_FALSE; + mz_zip_writer_add_state state; + tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)); + if (!pComp) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + state.m_pZip = pZip; + state.m_cur_archive_file_ofs = cur_archive_file_ofs; + state.m_comp_size = 0; + + if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + } + + for (;;) + { + tdefl_status status; + tdefl_flush flush = TDEFL_NO_FLUSH; + + size_t n = read_callback(callback_opaque, file_ofs, pRead_buf, MZ_ZIP_MAX_IO_BUF_SIZE); + if ((n > MZ_ZIP_MAX_IO_BUF_SIZE) || (file_ofs + n > max_size)) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + break; + } + + file_ofs += n; + uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); + + if (pZip->m_pNeeds_keepalive != NULL && pZip->m_pNeeds_keepalive(pZip->m_pIO_opaque)) + flush = TDEFL_FULL_FLUSH; + + if (n == 0) + flush = TDEFL_FINISH; + + status = tdefl_compress_buffer(pComp, pRead_buf, n, flush); + if (status == TDEFL_STATUS_DONE) + { + result = MZ_TRUE; + break; + } + else if (status != TDEFL_STATUS_OKAY) + { + mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED); + break; + } + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + + if (!result) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return MZ_FALSE; + } + + uncomp_size = file_ofs; + comp_size = state.m_comp_size; + cur_archive_file_ofs = state.m_cur_archive_file_ofs; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + } + + if (!(level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE)) + { + mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; + mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; + + MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); + MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32); + if (pExtra_data == NULL) + { + if (comp_size > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + MZ_WRITE_LE32(local_dir_footer + 8, comp_size); + MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size); + } + else + { + MZ_WRITE_LE64(local_dir_footer + 8, comp_size); + MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size); + local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; + } + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size) + return MZ_FALSE; + + cur_archive_file_ofs += local_dir_footer_size; + } + + if (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE) + { + if (pExtra_data != NULL) + { + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (max_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (max_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, + (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len), + (max_size >= MZ_UINT32_MAX) ? MZ_UINT32_MAX : uncomp_size, + (max_size >= MZ_UINT32_MAX) ? MZ_UINT32_MAX : comp_size, + uncomp_crc32, method, gen_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + cur_archive_header_file_ofs = local_dir_header_ofs; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_header_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + if (pExtra_data != NULL) + { + cur_archive_header_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_header_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_header_file_ofs += archive_name_size; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_header_file_ofs, extra_data, extra_size) != extra_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_header_file_ofs += extra_size; + } + } + + if (pExtra_data != NULL) + { + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, (mz_uint16)extra_size, pComment, comment_size, + uncomp_size, comp_size, uncomp_crc32, method, gen_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes, + user_extra_data_central, user_extra_data_central_len)) + return MZ_FALSE; + + pZip->m_total_files++; + pZip->m_archive_size = cur_archive_file_ofs; + + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO + +static size_t mz_file_read_func_stdio(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + MZ_FILE *pSrc_file = (MZ_FILE *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pSrc_file); + + if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pSrc_file, (mz_int64)file_ofs, SEEK_SET)))) + return 0; + + return MZ_FREAD(pBuf, 1, n, pSrc_file); +} + +mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) +{ + return mz_zip_writer_add_read_buf_callback(pZip, pArchive_name, mz_file_read_func_stdio, pSrc_file, max_size, pFile_time, pComment, comment_size, level_and_flags, + user_extra_data, user_extra_data_len, user_extra_data_central, user_extra_data_central_len); +} + +mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) +{ + MZ_FILE *pSrc_file = NULL; + mz_uint64 uncomp_size = 0; + MZ_TIME_T file_modified_time; + MZ_TIME_T *pFile_time = NULL; + mz_bool status; + + memset(&file_modified_time, 0, sizeof(file_modified_time)); + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO) + pFile_time = &file_modified_time; + if (!mz_zip_get_file_modified_time(pSrc_filename, &file_modified_time)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_STAT_FAILED); +#endif + + pSrc_file = MZ_FOPEN(pSrc_filename, "rb"); + if (!pSrc_file) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + MZ_FSEEK64(pSrc_file, 0, SEEK_END); + uncomp_size = MZ_FTELL64(pSrc_file); + MZ_FSEEK64(pSrc_file, 0, SEEK_SET); + + status = mz_zip_writer_add_cfile(pZip, pArchive_name, pSrc_file, uncomp_size, pFile_time, pComment, comment_size, level_and_flags, NULL, 0, NULL, 0); + + MZ_FCLOSE(pSrc_file); + + return status; +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +static mz_bool mz_zip_writer_update_zip64_extension_block(mz_zip_array *pNew_ext, mz_zip_archive *pZip, const mz_uint8 *pExt, mz_uint32 ext_len, mz_uint64 *pComp_size, mz_uint64 *pUncomp_size, mz_uint64 *pLocal_header_ofs, mz_uint32 *pDisk_start) +{ + /* + 64 should be enough for any new zip64 data */ + if (!mz_zip_array_reserve(pZip, pNew_ext, ext_len + 64, MZ_FALSE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + mz_zip_array_resize(pZip, pNew_ext, 0, MZ_FALSE); + + if ((pUncomp_size) || (pComp_size) || (pLocal_header_ofs) || (pDisk_start)) + { + mz_uint8 new_ext_block[64]; + mz_uint8 *pDst = new_ext_block; + mz_write_le16(pDst, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID); + mz_write_le16(pDst + sizeof(mz_uint16), 0); + pDst += sizeof(mz_uint16) * 2; + + if (pUncomp_size) + { + mz_write_le64(pDst, *pUncomp_size); + pDst += sizeof(mz_uint64); + } + + if (pComp_size) + { + mz_write_le64(pDst, *pComp_size); + pDst += sizeof(mz_uint64); + } + + if (pLocal_header_ofs) + { + mz_write_le64(pDst, *pLocal_header_ofs); + pDst += sizeof(mz_uint64); + } + + if (pDisk_start) + { + mz_write_le32(pDst, *pDisk_start); + pDst += sizeof(mz_uint32); + } + + mz_write_le16(new_ext_block + sizeof(mz_uint16), (mz_uint16)((pDst - new_ext_block) - sizeof(mz_uint16) * 2)); + + if (!mz_zip_array_push_back(pZip, pNew_ext, new_ext_block, pDst - new_ext_block)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if ((pExt) && (ext_len)) + { + mz_uint32 extra_size_remaining = ext_len; + const mz_uint8 *pExtra_data = pExt; + + do + { + mz_uint32 field_id, field_data_size, field_total_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + field_total_size = field_data_size + sizeof(mz_uint16) * 2; + + if (field_total_size > extra_size_remaining) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (field_id != MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + if (!mz_zip_array_push_back(pZip, pNew_ext, pExtra_data, field_total_size)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + pExtra_data += field_total_size; + extra_size_remaining -= field_total_size; + } while (extra_size_remaining); + } + + return MZ_TRUE; +} + +/* TODO: This func is now pretty freakin complex due to zip64, split it up? */ +mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index) +{ + mz_uint n, bit_flags, num_alignment_padding_bytes, src_central_dir_following_data_size; + mz_uint64 src_archive_bytes_remaining, local_dir_header_ofs; + mz_uint64 cur_src_file_ofs, cur_dst_file_ofs; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + mz_uint8 new_central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + size_t orig_central_dir_size; + mz_zip_internal_state *pState; + void *pBuf; + const mz_uint8 *pSrc_central_header; + mz_zip_archive_file_stat src_file_stat; + mz_uint32 src_filename_len, src_comment_len, src_ext_len; + mz_uint32 local_header_filename_size, local_header_extra_len; + mz_uint64 local_header_comp_size, local_header_uncomp_size; + mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE; + + /* Sanity checks */ + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pSource_zip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + /* Don't support copying files from zip64 archives to non-zip64, even though in some cases this is possible */ + if ((pSource_zip->m_pState->m_zip64) && (!pZip->m_pState->m_zip64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Get pointer to the source central dir header and crack it */ + if (NULL == (pSrc_central_header = mz_zip_get_cdh(pSource_zip, src_file_index))) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_SIG_OFS) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + src_filename_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS); + src_comment_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS); + src_ext_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS); + src_central_dir_following_data_size = src_filename_len + src_ext_len + src_comment_len; + + /* TODO: We don't support central dir's >= MZ_UINT32_MAX bytes right now (+32 fudge factor in case we need to add more extra data) */ + if ((pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + 32) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + if (!pState->m_zip64) + { + if (pZip->m_total_files == MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + /* TODO: Our zip64 support still has some 32-bit limits that may not be worth fixing. */ + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + + if (!mz_zip_file_stat_internal(pSource_zip, src_file_index, pSrc_central_header, &src_file_stat, NULL)) + return MZ_FALSE; + + cur_src_file_ofs = src_file_stat.m_local_header_ofs; + cur_dst_file_ofs = pZip->m_archive_size; + + /* Read the source archive's local dir header */ + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; + + /* Compute the total size we need to copy (filename+extra data+compressed data) */ + local_header_filename_size = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS); + local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS); + local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS); + src_archive_bytes_remaining = local_header_filename_size + local_header_extra_len + src_file_stat.m_comp_size; + + /* Try to find a zip64 extended information field */ + if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX))) + { + mz_zip_array file_data_array; + const mz_uint8 *pExtra_data; + mz_uint32 extra_size_remaining = local_header_extra_len; + + mz_zip_array_init(&file_data_array, 1); + if (!mz_zip_array_resize(pZip, &file_data_array, local_header_extra_len, MZ_FALSE)) + { + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, src_file_stat.m_local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_size, file_data_array.m_p, local_header_extra_len) != local_header_extra_len) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + pExtra_data = (const mz_uint8 *)file_data_array.m_p; + + do + { + mz_uint32 field_id, field_data_size, field_total_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + field_total_size = field_data_size + sizeof(mz_uint16) * 2; + + if (field_total_size > extra_size_remaining) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32); + + if (field_data_size < sizeof(mz_uint64) * 2) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data); + local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64)); /* may be 0 if there's a descriptor */ + + found_zip64_ext_data_in_ldir = MZ_TRUE; + break; + } + + pExtra_data += field_total_size; + extra_size_remaining -= field_total_size; + } while (extra_size_remaining); + + mz_zip_array_clear(pZip, &file_data_array); + } + + if (!pState->m_zip64) + { + /* Try to detect if the new archive will most likely wind up too big and bail early (+(sizeof(mz_uint32) * 4) is for the optional descriptor which could be present, +64 is a fudge factor). */ + /* We also check when the archive is finalized so this doesn't need to be perfect. */ + mz_uint64 approx_new_archive_size = cur_dst_file_ofs + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + src_archive_bytes_remaining + (sizeof(mz_uint32) * 4) + + pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 64; + + if (approx_new_archive_size >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + } + + /* Write dest archive padding */ + if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes)) + return MZ_FALSE; + + cur_dst_file_ofs += num_alignment_padding_bytes; + + local_dir_header_ofs = cur_dst_file_ofs; + if (pZip->m_file_offset_alignment) + { + MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); + } + + /* The original zip's local header+ext block doesn't change, even with zip64, so we can just copy it over to the dest zip */ + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; + + /* Copy over the source archive bytes to the dest archive, also ensure we have enough buf space to handle optional data descriptor */ + if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)MZ_MAX(32U, MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining))))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + while (src_archive_bytes_remaining) + { + n = (mz_uint)MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining); + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + cur_src_file_ofs += n; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_dst_file_ofs += n; + + src_archive_bytes_remaining -= n; + } + + /* Now deal with the optional data descriptor */ + bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); + if (bit_flags & 8) + { + /* Copy data descriptor */ + if ((pSource_zip->m_pState->m_zip64) || (found_zip64_ext_data_in_ldir)) + { + /* src is zip64, dest must be zip64 */ + + /* name uint32_t's */ + /* id 1 (optional in zip64?) */ + /* crc 1 */ + /* comp_size 2 */ + /* uncomp_size 2 */ + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, (sizeof(mz_uint32) * 6)) != (sizeof(mz_uint32) * 6)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID) ? 6 : 5); + } + else + { + /* src is NOT zip64 */ + mz_bool has_id; + + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + has_id = (MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID); + + if (pZip->m_pState->m_zip64) + { + /* dest is zip64, so upgrade the data descriptor */ + const mz_uint8 *pSrc_descriptor = (const mz_uint8 *)pBuf + (has_id ? sizeof(mz_uint32) : 0); + const mz_uint32 src_crc32 = MZ_READ_LE32(pSrc_descriptor); + const mz_uint64 src_comp_size = MZ_READ_LE32(pSrc_descriptor + sizeof(mz_uint32)); + const mz_uint64 src_uncomp_size = MZ_READ_LE32(pSrc_descriptor + 2*sizeof(mz_uint32)); + + mz_write_le32((mz_uint8 *)pBuf, MZ_ZIP_DATA_DESCRIPTOR_ID); + mz_write_le32((mz_uint8 *)pBuf + sizeof(mz_uint32) * 1, src_crc32); + mz_write_le64((mz_uint8 *)pBuf + sizeof(mz_uint32) * 2, src_comp_size); + mz_write_le64((mz_uint8 *)pBuf + sizeof(mz_uint32) * 4, src_uncomp_size); + + n = sizeof(mz_uint32) * 6; + } + else + { + /* dest is NOT zip64, just copy it as-is */ + n = sizeof(mz_uint32) * (has_id ? 4 : 3); + } + } + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_src_file_ofs += n; + cur_dst_file_ofs += n; + } + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + + /* Finally, add the new central dir header */ + orig_central_dir_size = pState->m_central_dir.m_size; + + memcpy(new_central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); + + if (pState->m_zip64) + { + /* This is the painful part: We need to write a new central dir header + ext block with updated zip64 fields, and ensure the old fields (if any) are not included. */ + const mz_uint8 *pSrc_ext = pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len; + mz_zip_array new_ext_block; + + mz_zip_array_init(&new_ext_block, sizeof(mz_uint8)); + + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_UINT32_MAX); + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_UINT32_MAX); + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_UINT32_MAX); + + if (!mz_zip_writer_update_zip64_extension_block(&new_ext_block, pZip, pSrc_ext, src_ext_len, &src_file_stat.m_comp_size, &src_file_stat.m_uncomp_size, &local_dir_header_ofs, NULL)) + { + mz_zip_array_clear(pZip, &new_ext_block); + return MZ_FALSE; + } + + MZ_WRITE_LE16(new_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS, new_ext_block.m_size); + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) + { + mz_zip_array_clear(pZip, &new_ext_block); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_filename_len)) + { + mz_zip_array_clear(pZip, &new_ext_block); + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_ext_block.m_p, new_ext_block.m_size)) + { + mz_zip_array_clear(pZip, &new_ext_block); + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len + src_ext_len, src_comment_len)) + { + mz_zip_array_clear(pZip, &new_ext_block); + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + mz_zip_array_clear(pZip, &new_ext_block); + } + else + { + /* sanity checks */ + if (cur_dst_file_ofs > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + if (local_dir_header_ofs >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs); + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_central_dir_following_data_size)) + { + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + } + + /* This shouldn't trigger unless we screwed up during the initial sanity checks */ + if (pState->m_central_dir.m_size >= MZ_UINT32_MAX) + { + /* TODO: Support central dirs >= 32-bits in size */ + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + } + + n = (mz_uint32)orig_central_dir_size; + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1)) + { + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + pZip->m_total_files++; + pZip->m_archive_size = cur_dst_file_ofs; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) +{ + mz_zip_internal_state *pState; + mz_uint64 central_dir_ofs, central_dir_size; + mz_uint8 hdr[256]; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + if (pState->m_zip64) + { + if ((mz_uint64)pState->m_central_dir.m_size >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if ((pZip->m_total_files > MZ_UINT16_MAX) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + + central_dir_ofs = 0; + central_dir_size = 0; + if (pZip->m_total_files) + { + /* Write central directory */ + central_dir_ofs = pZip->m_archive_size; + central_dir_size = pState->m_central_dir.m_size; + pZip->m_central_directory_file_ofs = central_dir_ofs; + if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + pZip->m_archive_size += central_dir_size; + } + + if (pState->m_zip64) + { + /* Write zip64 end of central directory header */ + mz_uint64 rel_ofs_to_zip64_ecdr = pZip->m_archive_size; + + MZ_CLEAR_ARR(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDH_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - sizeof(mz_uint32) - sizeof(mz_uint64)); + MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS, 0x031E); /* TODO: always Unix */ + MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_NEEDED_OFS, 0x002D); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_SIZE_OFS, central_dir_size); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_OFS_OFS, central_dir_ofs); + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE; + + /* Write zip64 end of central directory locator */ + MZ_CLEAR_ARR(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS, rel_ofs_to_zip64_ecdr); + MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS, 1); + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE; + } + + /* Write end of central directory record */ + MZ_CLEAR_ARR(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); + MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_size)); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_ofs)); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + +#ifndef MINIZ_NO_STDIO + if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); +#endif /* #ifndef MINIZ_NO_STDIO */ + + pZip->m_archive_size += MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE; + + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED; + return MZ_TRUE; +} + +mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize) +{ + if ((!ppBuf) || (!pSize)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + *ppBuf = NULL; + *pSize = 0; + + if ((!pZip) || (!pZip->m_pState)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (pZip->m_pWrite != mz_zip_heap_write_func) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_writer_finalize_archive(pZip)) + return MZ_FALSE; + + *ppBuf = pZip->m_pState->m_pMem; + *pSize = pZip->m_pState->m_mem_size; + pZip->m_pState->m_pMem = NULL; + pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_end(mz_zip_archive *pZip) +{ + return mz_zip_writer_end_internal(pZip, MZ_TRUE); +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) +{ + return mz_zip_add_mem_to_archive_file_in_place_v2(pZip_filename, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, NULL); +} + +mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr) +{ + mz_bool status, created_new_archive = MZ_FALSE; + mz_zip_archive zip_archive; + struct MZ_FILE_STAT_STRUCT file_stat; + mz_zip_error actual_err = MZ_ZIP_NO_ERROR; + + mz_zip_zero_struct(&zip_archive); + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + + if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION)) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + return MZ_FALSE; + } + + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_FILENAME; + return MZ_FALSE; + } + + /* Important: The regular non-64 bit version of stat() can fail here if the file is very large, which could cause the archive to be overwritten. */ + /* So be sure to compile with _LARGEFILE64_SOURCE 1 */ + if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0) + { + /* Create a new archive. */ + if (!mz_zip_writer_init_file_v2(&zip_archive, pZip_filename, 0, level_and_flags)) + { + if (pErr) + *pErr = zip_archive.m_last_error; + return MZ_FALSE; + } + + created_new_archive = MZ_TRUE; + } + else + { + /* Append to an existing archive. */ + if (!mz_zip_reader_init_file_v2(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) + { + if (pErr) + *pErr = zip_archive.m_last_error; + return MZ_FALSE; + } + + if (!mz_zip_writer_init_from_reader_v2(&zip_archive, pZip_filename, level_and_flags)) + { + if (pErr) + *pErr = zip_archive.m_last_error; + + mz_zip_reader_end_internal(&zip_archive, MZ_FALSE); + + return MZ_FALSE; + } + } + + status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0); + actual_err = zip_archive.m_last_error; + + /* Always finalize, even if adding failed for some reason, so we have a valid central directory. (This may not always succeed, but we can try.) */ + if (!mz_zip_writer_finalize_archive(&zip_archive)) + { + if (!actual_err) + actual_err = zip_archive.m_last_error; + + status = MZ_FALSE; + } + + if (!mz_zip_writer_end_internal(&zip_archive, status)) + { + if (!actual_err) + actual_err = zip_archive.m_last_error; + + status = MZ_FALSE; + } + + if ((!status) && (created_new_archive)) + { + /* It's a new archive and something went wrong, so just delete it. */ + int ignoredStatus = MZ_DELETE_FILE(pZip_filename); + (void)ignoredStatus; + } + + if (pErr) + *pErr = actual_err; + + return status; +} + +void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr) +{ + mz_uint32 file_index; + mz_zip_archive zip_archive; + void *p = NULL; + + if (pSize) + *pSize = 0; + + if ((!pZip_filename) || (!pArchive_name)) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + + return NULL; + } + + mz_zip_zero_struct(&zip_archive); + if (!mz_zip_reader_init_file_v2(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) + { + if (pErr) + *pErr = zip_archive.m_last_error; + + return NULL; + } + + if (mz_zip_reader_locate_file_v2(&zip_archive, pArchive_name, pComment, flags, &file_index)) + { + p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags); + } + + mz_zip_reader_end_internal(&zip_archive, p != NULL); + + if (pErr) + *pErr = zip_archive.m_last_error; + + return p; +} + +void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags) +{ + return mz_zip_extract_archive_file_to_heap_v2(pZip_filename, pArchive_name, NULL, pSize, flags, NULL); +} + +#endif /* #ifndef MINIZ_NO_STDIO */ + +#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ + +/* ------------------- Misc utils */ + +mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip) +{ + return pZip ? pZip->m_zip_mode : MZ_ZIP_MODE_INVALID; +} + +mz_zip_type mz_zip_get_type(mz_zip_archive *pZip) +{ + return pZip ? pZip->m_zip_type : MZ_ZIP_TYPE_INVALID; +} + +mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num) +{ + mz_zip_error prev_err; + + if (!pZip) + return MZ_ZIP_INVALID_PARAMETER; + + prev_err = pZip->m_last_error; + + pZip->m_last_error = err_num; + return prev_err; +} + +mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip) +{ + if (!pZip) + return MZ_ZIP_INVALID_PARAMETER; + + return pZip->m_last_error; +} + +mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip) +{ + return mz_zip_set_last_error(pZip, MZ_ZIP_NO_ERROR); +} + +mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip) +{ + mz_zip_error prev_err; + + if (!pZip) + return MZ_ZIP_INVALID_PARAMETER; + + prev_err = pZip->m_last_error; + + pZip->m_last_error = MZ_ZIP_NO_ERROR; + return prev_err; +} + +const char *mz_zip_get_error_string(mz_zip_error mz_err) +{ + switch (mz_err) + { + case MZ_ZIP_NO_ERROR: + return "no error"; + case MZ_ZIP_UNDEFINED_ERROR: + return "undefined error"; + case MZ_ZIP_TOO_MANY_FILES: + return "too many files"; + case MZ_ZIP_FILE_TOO_LARGE: + return "file too large"; + case MZ_ZIP_UNSUPPORTED_METHOD: + return "unsupported method"; + case MZ_ZIP_UNSUPPORTED_ENCRYPTION: + return "unsupported encryption"; + case MZ_ZIP_UNSUPPORTED_FEATURE: + return "unsupported feature"; + case MZ_ZIP_FAILED_FINDING_CENTRAL_DIR: + return "failed finding central directory"; + case MZ_ZIP_NOT_AN_ARCHIVE: + return "not a ZIP archive"; + case MZ_ZIP_INVALID_HEADER_OR_CORRUPTED: + return "invalid header or archive is corrupted"; + case MZ_ZIP_UNSUPPORTED_MULTIDISK: + return "unsupported multidisk archive"; + case MZ_ZIP_DECOMPRESSION_FAILED: + return "decompression failed or archive is corrupted"; + case MZ_ZIP_COMPRESSION_FAILED: + return "compression failed"; + case MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE: + return "unexpected decompressed size"; + case MZ_ZIP_CRC_CHECK_FAILED: + return "CRC-32 check failed"; + case MZ_ZIP_UNSUPPORTED_CDIR_SIZE: + return "unsupported central directory size"; + case MZ_ZIP_ALLOC_FAILED: + return "allocation failed"; + case MZ_ZIP_FILE_OPEN_FAILED: + return "file open failed"; + case MZ_ZIP_FILE_CREATE_FAILED: + return "file create failed"; + case MZ_ZIP_FILE_WRITE_FAILED: + return "file write failed"; + case MZ_ZIP_FILE_READ_FAILED: + return "file read failed"; + case MZ_ZIP_FILE_CLOSE_FAILED: + return "file close failed"; + case MZ_ZIP_FILE_SEEK_FAILED: + return "file seek failed"; + case MZ_ZIP_FILE_STAT_FAILED: + return "file stat failed"; + case MZ_ZIP_INVALID_PARAMETER: + return "invalid parameter"; + case MZ_ZIP_INVALID_FILENAME: + return "invalid filename"; + case MZ_ZIP_BUF_TOO_SMALL: + return "buffer too small"; + case MZ_ZIP_INTERNAL_ERROR: + return "internal error"; + case MZ_ZIP_FILE_NOT_FOUND: + return "file not found"; + case MZ_ZIP_ARCHIVE_TOO_LARGE: + return "archive is too large"; + case MZ_ZIP_VALIDATION_FAILED: + return "validation failed"; + case MZ_ZIP_WRITE_CALLBACK_FAILED: + return "write callback failed"; + case MZ_ZIP_TOTAL_ERRORS: + return "total errors"; + default: + break; + } + + return "unknown error"; +} + +/* Note: Just because the archive is not zip64 doesn't necessarily mean it doesn't have Zip64 extended information extra field, argh. */ +mz_bool mz_zip_is_zip64(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState)) + return MZ_FALSE; + + return pZip->m_pState->m_zip64; +} + +size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState)) + return 0; + + return pZip->m_pState->m_central_dir.m_size; +} + +mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip) +{ + return pZip ? pZip->m_total_files : 0; +} + +mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip) +{ + if (!pZip) + return 0; + return pZip->m_archive_size; +} + +mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState)) + return 0; + return pZip->m_pState->m_file_archive_start_ofs; +} + +MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState)) + return 0; + return pZip->m_pState->m_pFile; +} + +size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + return pZip->m_pRead(pZip->m_pIO_opaque, file_ofs, pBuf, n); +} + +mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size) +{ + mz_uint n; + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) + { + if (filename_buf_size) + pFilename[0] = '\0'; + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return 0; + } + n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + if (filename_buf_size) + { + n = MZ_MIN(n, filename_buf_size - 1); + memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); + pFilename[n] = '\0'; + } + return n + 1; +} + +mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat) +{ + return mz_zip_file_stat_internal(pZip, file_index, mz_zip_get_cdh(pZip, file_index), pStat, NULL); +} + +mz_bool mz_zip_end(mz_zip_archive *pZip) +{ + if (!pZip) + return MZ_FALSE; + + if (pZip->m_zip_mode == MZ_ZIP_MODE_READING) + return mz_zip_reader_end(pZip); +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + else if ((pZip->m_zip_mode == MZ_ZIP_MODE_WRITING) || (pZip->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED)) + return mz_zip_writer_end(pZip); +#endif + + return MZ_FALSE; +} + +#ifdef __cplusplus +} +#endif + +#endif /*#ifndef MINIZ_NO_ARCHIVE_APIS*/ diff --git a/Source/UnrealLibretroEditor/miniz/miniz.h b/Source/UnrealLibretroEditor/miniz/miniz.h new file mode 100644 index 00000000..35c740c7 --- /dev/null +++ b/Source/UnrealLibretroEditor/miniz/miniz.h @@ -0,0 +1,1422 @@ +#ifndef MINIZ_EXPORT +#define MINIZ_EXPORT +#endif +/* miniz.c 3.0.0 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing + See "unlicense" statement at the end of this file. + Rich Geldreich , last updated Oct. 13, 2013 + Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt + + Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define + MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros). + + * Low-level Deflate/Inflate implementation notes: + + Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or + greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses + approximately as well as zlib. + + Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function + coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory + block large enough to hold the entire file. + + The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation. + + * zlib-style API notes: + + miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in + zlib replacement in many apps: + The z_stream struct, optional memory allocation callbacks + deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound + inflateInit/inflateInit2/inflate/inflateReset/inflateEnd + compress, compress2, compressBound, uncompress + CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines. + Supports raw deflate streams or standard zlib streams with adler-32 checking. + + Limitations: + The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries. + I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but + there are no guarantees that miniz.c pulls this off perfectly. + + * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by + Alex Evans. Supports 1-4 bytes/pixel images. + + * ZIP archive API notes: + + The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to + get the job done with minimal fuss. There are simple API's to retrieve file information, read files from + existing archives, create new archives, append new files to existing archives, or clone archive data from + one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h), + or you can specify custom file read/write callbacks. + + - Archive reading: Just call this function to read a single file from a disk archive: + + void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, + size_t *pSize, mz_uint zip_flags); + + For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central + directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files. + + - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file: + + int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); + + The locate operation can optionally check file comments too, which (as one example) can be used to identify + multiple versions of the same file in an archive. This function uses a simple linear search through the central + directory, so it's not very fast. + + Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and + retrieve detailed info on each file by calling mz_zip_reader_file_stat(). + + - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data + to disk and builds an exact image of the central directory in memory. The central directory image is written + all at once at the end of the archive file when the archive is finalized. + + The archive writer can optionally align each file's local header and file data to any power of 2 alignment, + which can be useful when the archive will be read from optical media. Also, the writer supports placing + arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still + readable by any ZIP tool. + + - Archive appending: The simple way to add a single file to an archive is to call this function: + + mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, + const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); + + The archive will be created if it doesn't already exist, otherwise it'll be appended to. + Note the appending is done in-place and is not an atomic operation, so if something goes wrong + during the operation it's possible the archive could be left without a central directory (although the local + file headers and file data will be fine, so the archive will be recoverable). + + For more complex archive modification scenarios: + 1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to + preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the + compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and + you're done. This is safe but requires a bunch of temporary disk space or heap memory. + + 2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(), + append new files as needed, then finalize the archive which will write an updated central directory to the + original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a + possibility that the archive's central directory could be lost with this method if anything goes wrong, though. + + - ZIP archive support limitations: + No spanning support. Extraction functions can only handle unencrypted, stored or deflated files. + Requires streams capable of seeking. + + * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the + below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it. + + * Important: For best perf. be sure to customize the below macros for your target platform: + #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 + #define MINIZ_LITTLE_ENDIAN 1 + #define MINIZ_HAS_64BIT_REGISTERS 1 + + * On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before including miniz.c to ensure miniz + uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files + (i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes). +*/ +#pragma once + + + +/* Defines to completely disable specific portions of miniz.c: + If all macros here are defined the only functionality remaining will be CRC-32 and adler-32. */ + +/* Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. */ +/*#define MINIZ_NO_STDIO */ + +/* If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or */ +/* get/set file times, and the C run-time funcs that get/set times won't be called. */ +/* The current downside is the times written to your archives will be from 1979. */ +/*#define MINIZ_NO_TIME */ + +/* Define MINIZ_NO_DEFLATE_APIS to disable all compression API's. */ +/*#define MINIZ_NO_DEFLATE_APIS */ + +/* Define MINIZ_NO_INFLATE_APIS to disable all decompression API's. */ +/*#define MINIZ_NO_INFLATE_APIS */ + +/* Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. */ +/*#define MINIZ_NO_ARCHIVE_APIS */ + +/* Define MINIZ_NO_ARCHIVE_WRITING_APIS to disable all writing related ZIP archive API's. */ +/*#define MINIZ_NO_ARCHIVE_WRITING_APIS */ + +/* Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's. */ +/*#define MINIZ_NO_ZLIB_APIS */ + +/* Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib. */ +/*#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ + +/* Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. + Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc + callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user + functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. */ +/*#define MINIZ_NO_MALLOC */ + +#ifdef MINIZ_NO_INFLATE_APIS +#define MINIZ_NO_ARCHIVE_APIS +#endif + +#ifdef MINIZ_NO_DEFLATE_APIS +#define MINIZ_NO_ARCHIVE_WRITING_APIS +#endif + +#if defined(__TINYC__) && (defined(__linux) || defined(__linux__)) +/* TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux */ +#define MINIZ_NO_TIME +#endif + +#include + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS) +#include +#endif + +#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__) +/* MINIZ_X86_OR_X64_CPU is only used to help set the below macros. */ +#define MINIZ_X86_OR_X64_CPU 1 +#else +#define MINIZ_X86_OR_X64_CPU 0 +#endif + +/* Set MINIZ_LITTLE_ENDIAN only if not set */ +#if !defined(MINIZ_LITTLE_ENDIAN) +#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) + +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) +/* Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. */ +#define MINIZ_LITTLE_ENDIAN 1 +#else +#define MINIZ_LITTLE_ENDIAN 0 +#endif + +#else + +#if MINIZ_X86_OR_X64_CPU +#define MINIZ_LITTLE_ENDIAN 1 +#else +#define MINIZ_LITTLE_ENDIAN 0 +#endif + +#endif +#endif + +/* Using unaligned loads and stores causes errors when using UBSan */ +#if defined(__has_feature) +#if __has_feature(undefined_behavior_sanitizer) +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 +#endif +#endif + +/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES only if not set */ +#if !defined(MINIZ_USE_UNALIGNED_LOADS_AND_STORES) +#if MINIZ_X86_OR_X64_CPU +/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. */ +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 +#define MINIZ_UNALIGNED_USE_MEMCPY +#else +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 +#endif +#endif + +#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__) +/* Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions). */ +#define MINIZ_HAS_64BIT_REGISTERS 1 +#else +#define MINIZ_HAS_64BIT_REGISTERS 0 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- zlib-style API Definitions. */ + +/* For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! */ +typedef unsigned long mz_ulong; + +/* mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap. */ +MINIZ_EXPORT void mz_free(void *p); + +#define MZ_ADLER32_INIT (1) +/* mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. */ +MINIZ_EXPORT mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); + +#define MZ_CRC32_INIT (0) +/* mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. */ +MINIZ_EXPORT mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); + +/* Compression strategies. */ +enum +{ + MZ_DEFAULT_STRATEGY = 0, + MZ_FILTERED = 1, + MZ_HUFFMAN_ONLY = 2, + MZ_RLE = 3, + MZ_FIXED = 4 +}; + +/* Method */ +#define MZ_DEFLATED 8 + +/* Heap allocation callbacks. +Note that mz_alloc_func parameter types purposely differ from zlib's: items/size is size_t, not unsigned long. */ +typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); +typedef void (*mz_free_func)(void *opaque, void *address); +typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size); + +/* Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. */ +enum +{ + MZ_NO_COMPRESSION = 0, + MZ_BEST_SPEED = 1, + MZ_BEST_COMPRESSION = 9, + MZ_UBER_COMPRESSION = 10, + MZ_DEFAULT_LEVEL = 6, + MZ_DEFAULT_COMPRESSION = -1 +}; + +#define MZ_VERSION "11.0.1" +#define MZ_VERNUM 0xB001 +#define MZ_VER_MAJOR 11 +#define MZ_VER_MINOR 1 +#define MZ_VER_REVISION 0 +#define MZ_VER_SUBREVISION 0 + +#ifndef MINIZ_NO_ZLIB_APIS + +/* Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs). */ +enum +{ + MZ_NO_FLUSH = 0, + MZ_PARTIAL_FLUSH = 1, + MZ_SYNC_FLUSH = 2, + MZ_FULL_FLUSH = 3, + MZ_FINISH = 4, + MZ_BLOCK = 5 +}; + +/* Return status codes. MZ_PARAM_ERROR is non-standard. */ +enum +{ + MZ_OK = 0, + MZ_STREAM_END = 1, + MZ_NEED_DICT = 2, + MZ_ERRNO = -1, + MZ_STREAM_ERROR = -2, + MZ_DATA_ERROR = -3, + MZ_MEM_ERROR = -4, + MZ_BUF_ERROR = -5, + MZ_VERSION_ERROR = -6, + MZ_PARAM_ERROR = -10000 +}; + +/* Window bits */ +#define MZ_DEFAULT_WINDOW_BITS 15 + +struct mz_internal_state; + +/* Compression/decompression stream struct. */ +typedef struct mz_stream_s +{ + const unsigned char *next_in; /* pointer to next byte to read */ + unsigned int avail_in; /* number of bytes available at next_in */ + mz_ulong total_in; /* total number of bytes consumed so far */ + + unsigned char *next_out; /* pointer to next byte to write */ + unsigned int avail_out; /* number of bytes that can be written to next_out */ + mz_ulong total_out; /* total number of bytes produced so far */ + + char *msg; /* error msg (unused) */ + struct mz_internal_state *state; /* internal state, allocated by zalloc/zfree */ + + mz_alloc_func zalloc; /* optional heap allocation function (defaults to malloc) */ + mz_free_func zfree; /* optional heap free function (defaults to free) */ + void *opaque; /* heap alloc function user pointer */ + + int data_type; /* data_type (unused) */ + mz_ulong adler; /* adler32 of the source or uncompressed data */ + mz_ulong reserved; /* not used */ +} mz_stream; + +typedef mz_stream *mz_streamp; + +/* Returns the version string of miniz.c. */ +MINIZ_EXPORT const char *mz_version(void); + +#ifndef MINIZ_NO_DEFLATE_APIS + +/* mz_deflateInit() initializes a compressor with default options: */ +/* Parameters: */ +/* pStream must point to an initialized mz_stream struct. */ +/* level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. */ +/* level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio. */ +/* (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.) */ +/* Return values: */ +/* MZ_OK on success. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_PARAM_ERROR if the input parameters are bogus. */ +/* MZ_MEM_ERROR on out of memory. */ +MINIZ_EXPORT int mz_deflateInit(mz_streamp pStream, int level); + +/* mz_deflateInit2() is like mz_deflate(), except with more control: */ +/* Additional parameters: */ +/* method must be MZ_DEFLATED */ +/* window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer) */ +/* mem_level must be between [1, 9] (it's checked but ignored by miniz.c) */ +MINIZ_EXPORT int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); + +/* Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). */ +MINIZ_EXPORT int mz_deflateReset(mz_streamp pStream); + +/* mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. */ +/* Parameters: */ +/* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */ +/* flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. */ +/* Return values: */ +/* MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full). */ +/* MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_PARAM_ERROR if one of the parameters is invalid. */ +/* MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.) */ +MINIZ_EXPORT int mz_deflate(mz_streamp pStream, int flush); + +/* mz_deflateEnd() deinitializes a compressor: */ +/* Return values: */ +/* MZ_OK on success. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +MINIZ_EXPORT int mz_deflateEnd(mz_streamp pStream); + +/* mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH. */ +MINIZ_EXPORT mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); + +/* Single-call compression functions mz_compress() and mz_compress2(): */ +/* Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. */ +MINIZ_EXPORT int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); +MINIZ_EXPORT int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level); + +/* mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). */ +MINIZ_EXPORT mz_ulong mz_compressBound(mz_ulong source_len); + +#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ + +#ifndef MINIZ_NO_INFLATE_APIS + +/* Initializes a decompressor. */ +MINIZ_EXPORT int mz_inflateInit(mz_streamp pStream); + +/* mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer: */ +/* window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). */ +MINIZ_EXPORT int mz_inflateInit2(mz_streamp pStream, int window_bits); + +/* Quickly resets a compressor without having to reallocate anything. Same as calling mz_inflateEnd() followed by mz_inflateInit()/mz_inflateInit2(). */ +MINIZ_EXPORT int mz_inflateReset(mz_streamp pStream); + +/* Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible. */ +/* Parameters: */ +/* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */ +/* flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. */ +/* On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster). */ +/* MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data. */ +/* Return values: */ +/* MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full. */ +/* MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_DATA_ERROR if the deflate stream is invalid. */ +/* MZ_PARAM_ERROR if one of the parameters is invalid. */ +/* MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again */ +/* with more input data, or with more room in the output buffer (except when using single call decompression, described above). */ +MINIZ_EXPORT int mz_inflate(mz_streamp pStream, int flush); + +/* Deinitializes a decompressor. */ +MINIZ_EXPORT int mz_inflateEnd(mz_streamp pStream); + +/* Single-call decompression. */ +/* Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. */ +MINIZ_EXPORT int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); +MINIZ_EXPORT int mz_uncompress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong *pSource_len); +#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ + +/* Returns a string description of the specified error code, or NULL if the error code is invalid. */ +MINIZ_EXPORT const char *mz_error(int err); + +/* Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports. */ +/* Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. */ +#ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES +typedef unsigned char Byte; +typedef unsigned int uInt; +typedef mz_ulong uLong; +typedef Byte Bytef; +typedef uInt uIntf; +typedef char charf; +typedef int intf; +typedef void *voidpf; +typedef uLong uLongf; +typedef void *voidp; +typedef void *const voidpc; +#define Z_NULL 0 +#define Z_NO_FLUSH MZ_NO_FLUSH +#define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH +#define Z_SYNC_FLUSH MZ_SYNC_FLUSH +#define Z_FULL_FLUSH MZ_FULL_FLUSH +#define Z_FINISH MZ_FINISH +#define Z_BLOCK MZ_BLOCK +#define Z_OK MZ_OK +#define Z_STREAM_END MZ_STREAM_END +#define Z_NEED_DICT MZ_NEED_DICT +#define Z_ERRNO MZ_ERRNO +#define Z_STREAM_ERROR MZ_STREAM_ERROR +#define Z_DATA_ERROR MZ_DATA_ERROR +#define Z_MEM_ERROR MZ_MEM_ERROR +#define Z_BUF_ERROR MZ_BUF_ERROR +#define Z_VERSION_ERROR MZ_VERSION_ERROR +#define Z_PARAM_ERROR MZ_PARAM_ERROR +#define Z_NO_COMPRESSION MZ_NO_COMPRESSION +#define Z_BEST_SPEED MZ_BEST_SPEED +#define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION +#define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION +#define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY +#define Z_FILTERED MZ_FILTERED +#define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY +#define Z_RLE MZ_RLE +#define Z_FIXED MZ_FIXED +#define Z_DEFLATED MZ_DEFLATED +#define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS +#define alloc_func mz_alloc_func +#define free_func mz_free_func +#define internal_state mz_internal_state +#define z_stream mz_stream + +#ifndef MINIZ_NO_DEFLATE_APIS +#define deflateInit mz_deflateInit +#define deflateInit2 mz_deflateInit2 +#define deflateReset mz_deflateReset +#define deflate mz_deflate +#define deflateEnd mz_deflateEnd +#define deflateBound mz_deflateBound +#define compress mz_compress +#define compress2 mz_compress2 +#define compressBound mz_compressBound +#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ + +#ifndef MINIZ_NO_INFLATE_APIS +#define inflateInit mz_inflateInit +#define inflateInit2 mz_inflateInit2 +#define inflateReset mz_inflateReset +#define inflate mz_inflate +#define inflateEnd mz_inflateEnd +#define uncompress mz_uncompress +#define uncompress2 mz_uncompress2 +#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ + +#define crc32 mz_crc32 +#define adler32 mz_adler32 +#define MAX_WBITS 15 +#define MAX_MEM_LEVEL 9 +#define zError mz_error +#define ZLIB_VERSION MZ_VERSION +#define ZLIB_VERNUM MZ_VERNUM +#define ZLIB_VER_MAJOR MZ_VER_MAJOR +#define ZLIB_VER_MINOR MZ_VER_MINOR +#define ZLIB_VER_REVISION MZ_VER_REVISION +#define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION +#define zlibVersion mz_version +#define zlib_version mz_version() +#endif /* #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ + +#endif /* MINIZ_NO_ZLIB_APIS */ + +#ifdef __cplusplus +} +#endif + + + + + +#pragma once +#include +#include +#include +#include + + + +/* ------------------- Types and macros */ +typedef unsigned char mz_uint8; +typedef signed short mz_int16; +typedef unsigned short mz_uint16; +typedef unsigned int mz_uint32; +typedef unsigned int mz_uint; +typedef int64_t mz_int64; +typedef uint64_t mz_uint64; +typedef int mz_bool; + +#define MZ_FALSE (0) +#define MZ_TRUE (1) + +/* Works around MSVC's spammy "warning C4127: conditional expression is constant" message. */ +#ifdef _MSC_VER +#define MZ_MACRO_END while (0, 0) +#else +#define MZ_MACRO_END while (0) +#endif + +#ifdef MINIZ_NO_STDIO +#define MZ_FILE void * +#else +#include +#define MZ_FILE FILE +#endif /* #ifdef MINIZ_NO_STDIO */ + +#ifdef MINIZ_NO_TIME +typedef struct mz_dummy_time_t_tag +{ + mz_uint32 m_dummy1; + mz_uint32 m_dummy2; +} mz_dummy_time_t; +#define MZ_TIME_T mz_dummy_time_t +#else +#define MZ_TIME_T time_t +#endif + +#define MZ_ASSERT(x) assert(x) + +#ifdef MINIZ_NO_MALLOC +#define MZ_MALLOC(x) NULL +#define MZ_FREE(x) (void)x, ((void)0) +#define MZ_REALLOC(p, x) NULL +#else +#define MZ_MALLOC(x) malloc(x) +#define MZ_FREE(x) free(x) +#define MZ_REALLOC(p, x) realloc(p, x) +#endif + +#define MZ_MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define MZ_MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) +#define MZ_CLEAR_ARR(obj) memset((obj), 0, sizeof(obj)) +#define MZ_CLEAR_PTR(obj) memset((obj), 0, sizeof(*obj)) + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN +#define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) +#define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) +#else +#define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) +#define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) +#endif + +#define MZ_READ_LE64(p) (((mz_uint64)MZ_READ_LE32(p)) | (((mz_uint64)MZ_READ_LE32((const mz_uint8 *)(p) + sizeof(mz_uint32))) << 32U)) + +#ifdef _MSC_VER +#define MZ_FORCEINLINE __forceinline +#elif defined(__GNUC__) +#define MZ_FORCEINLINE __inline__ __attribute__((__always_inline__)) +#else +#define MZ_FORCEINLINE inline +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +extern MINIZ_EXPORT void *miniz_def_alloc_func(void *opaque, size_t items, size_t size); +extern MINIZ_EXPORT void miniz_def_free_func(void *opaque, void *address); +extern MINIZ_EXPORT void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size); + +#define MZ_UINT16_MAX (0xFFFFU) +#define MZ_UINT32_MAX (0xFFFFFFFFU) + +#ifdef __cplusplus +} +#endif + #pragma once + + +#ifndef MINIZ_NO_DEFLATE_APIS + +#ifdef __cplusplus +extern "C" { +#endif +/* ------------------- Low-level Compression API Definitions */ + +/* Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently). */ +#define TDEFL_LESS_MEMORY 0 + +/* tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search): */ +/* TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression). */ +enum +{ + TDEFL_HUFFMAN_ONLY = 0, + TDEFL_DEFAULT_MAX_PROBES = 128, + TDEFL_MAX_PROBES_MASK = 0xFFF +}; + +/* TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data. */ +/* TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers). */ +/* TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing. */ +/* TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory). */ +/* TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1) */ +/* TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. */ +/* TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. */ +/* TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. */ +/* The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK). */ +enum +{ + TDEFL_WRITE_ZLIB_HEADER = 0x01000, + TDEFL_COMPUTE_ADLER32 = 0x02000, + TDEFL_GREEDY_PARSING_FLAG = 0x04000, + TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, + TDEFL_RLE_MATCHES = 0x10000, + TDEFL_FILTER_MATCHES = 0x20000, + TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, + TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 +}; + +/* High level compression functions: */ +/* tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc(). */ +/* On entry: */ +/* pSrc_buf, src_buf_len: Pointer and size of source block to compress. */ +/* flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression. */ +/* On return: */ +/* Function returns a pointer to the compressed data, or NULL on failure. */ +/* *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. */ +/* The caller must free() the returned block when it's no longer needed. */ +MINIZ_EXPORT void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +/* tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. */ +/* Returns 0 on failure. */ +MINIZ_EXPORT size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + +/* Compresses an image to a compressed PNG file in memory. */ +/* On entry: */ +/* pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4. */ +/* The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory. */ +/* level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL */ +/* If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps). */ +/* On return: */ +/* Function returns a pointer to the compressed data, or NULL on failure. */ +/* *pLen_out will be set to the size of the PNG image file. */ +/* The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. */ +MINIZ_EXPORT void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip); +MINIZ_EXPORT void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out); + +/* Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. */ +typedef mz_bool (*tdefl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); + +/* tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. */ +MINIZ_EXPORT mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +enum +{ + TDEFL_MAX_HUFF_TABLES = 3, + TDEFL_MAX_HUFF_SYMBOLS_0 = 288, + TDEFL_MAX_HUFF_SYMBOLS_1 = 32, + TDEFL_MAX_HUFF_SYMBOLS_2 = 19, + TDEFL_LZ_DICT_SIZE = 32768, + TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, + TDEFL_MIN_MATCH_LEN = 3, + TDEFL_MAX_MATCH_LEN = 258 +}; + +/* TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes). */ +#if TDEFL_LESS_MEMORY +enum +{ + TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, + TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, + TDEFL_MAX_HUFF_SYMBOLS = 288, + TDEFL_LZ_HASH_BITS = 12, + TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, + TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, + TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS +}; +#else +enum +{ + TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, + TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, + TDEFL_MAX_HUFF_SYMBOLS = 288, + TDEFL_LZ_HASH_BITS = 15, + TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, + TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, + TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS +}; +#endif + +/* The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions. */ +typedef enum { + TDEFL_STATUS_BAD_PARAM = -2, + TDEFL_STATUS_PUT_BUF_FAILED = -1, + TDEFL_STATUS_OKAY = 0, + TDEFL_STATUS_DONE = 1 +} tdefl_status; + +/* Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums */ +typedef enum { + TDEFL_NO_FLUSH = 0, + TDEFL_SYNC_FLUSH = 2, + TDEFL_FULL_FLUSH = 3, + TDEFL_FINISH = 4 +} tdefl_flush; + +/* tdefl's compression state structure. */ +typedef struct +{ + tdefl_put_buf_func_ptr m_pPut_buf_func; + void *m_pPut_buf_user; + mz_uint m_flags, m_max_probes[2]; + int m_greedy_parsing; + mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; + mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; + mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer; + mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish; + tdefl_status m_prev_return_status; + const void *m_pIn_buf; + void *m_pOut_buf; + size_t *m_pIn_buf_size, *m_pOut_buf_size; + tdefl_flush m_flush; + const mz_uint8 *m_pSrc; + size_t m_src_buf_left, m_out_buf_ofs; + mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; + mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; + mz_uint16 m_next[TDEFL_LZ_DICT_SIZE]; + mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE]; + mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; +} tdefl_compressor; + +/* Initializes the compressor. */ +/* There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory. */ +/* pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression. */ +/* If pBut_buf_func is NULL the user should always call the tdefl_compress() API. */ +/* flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) */ +MINIZ_EXPORT tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +/* Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible. */ +MINIZ_EXPORT tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush); + +/* tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr. */ +/* tdefl_compress_buffer() always consumes the entire input buffer. */ +MINIZ_EXPORT tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); + +MINIZ_EXPORT tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); +MINIZ_EXPORT mz_uint32 tdefl_get_adler32(tdefl_compressor *d); + +/* Create tdefl_compress() flags given zlib-style compression parameters. */ +/* level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) */ +/* window_bits may be -15 (raw deflate) or 15 (zlib) */ +/* strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED */ +MINIZ_EXPORT mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); + +#ifndef MINIZ_NO_MALLOC +/* Allocate the tdefl_compressor structure in C so that */ +/* non-C language bindings to tdefl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ +MINIZ_EXPORT tdefl_compressor *tdefl_compressor_alloc(void); +MINIZ_EXPORT void tdefl_compressor_free(tdefl_compressor *pComp); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ + #pragma once + +/* ------------------- Low-level Decompression API Definitions */ + +#ifndef MINIZ_NO_INFLATE_APIS + +#ifdef __cplusplus +extern "C" { +#endif +/* Decompression flags used by tinfl_decompress(). */ +/* TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. */ +/* TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. */ +/* TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). */ +/* TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. */ +enum +{ + TINFL_FLAG_PARSE_ZLIB_HEADER = 1, + TINFL_FLAG_HAS_MORE_INPUT = 2, + TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, + TINFL_FLAG_COMPUTE_ADLER32 = 8 +}; + +/* High level decompression functions: */ +/* tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). */ +/* On entry: */ +/* pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. */ +/* On return: */ +/* Function returns a pointer to the decompressed data, or NULL on failure. */ +/* *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. */ +/* The caller must call mz_free() on the returned block when it's no longer needed. */ +MINIZ_EXPORT void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +/* tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. */ +/* Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. */ +#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) +MINIZ_EXPORT size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + +/* tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. */ +/* Returns 1 on success or 0 on failure. */ +typedef int (*tinfl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); +MINIZ_EXPORT int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +struct tinfl_decompressor_tag; +typedef struct tinfl_decompressor_tag tinfl_decompressor; + +#ifndef MINIZ_NO_MALLOC +/* Allocate the tinfl_decompressor structure in C so that */ +/* non-C language bindings to tinfl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ +MINIZ_EXPORT tinfl_decompressor *tinfl_decompressor_alloc(void); +MINIZ_EXPORT void tinfl_decompressor_free(tinfl_decompressor *pDecomp); +#endif + +/* Max size of LZ dictionary. */ +#define TINFL_LZ_DICT_SIZE 32768 + +/* Return status. */ +typedef enum { + /* This flags indicates the inflator needs 1 or more input bytes to make forward progress, but the caller is indicating that no more are available. The compressed data */ + /* is probably corrupted. If you call the inflator again with more bytes it'll try to continue processing the input but this is a BAD sign (either the data is corrupted or you called it incorrectly). */ + /* If you call it again with no input you'll just get TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS again. */ + TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS = -4, + + /* This flag indicates that one or more of the input parameters was obviously bogus. (You can try calling it again, but if you get this error the calling code is wrong.) */ + TINFL_STATUS_BAD_PARAM = -3, + + /* This flags indicate the inflator is finished but the adler32 check of the uncompressed data didn't match. If you call it again it'll return TINFL_STATUS_DONE. */ + TINFL_STATUS_ADLER32_MISMATCH = -2, + + /* This flags indicate the inflator has somehow failed (bad code, corrupted input, etc.). If you call it again without resetting via tinfl_init() it it'll just keep on returning the same status failure code. */ + TINFL_STATUS_FAILED = -1, + + /* Any status code less than TINFL_STATUS_DONE must indicate a failure. */ + + /* This flag indicates the inflator has returned every byte of uncompressed data that it can, has consumed every byte that it needed, has successfully reached the end of the deflate stream, and */ + /* if zlib headers and adler32 checking enabled that it has successfully checked the uncompressed data's adler32. If you call it again you'll just get TINFL_STATUS_DONE over and over again. */ + TINFL_STATUS_DONE = 0, + + /* This flag indicates the inflator MUST have more input data (even 1 byte) before it can make any more forward progress, or you need to clear the TINFL_FLAG_HAS_MORE_INPUT */ + /* flag on the next call if you don't have any more source data. If the source data was somehow corrupted it's also possible (but unlikely) for the inflator to keep on demanding input to */ + /* proceed, so be sure to properly set the TINFL_FLAG_HAS_MORE_INPUT flag. */ + TINFL_STATUS_NEEDS_MORE_INPUT = 1, + + /* This flag indicates the inflator definitely has 1 or more bytes of uncompressed data available, but it cannot write this data into the output buffer. */ + /* Note if the source compressed data was corrupted it's possible for the inflator to return a lot of uncompressed data to the caller. I've been assuming you know how much uncompressed data to expect */ + /* (either exact or worst case) and will stop calling the inflator and fail after receiving too much. In pure streaming scenarios where you have no idea how many bytes to expect this may not be possible */ + /* so I may need to add some code to address this. */ + TINFL_STATUS_HAS_MORE_OUTPUT = 2 +} tinfl_status; + +/* Initializes the decompressor to its initial state. */ +#define tinfl_init(r) \ + do \ + { \ + (r)->m_state = 0; \ + } \ + MZ_MACRO_END +#define tinfl_get_adler32(r) (r)->m_check_adler32 + +/* Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. */ +/* This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. */ +MINIZ_EXPORT tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags); + +/* Internal/private bits follow. */ +enum +{ + TINFL_MAX_HUFF_TABLES = 3, + TINFL_MAX_HUFF_SYMBOLS_0 = 288, + TINFL_MAX_HUFF_SYMBOLS_1 = 32, + TINFL_MAX_HUFF_SYMBOLS_2 = 19, + TINFL_FAST_LOOKUP_BITS = 10, + TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS +}; + +#if MINIZ_HAS_64BIT_REGISTERS +#define TINFL_USE_64BIT_BITBUF 1 +#else +#define TINFL_USE_64BIT_BITBUF 0 +#endif + +#if TINFL_USE_64BIT_BITBUF +typedef mz_uint64 tinfl_bit_buf_t; +#define TINFL_BITBUF_SIZE (64) +#else +typedef mz_uint32 tinfl_bit_buf_t; +#define TINFL_BITBUF_SIZE (32) +#endif + +struct tinfl_decompressor_tag +{ + mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; + tinfl_bit_buf_t m_bit_buf; + size_t m_dist_from_out_buf_start; + mz_int16 m_look_up[TINFL_MAX_HUFF_TABLES][TINFL_FAST_LOOKUP_SIZE]; + mz_int16 m_tree_0[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; + mz_int16 m_tree_1[TINFL_MAX_HUFF_SYMBOLS_1 * 2]; + mz_int16 m_tree_2[TINFL_MAX_HUFF_SYMBOLS_2 * 2]; + mz_uint8 m_code_size_0[TINFL_MAX_HUFF_SYMBOLS_0]; + mz_uint8 m_code_size_1[TINFL_MAX_HUFF_SYMBOLS_1]; + mz_uint8 m_code_size_2[TINFL_MAX_HUFF_SYMBOLS_2]; + mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; +}; + +#ifdef __cplusplus +} +#endif + +#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ + +#pragma once + + +/* ------------------- ZIP archive reading/writing */ + +#ifndef MINIZ_NO_ARCHIVE_APIS + +#ifdef __cplusplus +extern "C" { +#endif + +enum +{ + /* Note: These enums can be reduced as needed to save memory or stack space - they are pretty conservative. */ + MZ_ZIP_MAX_IO_BUF_SIZE = 64 * 1024, + MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 512, + MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 512 +}; + +typedef struct +{ + /* Central directory file index. */ + mz_uint32 m_file_index; + + /* Byte offset of this entry in the archive's central directory. Note we currently only support up to UINT_MAX or less bytes in the central dir. */ + mz_uint64 m_central_dir_ofs; + + /* These fields are copied directly from the zip's central dir. */ + mz_uint16 m_version_made_by; + mz_uint16 m_version_needed; + mz_uint16 m_bit_flag; + mz_uint16 m_method; + + /* CRC-32 of uncompressed data. */ + mz_uint32 m_crc32; + + /* File's compressed size. */ + mz_uint64 m_comp_size; + + /* File's uncompressed size. Note, I've seen some old archives where directory entries had 512 bytes for their uncompressed sizes, but when you try to unpack them you actually get 0 bytes. */ + mz_uint64 m_uncomp_size; + + /* Zip internal and external file attributes. */ + mz_uint16 m_internal_attr; + mz_uint32 m_external_attr; + + /* Entry's local header file offset in bytes. */ + mz_uint64 m_local_header_ofs; + + /* Size of comment in bytes. */ + mz_uint32 m_comment_size; + + /* MZ_TRUE if the entry appears to be a directory. */ + mz_bool m_is_directory; + + /* MZ_TRUE if the entry uses encryption/strong encryption (which miniz_zip doesn't support) */ + mz_bool m_is_encrypted; + + /* MZ_TRUE if the file is not encrypted, a patch file, and if it uses a compression method we support. */ + mz_bool m_is_supported; + + /* Filename. If string ends in '/' it's a subdirectory entry. */ + /* Guaranteed to be zero terminated, may be truncated to fit. */ + char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; + + /* Comment field. */ + /* Guaranteed to be zero terminated, may be truncated to fit. */ + char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; + +#ifdef MINIZ_NO_TIME + MZ_TIME_T m_padding; +#else + MZ_TIME_T m_time; +#endif +} mz_zip_archive_file_stat; + +typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); +typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n); +typedef mz_bool (*mz_file_needs_keepalive)(void *pOpaque); + +struct mz_zip_internal_state_tag; +typedef struct mz_zip_internal_state_tag mz_zip_internal_state; + +typedef enum { + MZ_ZIP_MODE_INVALID = 0, + MZ_ZIP_MODE_READING = 1, + MZ_ZIP_MODE_WRITING = 2, + MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 +} mz_zip_mode; + +typedef enum { + MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, + MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, + MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, + MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800, + MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG = 0x1000, /* if enabled, mz_zip_reader_locate_file() will be called on each file as its validated to ensure the func finds the file in the central dir (intended for testing) */ + MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY = 0x2000, /* validate the local headers, but don't decompress the entire file and check the crc32 */ + MZ_ZIP_FLAG_WRITE_ZIP64 = 0x4000, /* always use the zip64 file format, instead of the original zip file format with automatic switch to zip64. Use as flags parameter with mz_zip_writer_init*_v2 */ + MZ_ZIP_FLAG_WRITE_ALLOW_READING = 0x8000, + MZ_ZIP_FLAG_ASCII_FILENAME = 0x10000, + /*After adding a compressed file, seek back + to local file header and set the correct sizes*/ + MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE = 0x20000 +} mz_zip_flags; + +typedef enum { + MZ_ZIP_TYPE_INVALID = 0, + MZ_ZIP_TYPE_USER, + MZ_ZIP_TYPE_MEMORY, + MZ_ZIP_TYPE_HEAP, + MZ_ZIP_TYPE_FILE, + MZ_ZIP_TYPE_CFILE, + MZ_ZIP_TOTAL_TYPES +} mz_zip_type; + +/* miniz error codes. Be sure to update mz_zip_get_error_string() if you add or modify this enum. */ +typedef enum { + MZ_ZIP_NO_ERROR = 0, + MZ_ZIP_UNDEFINED_ERROR, + MZ_ZIP_TOO_MANY_FILES, + MZ_ZIP_FILE_TOO_LARGE, + MZ_ZIP_UNSUPPORTED_METHOD, + MZ_ZIP_UNSUPPORTED_ENCRYPTION, + MZ_ZIP_UNSUPPORTED_FEATURE, + MZ_ZIP_FAILED_FINDING_CENTRAL_DIR, + MZ_ZIP_NOT_AN_ARCHIVE, + MZ_ZIP_INVALID_HEADER_OR_CORRUPTED, + MZ_ZIP_UNSUPPORTED_MULTIDISK, + MZ_ZIP_DECOMPRESSION_FAILED, + MZ_ZIP_COMPRESSION_FAILED, + MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE, + MZ_ZIP_CRC_CHECK_FAILED, + MZ_ZIP_UNSUPPORTED_CDIR_SIZE, + MZ_ZIP_ALLOC_FAILED, + MZ_ZIP_FILE_OPEN_FAILED, + MZ_ZIP_FILE_CREATE_FAILED, + MZ_ZIP_FILE_WRITE_FAILED, + MZ_ZIP_FILE_READ_FAILED, + MZ_ZIP_FILE_CLOSE_FAILED, + MZ_ZIP_FILE_SEEK_FAILED, + MZ_ZIP_FILE_STAT_FAILED, + MZ_ZIP_INVALID_PARAMETER, + MZ_ZIP_INVALID_FILENAME, + MZ_ZIP_BUF_TOO_SMALL, + MZ_ZIP_INTERNAL_ERROR, + MZ_ZIP_FILE_NOT_FOUND, + MZ_ZIP_ARCHIVE_TOO_LARGE, + MZ_ZIP_VALIDATION_FAILED, + MZ_ZIP_WRITE_CALLBACK_FAILED, + MZ_ZIP_TOTAL_ERRORS +} mz_zip_error; + +typedef struct +{ + mz_uint64 m_archive_size; + mz_uint64 m_central_directory_file_ofs; + + /* We only support up to UINT32_MAX files in zip64 mode. */ + mz_uint32 m_total_files; + mz_zip_mode m_zip_mode; + mz_zip_type m_zip_type; + mz_zip_error m_last_error; + + mz_uint64 m_file_offset_alignment; + + mz_alloc_func m_pAlloc; + mz_free_func m_pFree; + mz_realloc_func m_pRealloc; + void *m_pAlloc_opaque; + + mz_file_read_func m_pRead; + mz_file_write_func m_pWrite; + mz_file_needs_keepalive m_pNeeds_keepalive; + void *m_pIO_opaque; + + mz_zip_internal_state *m_pState; + +} mz_zip_archive; + +typedef struct +{ + mz_zip_archive *pZip; + mz_uint flags; + + int status; + + mz_uint64 read_buf_size, read_buf_ofs, read_buf_avail, comp_remaining, out_buf_ofs, cur_file_ofs; + mz_zip_archive_file_stat file_stat; + void *pRead_buf; + void *pWrite_buf; + + size_t out_blk_remain; + + tinfl_decompressor inflator; + +#ifdef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + mz_uint padding; +#else + mz_uint file_crc32; +#endif + +} mz_zip_reader_extract_iter_state; + +/* -------- ZIP reading */ + +/* Inits a ZIP archive reader. */ +/* These functions read and validate the archive's central directory. */ +MINIZ_EXPORT mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags); + +MINIZ_EXPORT mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags); + +#ifndef MINIZ_NO_STDIO +/* Read a archive from a disk file. */ +/* file_start_ofs is the file offset where the archive actually begins, or 0. */ +/* actual_archive_size is the true total size of the archive, which may be smaller than the file's actual size on disk. If zero the entire file is treated as the archive. */ +MINIZ_EXPORT mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags); +MINIZ_EXPORT mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size); + +/* Read an archive from an already opened FILE, beginning at the current file position. */ +/* The archive is assumed to be archive_size bytes long. If archive_size is 0, then the entire rest of the file is assumed to contain the archive. */ +/* The FILE will NOT be closed when mz_zip_reader_end() is called. */ +MINIZ_EXPORT mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags); +#endif + +/* Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used. */ +MINIZ_EXPORT mz_bool mz_zip_reader_end(mz_zip_archive *pZip); + +/* -------- ZIP reading or writing */ + +/* Clears a mz_zip_archive struct to all zeros. */ +/* Important: This must be done before passing the struct to any mz_zip functions. */ +MINIZ_EXPORT void mz_zip_zero_struct(mz_zip_archive *pZip); + +MINIZ_EXPORT mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip); +MINIZ_EXPORT mz_zip_type mz_zip_get_type(mz_zip_archive *pZip); + +/* Returns the total number of files in the archive. */ +MINIZ_EXPORT mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); + +MINIZ_EXPORT mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip); +MINIZ_EXPORT mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip); +MINIZ_EXPORT MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip); + +/* Reads n bytes of raw archive data, starting at file offset file_ofs, to pBuf. */ +MINIZ_EXPORT size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n); + +/* All mz_zip funcs set the m_last_error field in the mz_zip_archive struct. These functions retrieve/manipulate this field. */ +/* Note that the m_last_error functionality is not thread safe. */ +MINIZ_EXPORT mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num); +MINIZ_EXPORT mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip); +MINIZ_EXPORT mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip); +MINIZ_EXPORT mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip); +MINIZ_EXPORT const char *mz_zip_get_error_string(mz_zip_error mz_err); + +/* MZ_TRUE if the archive file entry is a directory entry. */ +MINIZ_EXPORT mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index); + +/* MZ_TRUE if the file is encrypted/strong encrypted. */ +MINIZ_EXPORT mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index); + +/* MZ_TRUE if the compression method is supported, and the file is not encrypted, and the file is not a compressed patch file. */ +MINIZ_EXPORT mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index); + +/* Retrieves the filename of an archive file entry. */ +/* Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename. */ +MINIZ_EXPORT mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size); + +/* Attempts to locates a file in the archive's central directory. */ +/* Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH */ +/* Returns -1 if the file cannot be found. */ +MINIZ_EXPORT int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *file_index); + +/* Returns detailed information about an archive file entry. */ +MINIZ_EXPORT mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat); + +/* MZ_TRUE if the file is in zip64 format. */ +/* A file is considered zip64 if it contained a zip64 end of central directory marker, or if it contained any zip64 extended file information fields in the central directory. */ +MINIZ_EXPORT mz_bool mz_zip_is_zip64(mz_zip_archive *pZip); + +/* Returns the total central directory size in bytes. */ +/* The current max supported size is <= MZ_UINT32_MAX. */ +MINIZ_EXPORT size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip); + +/* Extracts a archive file to a memory buffer using no memory allocation. */ +/* There must be at least enough room on the stack to store the inflator's state (~34KB or so). */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); + +/* Extracts a archive file to a memory buffer. */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags); + +/* Extracts a archive file to a dynamically allocated heap buffer. */ +/* The memory will be allocated via the mz_zip_archive's alloc/realloc functions. */ +/* Returns NULL and sets the last error on failure. */ +MINIZ_EXPORT void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags); +MINIZ_EXPORT void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags); + +/* Extracts a archive file using a callback function to output the file's data. */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); + +/* Extract a file iteratively */ +MINIZ_EXPORT mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); +MINIZ_EXPORT mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); +MINIZ_EXPORT size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState); + +#ifndef MINIZ_NO_STDIO +/* Extracts a archive file to a disk file and sets its last accessed and modified times. */ +/* This function only extracts files, not archive directory records. */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags); + +/* Extracts a archive file starting at the current position in the destination FILE stream. */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *File, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags); +#endif + +#if 0 +/* TODO */ + typedef void *mz_zip_streaming_extract_state_ptr; + mz_zip_streaming_extract_state_ptr mz_zip_streaming_extract_begin(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); + mz_uint64 mz_zip_streaming_extract_get_size(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); + mz_uint64 mz_zip_streaming_extract_get_cur_ofs(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); + mz_bool mz_zip_streaming_extract_seek(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, mz_uint64 new_ofs); + size_t mz_zip_streaming_extract_read(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, void *pBuf, size_t buf_size); + mz_bool mz_zip_streaming_extract_end(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); +#endif + +/* This function compares the archive's local headers, the optional local zip64 extended information block, and the optional descriptor following the compressed data vs. the data in the central directory. */ +/* It also validates that each file can be successfully uncompressed unless the MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY is specified. */ +MINIZ_EXPORT mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); + +/* Validates an entire archive by calling mz_zip_validate_file() on each file. */ +MINIZ_EXPORT mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags); + +/* Misc utils/helpers, valid for ZIP reading or writing */ +MINIZ_EXPORT mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr); +#ifndef MINIZ_NO_STDIO +MINIZ_EXPORT mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr); +#endif + +/* Universal end function - calls either mz_zip_reader_end() or mz_zip_writer_end(). */ +MINIZ_EXPORT mz_bool mz_zip_end(mz_zip_archive *pZip); + +/* -------- ZIP writing */ + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +/* Inits a ZIP archive writer. */ +/*Set pZip->m_pWrite (and pZip->m_pIO_opaque) before calling mz_zip_writer_init or mz_zip_writer_init_v2*/ +/*The output is streamable, i.e. file_ofs in mz_file_write_func always increases only by n*/ +MINIZ_EXPORT mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); +MINIZ_EXPORT mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags); + +MINIZ_EXPORT mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size); +MINIZ_EXPORT mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags); + +#ifndef MINIZ_NO_STDIO +MINIZ_EXPORT mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning); +MINIZ_EXPORT mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags); +#endif + +/* Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive. */ +/* For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called. */ +/* For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it). */ +/* Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL. */ +/* Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before */ +/* the archive is finalized the file's central directory will be hosed. */ +MINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename); +MINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); + +/* Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive. */ +/* To add a directory entry, call this method with an archive name ending in a forwardslash with an empty buffer. */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags); + +/* Like mz_zip_writer_add_mem(), except you can specify a file comment field, and optionally supply the function with already compressed data. */ +/* uncomp_size/uncomp_crc32 are only used if the MZ_ZIP_FLAG_COMPRESSED_DATA flag is specified. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint64 uncomp_size, mz_uint32 uncomp_crc32); + +MINIZ_EXPORT mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, const char *user_extra_data_local, mz_uint user_extra_data_local_len, + const char *user_extra_data_central, mz_uint user_extra_data_central_len); + +/* Adds the contents of a file to an archive. This function also records the disk file's modified time into the archive. */ +/* File data is supplied via a read callback function. User mz_zip_writer_add_(c)file to add a file directly.*/ +MINIZ_EXPORT mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void* callback_opaque, mz_uint64 max_size, + const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len, + const char *user_extra_data_central, mz_uint user_extra_data_central_len); + + +#ifndef MINIZ_NO_STDIO +/* Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive. */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); + +/* Like mz_zip_writer_add_file(), except the file data is read from the specified FILE stream. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 max_size, + const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len, + const char *user_extra_data_central, mz_uint user_extra_data_central_len); +#endif + +/* Adds a file to an archive by fully cloning the data from another archive. */ +/* This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data (it may add or modify the zip64 local header extra data field), and the optional descriptor following the compressed data. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index); + +/* Finalizes the archive by writing the central directory records followed by the end of central directory record. */ +/* After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end(). */ +/* An archive must be manually finalized by calling this function for it to be valid. */ +MINIZ_EXPORT mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); + +/* Finalizes a heap archive, returning a pointer to the heap block and its size. */ +/* The heap block will be allocated using the mz_zip_archive's alloc/realloc callbacks. */ +MINIZ_EXPORT mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize); + +/* Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used. */ +/* Note for the archive to be valid, it *must* have been finalized before ending (this function will not do it for you). */ +MINIZ_EXPORT mz_bool mz_zip_writer_end(mz_zip_archive *pZip); + +/* -------- Misc. high-level helper functions: */ + +/* mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive. */ +/* Note this is NOT a fully safe operation. If it crashes or dies in some way your archive can be left in a screwed up state (without a central directory). */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +/* TODO: Perhaps add an option to leave the existing central dir in place in case the add dies? We could then truncate the file (so the old central dir would be at the end) if something goes wrong. */ +MINIZ_EXPORT mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); +MINIZ_EXPORT mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr); + +#ifndef MINIZ_NO_STDIO +/* Reads a single file from an archive into a heap block. */ +/* If pComment is not NULL, only the file with the specified comment will be extracted. */ +/* Returns NULL on failure. */ +MINIZ_EXPORT void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags); +MINIZ_EXPORT void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr); +#endif + +#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ + +#ifdef __cplusplus +} +#endif + +#endif /* MINIZ_NO_ARCHIVE_APIS */ From 02789c7c47c13ec930109fc670edf22cf08c1a59 Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Tue, 20 Dec 2022 19:24:45 -0800 Subject: [PATCH 07/79] Make and document building faster --- CONTRIBUTING.md | 2 ++ Source/UnrealLibretro/UnrealLibretro.Build.cs | 1 + 2 files changed, 3 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 61f7d83a..25dc4429 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,6 +12,8 @@ cv2pdb.exe {core_name}.exe The pdb is produced in the same directory as the DLL. Copy both of them over to MyCores. Once that's done Visual Studio should automagically load the pdb and will ask you to point to the source files. And now you might get more insight into whats going wrong... hopefully. ## Random notes +To get faster iteration times use [Live Coding](https://docs.unrealengine.com/5.0/en-US/using-live-coding-to-recompile-unreal-engine-applications-at-runtime/) and add `bUseUnityBuild = false;` to your `[Project Name].Target.cs` and `[Project Name]Editor.Target.cs` build files. Mind that this might slow down the initial build the recompiles should be much faster. + Q: Visual Studio loads the source files for the wrong core when debugging A: Remove other pdbs AND source files and clean the build and rerun diff --git a/Source/UnrealLibretro/UnrealLibretro.Build.cs b/Source/UnrealLibretro/UnrealLibretro.Build.cs index a96018d7..46b3b854 100644 --- a/Source/UnrealLibretro/UnrealLibretro.Build.cs +++ b/Source/UnrealLibretro/UnrealLibretro.Build.cs @@ -8,6 +8,7 @@ public class UnrealLibretro : ModuleRules public UnrealLibretro(ReadOnlyTargetRules Target) : base(Target) { PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + bEnforceIWYU = true; PrivateIncludePaths.Add("UnrealLibretro/Public/libretro/include"); From e19b1bd96656af889a3f8831fa9bb0df92a0f02b Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Mon, 20 Feb 2023 14:59:17 -0800 Subject: [PATCH 08/79] Add partial implementation of Editor Module --- .gitattributes | 22 +- .gitignore | 1 - Config/DefaultUnrealLibretro.ini | 6 +- Config/Input.ini | 64 +- .../LibretroBlueprintFunctionLibrary.cpp | 8 +- .../Private/LibretroCoreInstance.cpp | 109 ++- Source/UnrealLibretro/Private/sdlarch.cpp | 68 +- Source/UnrealLibretro/Private/sdlarch.h | 4 +- .../Public/LibretroBlueprintFunctionLibrary.h | 5 +- .../Public/LibretroCoreInstance.h | 93 +- .../Public/LibretroGrabComponent.h | 255 ++++++ .../Public/LibretroInputDefinitions.h | 113 ++- .../Public/LibretroVRInteractionInterface.h | 38 + Source/UnrealLibretro/Public/LibretroVRPawn.h | 442 ++++++++++ Source/UnrealLibretro/Public/UnrealLibretro.h | 38 +- Source/UnrealLibretro/UnrealLibretro.Build.cs | 3 +- .../Private/LibretroCoreInstanceDetails.cpp | 797 ++++++++++++++++++ .../Private/LibretroCoreInstanceDetails.h | 100 +++ .../Private/UnrealLibretroEditor.cpp | 109 +++ .../Public/UnrealLibretroEditor.h | 29 + .../UnrealLibretroEditor.Build.cs | 37 + UnrealLibretro.uplugin | 8 +- 22 files changed, 2245 insertions(+), 104 deletions(-) create mode 100644 Source/UnrealLibretro/Public/LibretroGrabComponent.h create mode 100644 Source/UnrealLibretro/Public/LibretroVRInteractionInterface.h create mode 100644 Source/UnrealLibretro/Public/LibretroVRPawn.h create mode 100644 Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.cpp create mode 100644 Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.h create mode 100644 Source/UnrealLibretroEditor/Private/UnrealLibretroEditor.cpp create mode 100644 Source/UnrealLibretroEditor/Public/UnrealLibretroEditor.h create mode 100644 Source/UnrealLibretroEditor/UnrealLibretroEditor.Build.cs diff --git a/.gitattributes b/.gitattributes index dcc8faa7..665e862d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,11 +1,13 @@ -# This is quite implicit in what its doing. But its saying for all files (the * wildcard) if it thinks its a text file. It will try to automatically manage their line endings. Specifically it should make committed files have LF line endings most of the time. -* text=auto - -*.png binary - -*.sln eol=crlf -*.vcxproj eol=crlf -*.vcxproj.filters eol=crlf -*.cs eol=crlf - +# This is quite implicit in what its doing. But its saying for all files (the * wildcard) if it thinks its a text file. It will try to automatically manage their line endings. Specifically it should make committed files have LF line endings most of the time. +* text=auto + +*.png binary +*.uasset binary +*.umap binary + +*.sln eol=crlf +*.vcxproj eol=crlf +*.vcxproj.filters eol=crlf +*.cs eol=crlf + *.sh eol=lf \ No newline at end of file diff --git a/.gitignore b/.gitignore index e2b54911..271c980f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ ###### PROJECT SPECIFIC FILTERS ###### -/Content /MyROMs /MyGames /Saves diff --git a/Config/DefaultUnrealLibretro.ini b/Config/DefaultUnrealLibretro.ini index ed87394e..e1335168 100644 --- a/Config/DefaultUnrealLibretro.ini +++ b/Config/DefaultUnrealLibretro.ini @@ -1,3 +1,7 @@ [/Script/UnrealLibretro.LibretroSettings] CoreSaveDirectory=Saves/Core/ -CoreSystemDirectory=System/ \ No newline at end of file +CoreSystemDirectory=System/ + +;[/Script/Engine.PhysicsSettings] +;; Arcade guns won't work without this I have a text warning over the arcade gun actor for this, but I'm leaving this here as a reminder +;bSupportUVFromHitResults=True \ No newline at end of file diff --git a/Config/Input.ini b/Config/Input.ini index 85c113b0..4e7df091 100644 --- a/Config/Input.ini +++ b/Config/Input.ini @@ -19,4 +19,66 @@ +AxisMappings=(AxisName="LibretroLookUp",Scale=-1.000000,Key=Gamepad_RightY) +AxisMappings=(AxisName="LibretroLookRight",Scale=1.000000,Key=Gamepad_RightX) +AxisMappings=(AxisName="LibretroMoveForward",Scale=1.000000,Key=Gamepad_LeftY) -+AxisMappings=(AxisName="LibretroMoveRight",Scale=1.000000,Key=Gamepad_LeftX) \ No newline at end of file ++AxisMappings=(AxisName="LibretroMoveRight",Scale=1.000000,Key=Gamepad_LeftX) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Virtual Reality Bindings ;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ++AxisMappings=(AxisName="LibretroMovementAxisLeft_X",Scale=1.000000,Key=MixedReality_Left_Thumbstick_X) ++AxisMappings=(AxisName="LibretroMovementAxisLeft_X",Scale=1.000000,Key=OculusTouch_Left_Thumbstick_X) ++AxisMappings=(AxisName="LibretroMovementAxisLeft_X",Scale=1.000000,Key=ValveIndex_Left_Thumbstick_X) ++AxisMappings=(AxisName="LibretroMovementAxisLeft_X",Scale=1.000000,Key=Vive_Left_Trackpad_X) ++AxisMappings=(AxisName="LibretroMovementAxisLeft_Y",Scale=1.000000,Key=MixedReality_Left_Thumbstick_Y) ++AxisMappings=(AxisName="LibretroMovementAxisLeft_Y",Scale=1.000000,Key=OculusTouch_Left_Thumbstick_Y) ++AxisMappings=(AxisName="LibretroMovementAxisLeft_Y",Scale=1.000000,Key=ValveIndex_Left_Thumbstick_Y) ++AxisMappings=(AxisName="LibretroMovementAxisLeft_Y",Scale=1.000000,Key=Vive_Left_Trackpad_Y) ++AxisMappings=(AxisName="LibretroMovementAxisRight_X",Scale=1.000000,Key=MixedReality_Right_Thumbstick_X) ++AxisMappings=(AxisName="LibretroMovementAxisRight_X",Scale=1.000000,Key=OculusTouch_Right_Thumbstick_X) ++AxisMappings=(AxisName="LibretroMovementAxisRight_X",Scale=1.000000,Key=ValveIndex_Right_Thumbstick_X) ++AxisMappings=(AxisName="LibretroMovementAxisRight_X",Scale=1.000000,Key=Vive_Right_Trackpad_X) ++AxisMappings=(AxisName="LibretroMovementAxisRight_Y",Scale=1.000000,Key=MixedReality_Right_Thumbstick_Y) ++AxisMappings=(AxisName="LibretroMovementAxisRight_Y",Scale=1.000000,Key=OculusTouch_Right_A_Click) ++AxisMappings=(AxisName="LibretroMovementAxisRight_Y",Scale=1.000000,Key=OculusTouch_Right_Thumbstick_Y) ++AxisMappings=(AxisName="LibretroMovementAxisRight_Y",Scale=1.000000,Key=ValveIndex_Right_Thumbstick_Y) ++AxisMappings=(AxisName="LibretroMovementAxisRight_Y",Scale=1.000000,Key=Vive_Right_Trackpad_Y) ++AxisMappings=(AxisName="LibretroGrabAxisLeft",Scale=1.000000,Key=MixedReality_Left_Grip_Click) ++AxisMappings=(AxisName="LibretroGrabAxisLeft",Scale=1.000000,Key=OculusTouch_Left_Grip_Axis) ++AxisMappings=(AxisName="LibretroGrabAxisLeft",Scale=1.000000,Key=ValveIndex_Left_Grip_Axis) ++AxisMappings=(AxisName="LibretroGrabAxisLeft",Scale=1.000000,Key=Vive_Left_Grip_Click) ++AxisMappings=(AxisName="LibretroGrabAxisRight",Scale=1.000000,Key=MixedReality_Right_Grip_Click) ++AxisMappings=(AxisName="LibretroGrabAxisRight",Scale=1.000000,Key=OculusTouch_Right_Grip_Axis) ++AxisMappings=(AxisName="LibretroGrabAxisRight",Scale=1.000000,Key=ValveIndex_Right_Grip_Axis) ++AxisMappings=(AxisName="LibretroGrabAxisRight",Scale=1.000000,Key=Vive_Right_Grip_Click) ++AxisMappings=(AxisName="LibretroTriggerAxisLeft",Scale=1.000000,Key=MixedReality_Left_Trigger_Axis) ++AxisMappings=(AxisName="LibretroTriggerAxisLeft",Scale=1.000000,Key=OculusTouch_Left_Trigger_Axis) ++AxisMappings=(AxisName="LibretroTriggerAxisLeft",Scale=1.000000,Key=ValveIndex_Left_Trigger_Axis) ++AxisMappings=(AxisName="LibretroTriggerAxisLeft",Scale=1.000000,Key=Vive_Left_Trigger_Axis) ++AxisMappings=(AxisName="LibretroTriggerAxisRight",Scale=1.000000,Key=MixedReality_Right_Trigger_Axis) ++AxisMappings=(AxisName="LibretroTriggerAxisRight",Scale=1.000000,Key=OculusTouch_Right_Trigger_Axis) ++AxisMappings=(AxisName="LibretroTriggerAxisRight",Scale=1.000000,Key=ValveIndex_Right_Trigger_Axis) ++AxisMappings=(AxisName="LibretroTriggerAxisRight",Scale=1.000000,Key=Vive_Right_Trigger_Axis) ++ActionMappings=(ActionName="LibretroTriggerLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=MixedReality_Left_Trigger_Click) ++ActionMappings=(ActionName="LibretroTriggerLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=OculusTouch_Left_Trigger_Click) ++ActionMappings=(ActionName="LibretroTriggerLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=ValveIndex_Left_Trigger_Click) ++ActionMappings=(ActionName="LibretroTriggerLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Vive_Left_Trigger_Click) ++ActionMappings=(ActionName="LibretroTriggerRight",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=MixedReality_Right_Trigger_Click) ++ActionMappings=(ActionName="LibretroTriggerRight",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=OculusTouch_Right_Trigger_Click) ++ActionMappings=(ActionName="LibretroTriggerRight",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=ValveIndex_Right_Trigger_Click) ++ActionMappings=(ActionName="LibretroTriggerRight",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Vive_Right_Trigger_Click) ++ActionMappings=(ActionName="LibretroGrabLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=MixedReality_Left_Grip_Click) ++ActionMappings=(ActionName="LibretroGrabLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=OculusTouch_Left_Grip_Click) ++ActionMappings=(ActionName="LibretroGrabLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=ValveIndex_Left_Grip_Axis) ++ActionMappings=(ActionName="LibretroGrabLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Vive_Left_Grip_Click) ++ActionMappings=(ActionName="LibretroGrabRight",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=MixedReality_Right_Grip_Click) ++ActionMappings=(ActionName="LibretroGrabRight",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=OculusTouch_Right_Grip_Click) ++ActionMappings=(ActionName="LibretroGrabRight",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=ValveIndex_Right_Grip_Axis) ++ActionMappings=(ActionName="LibretroGrabRight",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Vive_Right_Grip_Click) ++ActionMappings=(ActionName="LibretroMenuToggleLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=MixedReality_Left_Menu_Click) ++ActionMappings=(ActionName="LibretroMenuToggleLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=OculusTouch_Left_Menu_Click) ++ActionMappings=(ActionName="LibretroMenuToggleLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=OculusTouch_Left_Thumbstick_Click) ++ActionMappings=(ActionName="LibretroMenuToggleLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=ValveIndex_Left_Thumbstick_Click) ++ActionMappings=(ActionName="LibretroMenuToggleLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Vive_Left_Menu_Click) ++ActionMappings=(ActionName="LibretroMenuToggleRight",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=MixedReality_Right_Menu_Click) ++ActionMappings=(ActionName="LibretroMenuToggleRight",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=OculusTouch_Right_Thumbstick_Click) ++ActionMappings=(ActionName="LibretroMenuToggleRight",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=ValveIndex_Right_Thumbstick_Click) ++ActionMappings=(ActionName="LibretroMenuToggleRight",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Vive_Right_Menu_Click) \ No newline at end of file diff --git a/Source/UnrealLibretro/Private/LibretroBlueprintFunctionLibrary.cpp b/Source/UnrealLibretro/Private/LibretroBlueprintFunctionLibrary.cpp index a77d4c4a..44c6f416 100644 --- a/Source/UnrealLibretro/Private/LibretroBlueprintFunctionLibrary.cpp +++ b/Source/UnrealLibretro/Private/LibretroBlueprintFunctionLibrary.cpp @@ -4,6 +4,7 @@ #include "Engine/Engine.h" #include "Camera/CameraComponent.h" +#include "PhysicsEngine/PhysicsSettings.h" AActor* ULibretroBlueprintFunctionLibrary::LookingAtActor(UCameraComponent* CameraComponent, EBranchNames& Branch) { @@ -27,9 +28,14 @@ UActorComponent* ULibretroBlueprintFunctionLibrary::HasComponent(AActor* Actor, return Component; } -TMap ULibretroBlueprintFunctionLibrary::CombineInputMaps(const TMap& InMap1, const TMap& InMap2) +TMap ULibretroBlueprintFunctionLibrary::CombineInputMaps(const TMap& InMap1, const TMap& InMap2) { auto OutMap(InMap1); OutMap.Append(InMap2); return OutMap; +} + +bool ULibretroBlueprintFunctionLibrary::IsSupportUVFromHitResultsEnabledInConfig() +{ + return UPhysicsSettings::Get()->bSupportUVFromHitResults; } \ No newline at end of file diff --git a/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp b/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp index ed78bf2f..ef75bc0c 100644 --- a/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp +++ b/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp @@ -22,7 +22,7 @@ ULibretroCoreInstance::ULibretroCoreInstance() void ULibretroCoreInstance::ConnectController(APlayerController* PlayerController, int Port, - TMap ControllerBindings, + TMap ControllerBindings, FOnControllerDisconnected OnControllerDisconnected, bool ForwardAllKeyboardInputToCoreIfPossible) { @@ -47,7 +47,7 @@ void ULibretroCoreInstance::DisconnectController(int Port) { CoreInstance.GetValue()->EnqueueTask([&InputStatePort = this->CoreInstance.GetValue()->InputState[Port]](libretro_api_t& libretro_api) { - InputStatePort = { { 0 } }; + InputStatePort = FLibretroInputState(); if (libretro_api.keyboard_event) { @@ -95,8 +95,17 @@ void ULibretroCoreInstance::Launch() RenderTarget->Filter = TF_Nearest; // @todo remove this - this->CoreInstance = LibretroContext::Launch(_CorePath, _RomPath, RenderTarget, static_cast(AudioBuffer), - [weakThis = MakeWeakObjectPtr(this), SRAMPath = FUnrealLibretroModule::ResolveSRAMPath(_RomPath, SRAMPath)] + // @todo Figure out if this is actually a problem then fix it maybe + // Sometimes it can be practical to make the UV's oversized so we don't want it to wrap + // however this might make debugging a little more confusing if you have a UV transformation issue because the texture might be rendered + // as completely black and not reflected or tiled or something <-- I actually immediately ran into this issue because the logic here was + // broken because I think all my UV's are negative + //RenderTarget->AddressX = TA_Clamp; + //RenderTarget->AddressY = TA_Clamp; + + this->CoreInstance = LibretroContext::Launch(this, _CorePath, _RomPath, RenderTarget, static_cast(AudioBuffer), + [weakThis = MakeWeakObjectPtr(this), SRAMPath = FUnrealLibretroModule::ResolveSRAMPath(_RomPath, SRAMPath), + ControllersSetOnLaunch = this->ControllersSetOnLaunch] (LibretroContext *_CoreInstance, libretro_api_t &libretro_api) { // Core has loaded @@ -206,7 +215,8 @@ void ULibretroCoreInstance::Pause(bool ShouldPause) Paused = ShouldPause; } -void ULibretroCoreInstance::Shutdown() { +void ULibretroCoreInstance::Shutdown() +{ NOT_LAUNCHED_GUARD @@ -267,12 +277,74 @@ void ULibretroCoreInstance::BeginPlay() }*/ } -#include +void ULibretroCoreInstance::SetInputDigital(int Port, bool Pressed, ERetroDeviceID Input) +{ + NOT_LAUNCHED_GUARD + + if (Controller[Port] != nullptr) +{ + UE_LOG(Libretro, Warning, TEXT("This function '%s' is incompatible with ConnectController this call will have no effect"), __func__); + } + + CoreInstance.GetValue()->EnqueueTask([=, CoreInstance = CoreInstance.GetValue()](auto) + { + CoreInstance->InputState[Port][Input] = Pressed; + }); +} -template -static constexpr auto to_integral(E e) -> typename std::underlying_type::type +void ULibretroCoreInstance::SetInputAnalog(int Port, int _16BitSignedInteger, ERetroDeviceID Input) { - return static_cast::type>(e); + NOT_LAUNCHED_GUARD + + if (Controller[Port] != nullptr) + { + UE_LOG(Libretro, Warning, TEXT("This function '%s' is incompatible with ConnectController this call will have no effect"), __func__); + } + + CoreInstance.GetValue()->EnqueueTask([=, CoreInstance = CoreInstance.GetValue()](auto) + { + CoreInstance->InputState[Port][Input] = _16BitSignedInteger; + }); +} + +void ULibretroCoreInstance::GunPullTrigger(int Port, FVector2D XY) +{ + NOT_LAUNCHED_GUARD + + CoreInstance.GetValue()->EnqueueTask([=, CoreInstance = CoreInstance.GetValue()](auto) + { + // I think mame wants both these to be set + CoreInstance->InputState[Port][ERetroDeviceID::PointerPressed] = true; + CoreInstance->InputState[Port][ERetroDeviceID::PointerCount] = 1; + + // Translate into libretro coordinate system [0.0, 1.0] -> [-0x7fff, 0x7fff] which is just a shift and scale the rounding is pedantic + CoreInstance->InputState[Port][ERetroDeviceID::PointerX] = FMath::RoundHalfToEven(0x7FFF * 2.f * (XY[0] - 0.5f)); + CoreInstance->InputState[Port][ERetroDeviceID::PointerY] = FMath::RoundHalfToEven(0x7FFF * 2.f * (XY[1] - 0.5f)); + }); +} + +void ULibretroCoreInstance::GunPullTriggerOffscreen(int Port) +{ + NOT_LAUNCHED_GUARD + + CoreInstance.GetValue()->EnqueueTask([=](auto) + { + CoreInstance.GetValue()->InputState[Port][ERetroDeviceID::LightgunIsOffscreen] = true; + CoreInstance.GetValue()->InputState[Port][ERetroDeviceID::PointerPressed] = false; + CoreInstance.GetValue()->InputState[Port][ERetroDeviceID::PointerCount] = 1; + }); +} + +void ULibretroCoreInstance::GunReleaseTrigger(int Port) +{ + NOT_LAUNCHED_GUARD + + CoreInstance.GetValue()->EnqueueTask([=](auto) + { + CoreInstance.GetValue()->InputState[Port][ERetroDeviceID::LightgunIsOffscreen] = false; + CoreInstance.GetValue()->InputState[Port][ERetroDeviceID::PointerPressed] = false; + CoreInstance.GetValue()->InputState[Port][ERetroDeviceID::PointerCount] = 0; + }); } void ULibretroCoreInstance::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) @@ -287,29 +359,32 @@ void ULibretroCoreInstance::TickComponent(float DeltaTime, enum ELevelTick TickT { if (!Controller[Port].IsValid()) continue; - FLibretroInputState NextInputState = {{0}}; + FLibretroInputState NextInputState{}; for (auto ControllerBinding : Bindings[Port]) { if (ControllerBinding.Key.IsAxis1D()) { // Both Y-Axes should be inverted because of Libretro convention however Unreal has a quirk where the Y-Axis of the right stick is inverted by default for some reason - // LX RX LY RY - float StickAxisInvert[] = { 1, 1, -1, 1 }; - int StickAxisIndex = to_integral(ControllerBinding.Value) - to_integral(ERetroInput::LeftX); - // Some cores support 0x7FFF to -0x7FFF others to -0x8000. However I support only 0x7FFF to -0x7FFF - auto retro_value = (int16_t)FMath::RoundHalfToEven(StickAxisInvert[StickAxisIndex] * 0x7FFF * Controller[Port]->PlayerInput->GetKeyValue(ControllerBinding.Key)); - reinterpret_cast(NextInputState.analog)[StickAxisIndex] = int16_t(FMath::Clamp(reinterpret_cast(NextInputState.analog)[StickAxisIndex] + retro_value, -0x7FFF, 0x7FFF)); + // LX LY RX RY + float StickAxisInvert[] = { 1, -1, 1, 1 }; + int StickAxisInvertIndex = to_integral(ControllerBinding.Value) - to_integral(ERetroDeviceID::AnalogLeftX); + // Some cores support 0x7FFF to -0x7FFF others to -0x8000 + int16_t retro_value = FMath::RoundHalfToEven(StickAxisInvert[StickAxisInvertIndex] * 0x7FFF * Controller[Port]->PlayerInput->GetKeyValue(ControllerBinding.Key)); + NextInputState[ControllerBinding.Value] = FMath::Clamp(NextInputState[ControllerBinding.Value] + retro_value, -0x7FFF, 0x7FFF); } else { - NextInputState.digital[to_integral(ControllerBinding.Value)] |= static_cast(Controller[Port]->PlayerInput->IsPressed(ControllerBinding.Key)); + NextInputState[ControllerBinding.Value] |= static_cast(Controller[Port]->PlayerInput->IsPressed(ControllerBinding.Key)); } } + if (Bindings[Port].Num() > 0) + { CoreInstance.GetValue()->EnqueueTask([&InputStatePort = this->CoreInstance.GetValue()->InputState[Port], NextInputState](auto) { InputStatePort = NextInputState; }); + } if (ForwardKeyboardInput[Port]) { diff --git a/Source/UnrealLibretro/Private/sdlarch.cpp b/Source/UnrealLibretro/Private/sdlarch.cpp index 6d1c8cb1..2b857ee4 100644 --- a/Source/UnrealLibretro/Private/sdlarch.cpp +++ b/Source/UnrealLibretro/Private/sdlarch.cpp @@ -5,6 +5,7 @@ extern "C" #include "gfx/scaler/pixconv.h" } +#include "LibretroCoreInstance.h" #include "UnrealLibretro.h" // For Libretro debug log category #include "LibretroSettings.h" #include "LibretroInputDefinitions.h" @@ -313,7 +314,7 @@ static bool GLLogCall(const char* function, const char* file, int line) ID3D12Resource* ResolvedTexture = (ID3D12Resource*)this->Unreal.TextureRHI->GetTexture2D()->GetNativeResource(); D3D12_RESOURCE_DESC TextureAttributes = ResolvedTexture->GetDesc(); D3D12_RESOURCE_ALLOCATION_INFO TextureMemoryUsage = UE4D3DDevice->GetResourceAllocationInfo(0b0, 1, &TextureAttributes); - check(!FAILED(UE4D3DDevice->CreateSharedHandle(ResolvedTexture, NULL, GENERIC_ALL, *FString::Printf(TEXT("OpenGLSharedFramebuffer_UnrealLibretro_%u"), NamingIdx.Increment()), &SharedHandle))); + verify(!FAILED(UE4D3DDevice->CreateSharedHandle(ResolvedTexture, NULL, GENERIC_ALL, *FString::Printf(TEXT("OpenGLSharedFramebuffer_UnrealLibretro_%u"), NamingIdx.Increment()), &SharedHandle))); check(SharedHandle); MipLevels = TextureAttributes.MipLevels; @@ -410,13 +411,14 @@ static bool GLLogCall(const char* function, const char* file, int line) } else { for (auto i : { 0, 1 }) { core.software.bgra_buffers[i] = FMemory::Malloc(4 * core.av.geometry.max_width - * core.av.geometry.max_height); + * core.av.geometry.max_height, PLATFORM_CACHE_LINE_SIZE); } } core.hw.context_reset(); } +#include "Async/TaskGraphInterfaces.h" // Stripped down code for profiling purposes https://godbolt.org/z/c57esx void LibretroContext::core_video_refresh(const void *data, unsigned width, unsigned height, unsigned pitch) { DECLARE_SCOPE_CYCLE_COUNTER(TEXT("PrepareFrameBufferForRenderThread"), STAT_LibretroPrepareFrameBufferForRenderThread, STATGROUP_UnrealLibretro); @@ -598,7 +600,7 @@ static void core_log(enum retro_log_level level, const char *fmt, ...) { } bool LibretroContext::core_environment(unsigned cmd, void *data) { - bool delegate_status; + bool delegate_status{false}; if (CoreEnvironmentCallback) { delegate_status = CoreEnvironmentCallback(cmd, data); @@ -606,17 +608,16 @@ bool LibretroContext::core_environment(unsigned cmd, void *data) { switch (cmd) { case RETRO_ENVIRONMENT_GET_VARIABLE: { - struct retro_variable* var = (struct retro_variable*)data; + retro_variable* var = (struct retro_variable*)data; - auto key = std::string(var->key); + std::string key(var->key); if (core.settings.find(key) != core.settings.end()) { var->value = core.settings.at(key).c_str(); return true; } - else { + return false; - } } case RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE: { const struct retro_variable* var = (const struct retro_variable*)data; @@ -630,6 +631,8 @@ bool LibretroContext::core_environment(unsigned cmd, void *data) { // Initialize key const std::string key(arr_var->key); + // Don't override custom options already set + if (core.settings.count(key) == 0) { // Parse and initialize default setting, First delimited setting is default by Libretro convention auto advance_past_space = [](const char* x) { while (*x == ' ') { x++; } return x; }; auto past_comment = advance_past_space(strchr(arr_var->value, ';') + 1); @@ -639,6 +642,7 @@ bool LibretroContext::core_environment(unsigned cmd, void *data) { // Write setting to table core.settings[key] = default_setting; + } } while ((++arr_var)->key); @@ -759,9 +763,6 @@ bool LibretroContext::core_environment(unsigned cmd, void *data) { } case RETRO_ENVIRONMENT_SET_CONTROLLER_INFO: { auto controller_info = (const struct retro_controller_info*)data; - for (unsigned i = 0; i < controller_info->num_types; i++) { - UE_LOG(Libretro, Verbose, TEXT("Supported Controllers: %s"), ANSI_TO_TCHAR(controller_info->types[i].desc)); - } return true; } @@ -809,13 +810,19 @@ bool LibretroContext::core_environment(unsigned cmd, void *data) { return delegate_status; } +// Unfinished experiment with less branchy version of this function https://godbolt.org/z/hYeYxr95r int16_t LibretroContext::core_input_state(unsigned port, unsigned device, unsigned index, unsigned id) { + // Some of the setup to get the core to poll for certain types of input is either done through retro_set_controller_port_device + // or in the handled RETRO_ENVIRONMENT_GET_VARIABLE and RETRO_ENVIRONMENT_SET_CONTROLLER_INFO events in the retro_environment_t + // callback core_environment. Getting the core to handle specific input amounts to reading documentation and sifting through the core's code switch (device) { - case RETRO_DEVICE_ANALOG: - //check(index < 2); // "I haven't implemented Triggers and other analog controls yet" - return InputState[port].analog[id][index]; - case RETRO_DEVICE_JOYPAD: return InputState[port].digital[id]; + case RETRO_DEVICE_JOYPAD: return InputState[port][to_integral(ERetroDeviceID::JoypadB) + id]; + case RETRO_DEVICE_LIGHTGUN: return InputState[port][to_integral(ERetroDeviceID::LightgunX) + id]; + case RETRO_DEVICE_ANALOG: return InputState[port][to_integral(ERetroDeviceID::AnalogLeftX) + 2 * index + (id % RETRO_DEVICE_ID_JOYPAD_L2)]; // The indexing logic is broken and might OOBs if we're queried for something that isn't an analog trigger or stick + case RETRO_DEVICE_POINTER: return InputState[port][to_integral(ERetroDeviceID::PointerX) + id]; + case RETRO_DEVICE_MOUSE: + case RETRO_DEVICE_KEYBOARD: default: return 0; } } @@ -884,12 +891,9 @@ void LibretroContext::load(const char *sofile) { void LibretroContext::load_game(const char* filename) { - struct retro_system_info system = { 0 }; struct retro_game_info info = { filename , nullptr, (size_t)0, "" }; TArray gameBinary; - libretro_api.get_system_info(&system); - if (!system.need_fullpath) { verify(FFileHelper::LoadFileToArray(gameBinary, UTF8_TO_TCHAR(filename))); @@ -920,7 +924,7 @@ void LibretroContext::load_game(const char* filename) { video_configure(&core.av.geometry); } -LibretroContext* LibretroContext::Launch(FString core, FString game, UTextureRenderTarget2D* RenderTarget, URawAudioSoundWave* SoundBuffer, TUniqueFunction LoadedCallback) +LibretroContext* LibretroContext::Launch(ULibretroCoreInstance* LibretroCoreInstance, FString core, FString game, UTextureRenderTarget2D* RenderTarget, URawAudioSoundWave* SoundBuffer, TUniqueFunction LoadedCallback) { check(IsInGameThread()); // So static initialization is safe + UObject access @@ -930,6 +934,11 @@ LibretroContext* LibretroContext::Launch(FString core, FString game, UTextureRen LibretroContext *l = new LibretroContext(); + for (auto& Option : LibretroCoreInstance->CoreOptions) + { + l->core.settings[TCHAR_TO_UTF8(*Option.Key)] = TCHAR_TO_UTF8(*Option.Value); + } + // Grab a statically generated callback structure int32 InstanceNumber; { @@ -959,10 +968,11 @@ LibretroContext* LibretroContext::Launch(FString core, FString game, UTextureRen // Kick the initialization process off to another thread. It shouldn't be added to the Unreal task pool because those are too slow and my code relies on OpenGL state being thread local. // The Runnable system is the standard way for spawning and managing threads in Unreal. FThread looks enticing, but they removed any way to detach threads since "it doesn't work as expected" l->LambdaRunnable = FLambdaRunnable::RunLambdaOnBackGroundThread(FPaths::GetCleanFilename(core) + FPaths::GetCleanFilename(game), - [=, LoadedCallback = MoveTemp(LoadedCallback)]() { + [=, LoadedCallback = MoveTemp(LoadedCallback), ControllersSetOnLaunch = LibretroCoreInstance->ControllersSetOnLaunch]() { // Here I load a copy of the dll instead of the original. If you load the same dll multiple times you won't obtain a new instance of the dll loaded into memory, // instead all variables and function pointers will point to the original loaded dll + // WARNING: Don't ever even try to load the original dll since the editor needs to load it to query core settings (This can happen when you pause in PIE!) const FString InstancedCorePath = IFileManager::Get().ConvertToAbsolutePathForExternalAppForWrite( #if PLATFORM_ANDROID // On Android .so's have to be copied to your private application directory before they are loaded @@ -983,13 +993,27 @@ LibretroContext* LibretroContext::Launch(FString core, FString game, UTextureRen l->core.hw.context_destroy = []() {}; // Loads the dll and its function pointers into libretro_api - l->load(TCHAR_TO_ANSI(*InstancedCorePath)); + l->load(TCHAR_TO_UTF8(*InstancedCorePath)); + + l->libretro_api.get_system_info(&l->system); + for (int Port = 0; Port < PortCount; Port++) + { + auto* ControllerDescription = ControllersSetOnLaunch.Find(l->system.library_name); + if (ControllerDescription) + { + l->libretro_api.set_controller_port_device(Port, (*ControllerDescription)[Port].ID); + } + else + { + // This is needed for some cores nestopia specifically is one example + // otherwise nothing will be bound for some reason + l->libretro_api.set_controller_port_device(Port, RETRO_DEVICE_DEFAULT); + } + } // This does load the game but does many other things as well. If hardware rendering is needed it loads OpenGL resources from the OS and this also initializes the unreal engine resources for audio and video. l->load_game(TCHAR_TO_UTF8(*game)); - // This is needed for some cores nestopia specifically is one example - l->libretro_api.set_controller_port_device(0, RETRO_DEVICE_JOYPAD); LoadedCallback(l, l->libretro_api); diff --git a/Source/UnrealLibretro/Private/sdlarch.h b/Source/UnrealLibretro/Private/sdlarch.h index 4fe616e0..3508211e 100644 --- a/Source/UnrealLibretro/Private/sdlarch.h +++ b/Source/UnrealLibretro/Private/sdlarch.h @@ -117,7 +117,7 @@ struct LibretroContext { * @brief analogous to new except asynchronous * @post The LoadedCallback is always called */ - static LibretroContext* Launch(FString core, FString game, UTextureRenderTarget2D* RenderTarget, URawAudioSoundWave* SoundEmitter, TUniqueFunction LoadedCallback); + static LibretroContext* Launch(class ULibretroCoreInstance* LibretroCoreInstance, FString core, FString game, UTextureRenderTarget2D* RenderTarget, URawAudioSoundWave* SoundEmitter, TUniqueFunction LoadedCallback); /** * @brief analogous to delete except asynchronous @@ -139,6 +139,8 @@ struct LibretroContext { */ FLibretroInputState InputState[PortCount]; + struct retro_system_info system = { 0 }; + TUniqueFunction::Type> CoreEnvironmentCallback; protected: LibretroContext() {} diff --git a/Source/UnrealLibretro/Public/LibretroBlueprintFunctionLibrary.h b/Source/UnrealLibretro/Public/LibretroBlueprintFunctionLibrary.h index b65093a1..7d483c69 100644 --- a/Source/UnrealLibretro/Public/LibretroBlueprintFunctionLibrary.h +++ b/Source/UnrealLibretro/Public/LibretroBlueprintFunctionLibrary.h @@ -32,5 +32,8 @@ class UNREALLIBRETRO_API ULibretroBlueprintFunctionLibrary : public UBlueprintFu static UActorComponent* HasComponent(AActor* Actor, TSubclassOf ComponentClass, EBranchNames &Branch); UFUNCTION(BlueprintPure, Category = "Libretro|Util|Map", meta = (BlueprintThreadSafe)) - static TMap CombineInputMaps(const TMap &InMap1, const TMap &InMap2); + static TMap CombineInputMaps(const TMap &InMap1, const TMap &InMap2); + + UFUNCTION(BlueprintPure, Category = "Libretro|Util") + static bool IsSupportUVFromHitResultsEnabledInConfig(); }; diff --git a/Source/UnrealLibretro/Public/LibretroCoreInstance.h b/Source/UnrealLibretro/Public/LibretroCoreInstance.h index 7c0e1ad5..5cfdd782 100644 --- a/Source/UnrealLibretro/Public/LibretroCoreInstance.h +++ b/Source/UnrealLibretro/Public/LibretroCoreInstance.h @@ -5,9 +5,33 @@ #include "Components/AudioComponent.h" #include "LibretroInputDefinitions.h" #include "Engine/TextureRenderTarget2D.h" - #include "LibretroCoreInstance.generated.h" + +USTRUCT(BlueprintType) +struct FLibretroControllerDescription // retro_controller_description +{ + GENERATED_BODY() + + UPROPERTY(VisibleAnywhere) + FString Description{"Unspecified"}; + + UPROPERTY(VisibleAnywhere) + unsigned int ID{RETRO_DEVICE_DEFAULT}; +}; + +USTRUCT(BlueprintType) +struct FLibretroControllerDescriptions +{ + GENERATED_BODY() + + UPROPERTY(VisibleAnywhere) + FLibretroControllerDescription ControllerDescription[PortCount]; + + FLibretroControllerDescription& operator[](int Port) { return ControllerDescription[Port]; } + const FLibretroControllerDescription& operator[](int Port) const { return ControllerDescription[Port]; } +}; + DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnCoreIsReady, const class UTextureRenderTarget2D*, LibretroFramebuffer, const class USoundWave*, AudioBuffer); DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnCoreFramebufferResize); @@ -26,7 +50,7 @@ class UNREALLIBRETRO_API ULibretroCoreInstance : public UActorComponent virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; virtual void BeginDestroy(); - + friend class FLibretroCoreInstanceDetails; /** Delegate Functions */ /** @@ -101,7 +125,7 @@ class UNREALLIBRETRO_API ULibretroCoreInstance : public UActorComponent UFUNCTION(BlueprintCallable, Category = "Libretro") void ConnectController(APlayerController* PlayerController, int Port, - TMap ControllerBindings, + TMapControllerBindings, FOnControllerDisconnected OnControllerDisconnected, bool ForwardAllKeyboardInputToCoreIfPossible); @@ -115,6 +139,52 @@ class UNREALLIBRETRO_API ULibretroCoreInstance : public UActorComponent UFUNCTION(BlueprintCallable, Category = "Libretro") void DisconnectController(int Port); + /** + * @brief Sets the state of a button for the Libretro core + * + * Bindings in ConnectController will override inputs set here + * You can set analog inputs through this interface as well however it won't work well + * + */ + UFUNCTION(BlueprintCallable, Category = "Libretro|IneffectiveBeforeLaunch") + void SetInputDigital(int Port, bool Pressed, ERetroDeviceID Input); + + /** + * @brief Sets the analog state of an input for the Libretro core + * + * This should mainly only be used for analog stick inputs and analog triggers + * The internal datatype returned when input is queried by the Libretro Core is int16 + * for digital inputs its 1 or 0 in which case use SetInputDigital + */ + UFUNCTION(BlueprintCallable, Category = "Libretro|IneffectiveBeforeLaunch") + void SetInputAnalog(int Port, int _16BitSignedInteger, ERetroDeviceID Input); + + /** + * @brief Equivalent to pulling a trigger on a nes zapper or arcade gun + * + * @param XY - The top-left of the screen is coordinate (0, 0) bottom-right is (1, 1), x is the horizontal axis + * + * These are convenience functions you can try fiddling around with the Lightgun and Pointer input directly in SetInputDigital too + * + * There are some gotchas here: + * - on nestopia if you only pull the trigger for one frame it won't register + * - zapper on nes requires Port=1 which is the second port because of zero based indexing + */ + UFUNCTION(BlueprintCallable, Category = "Libretro") + void GunPullTrigger(int Port, FVector2D XY); + + /** + * @brief This is used for reloading in lots of games + */ + UFUNCTION(BlueprintCallable, Category = "Libretro") + void GunPullTriggerOffscreen(int Port); + + /** + * @brief Equivalent to releasing a trigger on a nes zapper or arcade gun + */ + UFUNCTION(BlueprintCallable, Category = "Libretro") + void GunReleaseTrigger(int Port); + /** * @brief Where the Libretro Core's frame is drawn * @@ -130,6 +200,21 @@ class UNREALLIBRETRO_API ULibretroCoreInstance : public UActorComponent UPROPERTY(BlueprintReadWrite, Category = Libretro) UAudioComponent* AudioComponent; + /** + * @brief A key-value map for configuring Libretro Cores + * + * Options are appended to this list when you change them in the 'Libretro Core Options' section of the details panel + * They are only added here if they differ from their default + */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Libretro) + TMap CoreOptions; + + /** + * Making @todo probably don't make this part of the public interface + */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Libretro, meta = (ShowInnerProperties)) + TMap ControllersSetOnLaunch; + /** * You should provide a path to your ROM relative to the MyROMs directory in the UnrealLibretro directory in your project's Plugins directory. * So if your ROM is at [MyProjectName]/Plugins/UnrealLibretro/MyROMs/myrom.rom this should be set to myrom.rom @@ -176,7 +261,7 @@ class UNREALLIBRETRO_API ULibretroCoreInstance : public UActorComponent UPROPERTY() USoundWave* AudioBuffer; - TStaticArray, PortCount> Bindings; + TStaticArray, PortCount> Bindings; TStaticArray, PortCount> Controller{ nullptr }; TStaticArray ForwardKeyboardInput{ false }; TStaticArray Disconnected{ FOnControllerDisconnected() }; diff --git a/Source/UnrealLibretro/Public/LibretroGrabComponent.h b/Source/UnrealLibretro/Public/LibretroGrabComponent.h new file mode 100644 index 00000000..767a7e20 --- /dev/null +++ b/Source/UnrealLibretro/Public/LibretroGrabComponent.h @@ -0,0 +1,255 @@ +// Let's you grab stuff with VR controllers +// Heavily based on the GrabComponent blueprint from the UE_5 VRTemplate +// I had to port it back to an older version of UE so I figured I'd make it C++ since the blueprint version already exists + +#pragma once + +#include "LibretroVRInteractionInterface.h" + +#include "CoreMinimal.h" +#include "Delegates/Delegate.h" +#include "Components/SceneComponent.h" +#include "MotionControllerComponent.h" +#include "Kismet/KismetSystemLibrary.h" +#include "Kismet/GameplayStatics.h" +#include "Haptics/HapticFeedbackEffect_Base.h" +#include "Components/PrimitiveComponent.h" +#include "LibretroGrabComponent.generated.h" + +UENUM(BlueprintType) +enum class LibretroGrabType : uint8 +{ + None, + Free, + Snap, + Custom, +}; + +// John: I modified the code to return the grab component when these events +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGrabbed, class ULibretroGrabComponent*, GrabComponent); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnDropped, class ULibretroGrabComponent*, GrabComponent); + +UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent)) +class UNREALLIBRETRO_API ULibretroGrabComponent : public USceneComponent, public ILibretroVRInteractionInterface +{ + GENERATED_BODY() + +public: + ULibretroGrabComponent() + { + // Tick is disabled in Class Defaults + PrimaryComponentTick.bCanEverTick = false; + } + + UPROPERTY(BlueprintReadWrite, AdvancedDisplay) + UMotionControllerComponent* MotionControllerRef; + + UPROPERTY(BlueprintReadWrite) + bool bIsHeld{false}; + + UPROPERTY(BlueprintReadWrite) + FRotator PrimaryGrabRelativeRotation; + + UPROPERTY(BlueprintReadWrite) + bool bSimulateOnDrop{false}; + + UPROPERTY(EditAnywhere) + LibretroGrabType GrabType; + + UPROPERTY(EditAnywhere) + UHapticFeedbackEffect_Base* OnGrabHapticEffect; + + UPROPERTY(BlueprintAssignable) + FOnGrabbed OnGrabbed; + + UPROPERTY(BlueprintAssignable) + FOnDropped OnDropped; + + UFUNCTION(BlueprintCallable) + void SetShouldSimulateOnDrop() + { + if (auto* Parent = Cast(GetAttachParent())) + { + if (Parent->IsAnySimulatingPhysics()) + { + bSimulateOnDrop = true; + } + } + }; + + UFUNCTION(BlueprintCallable) + void SetPrimitiveCompPhysics(bool bSimulate) + { + if (auto* Parent = Cast(GetAttachParent())) + { + Parent->SetSimulatePhysics(bSimulate); + } + else + { + UE_LOG(LogTemp, Warning, TEXT("GrabComponent->SetSimulatingParent->Cast To PrimitiveComponent FAILED")); + } + } + + UFUNCTION(BlueprintCallable) + bool TryGrab(UMotionControllerComponent* MotionController) + { + // Then 0: Try to grab + switch (GrabType) + { + case LibretroGrabType::Free: + case LibretroGrabType::Snap: { + SetPrimitiveCompPhysics(false); + { // Attach Parent to Motion Controller + + bool bAttachSuccessful = GetAttachParent()->AttachToComponent( + MotionController, + FAttachmentTransformRules( + EAttachmentRule::KeepWorld, + EAttachmentRule::KeepWorld, + EAttachmentRule::KeepWorld, + /*bWeldSimulatedBodies = */ true + )); + + if (!bAttachSuccessful) + { + // Debug Print String - Attachment failed. + // It's good practice to leave debug prints on conditions that are expected to be True + UE_LOG(LogTemp, Warning, TEXT("Attaching %s to %s FAILED - object not grabbed"), + *UKismetSystemLibrary::GetDisplayName(GetAttachParent()), + *UKismetSystemLibrary::GetDisplayName(MotionController)); + break; + } + + bIsHeld = true; + + if (GrabType == LibretroGrabType::Snap) + { + // Orient the held Actor to match GrabComponent's relative location + + GetAttachParent()->SetRelativeRotation( + GetRelativeRotation().GetInverse(), + /* Sweep = */ false, + /* OutSweepHitResult = */ nullptr, + ETeleportType::TeleportPhysics); + + GetAttachParent()->SetWorldLocation( + MotionController->GetComponentLocation() + (GetAttachParent()->GetComponentLocation() - GetComponentLocation()), + /* Sweep = */ false, + /* OutSweepHitResult = */ nullptr, + ETeleportType::TeleportPhysics); + } + } + break; + } + case LibretroGrabType::Custom: { + bIsHeld = true; + break; + } + case LibretroGrabType::None: + default: break; + } + + // Then 1: then continue + // Did the grab succeed? + if (bIsHeld) + { + MotionControllerRef = MotionController; + + OnGrabbed.Broadcast(this); + + if (auto* PlayerController = UGameplayStatics::GetPlayerController(GetWorld(), MotionController->PlayerIndex)) + { + PlayerController->PlayHapticEffect( + OnGrabHapticEffect, // Fine if nullptr + MotionController->GetTrackingSource(), + /* Scale = */ 1.0, + /* bLoop = */ false); + } + } + + return bIsHeld; + } + + UFUNCTION(BlueprintCallable) + bool TryRelease(UMotionControllerComponent* MotionController) + { + // Then 0: Try to release + switch (GrabType) + { + case LibretroGrabType::Free: + case LibretroGrabType::Snap: { + // If primary hand drops, release and check if we should simulate physics + if (bSimulateOnDrop) + { + SetPrimitiveCompPhysics(true); + } + else + { + // This will detach the GrabComponent's Parent from the MotionControllerComponent that it's attached to. + // If Get Attach Parent is the Root of the Actor, the actor is detached. + // However, you can also attach GrabComponents to components that aren't the root of the actor. + GetAttachParent()->DetachFromComponent( + FDetachmentTransformRules( + EDetachmentRule::KeepWorld, + EDetachmentRule::KeepWorld, + EDetachmentRule::KeepWorld, + /* bCallModify = */ true + )); + } + + // fallthrough + } + case LibretroGrabType::Custom: bIsHeld = false; + case LibretroGrabType::None: + default: break; + } + + // Then 1: then continue + bool bReleased = !bIsHeld; + if (bReleased) + { + OnDropped.Broadcast(this); + } + + return bReleased; + } + + // ILibretroVRInteractionInterface -- John: More or less this amounts to forwarding trigger input to held actor + void TriggerAxis_Implementation(float Scale) override + { + if (auto* Owner = Cast(GetOwner())) + { + Owner->TriggerAxis(Scale); + } + } + + void TriggerPressed_Implementation() override + { + if (auto* Owner = Cast(GetOwner())) + { + Owner->TriggerPressed(); + } + } + + void TriggerReleased_Implementation() override + { + if (auto* Owner = Cast(GetOwner())) + { + Owner->TriggerReleased(); + } + } + +protected: + // Called when the game starts + void BeginPlay() + { + Super::BeginPlay(); + + SetShouldSimulateOnDrop(); + + if (auto* Parent = Cast(GetAttachParent())) + { + Parent->SetCollisionProfileName("PhysicsActor"/*, bUpdateOverlaps = true*/); + } + } +}; diff --git a/Source/UnrealLibretro/Public/LibretroInputDefinitions.h b/Source/UnrealLibretro/Public/LibretroInputDefinitions.h index 157a9b91..ce821cd1 100644 --- a/Source/UnrealLibretro/Public/LibretroInputDefinitions.h +++ b/Source/UnrealLibretro/Public/LibretroInputDefinitions.h @@ -2,44 +2,95 @@ #include "libretro/libretro.h" + +// By convention RETRO_DEVICE_JOYPAD is the default controller although often it represents something different compared to +// the libretro.h documentation of RETRO_DEVICE_JOYPAD. Basically it will be the most reasonable controller for the core +#define RETRO_DEVICE_DEFAULT RETRO_DEVICE_JOYPAD + + #include "InputCoreTypes.h" #include "CoreMinimal.h" -constexpr int PortCount = 4; -struct FLibretroInputState +#include // One of the few std headers where its usage is recommended over a native Unreal Engine implementation see https://docs.unrealengine.com/5.0/en-US/epic-cplusplus-coding-standard-for-unreal-engine/#useofstandardlibraries + +// Check libretro.h for further documentation. More or less this maps to RETRO_DEVICE_ID values in there +// DO NOT REORDER THESE... doing so will break indexing badly.... +// The order for each RETRO_DEVICE is in the same numerical increasing order as every RETRO_DEVICE_*_ID value +UENUM(BlueprintType) +enum class ERetroDeviceID : uint8 { - unsigned digital[RETRO_DEVICE_ID_JOYPAD_R3 + 1]{}; - int16_t analog[RETRO_DEVICE_INDEX_ANALOG_RIGHT + 1][RETRO_DEVICE_ID_ANALOG_Y + 1]{}; + // RETRO_DEVICE_ID_JOYPAD + JoypadB, + JoypadY, + JoypadSelect, + JoypadStart, + JoypadUp, + JoypadDown, + JoypadLeft, + JoypadRight, + JoypadA, + JoypadX, + JoypadL, + JoypadR, + JoypadL2, + JoypadR2, + JoypadL3, + JoypadR3, + + // RETRO_DEVICE_ID_LIGHTGUN + LightgunX UMETA(Hidden), // The Lightgun entries marked UMETA(Hidden) here are deprecated according to libretro.h + LightgunY UMETA(Hidden), + LightgunTrigger, + LightgunAuxA, + LightgunAuxB, + LightgunPause UMETA(Hidden), + LightgunStart, + LightgunSelect, + LightgunAuxC, + LightgunDpadUp, + LightgunDpadDown, + LightgunDpadLeft, + LightgunDpadRight, + LightgunScreenX, + LightgunScreenY, + LightgunIsOffscreen, + LightgunReload, + + // RETRO_DEVICE_ID_ANALOG (For triggers) + // CartesianProduct(RETRO_DEVICE_ID_ANALOG, RETRO_DEVICE_INDEX) (For stick input) + AnalogLeftX, + AnalogLeftY, + AnalogRightX, + AnalogRightY, + AnalogL2, + AnalogR2, + + // RETRO_DEVICE_ID_POINTER + PointerX, + PointerY, + PointerPressed, + PointerCount, + + Size UMETA(Hidden), }; -// DO NOT REORDER THESE -UENUM(BlueprintType) -enum class ERetroInput : uint8 +// std alchemy that converts enum class variables to their integer type +template +static constexpr auto to_integral(E e) -> typename std::underlying_type::type { - B, - Y, - SELECT, - START, - UP, - DOWN, - LEFT, - RIGHT, - A, - X, - L, - R, - L2, - R2, - L3, - R3, - LeftX, - RightX, - LeftY, - RightY, - DisconnectController, - - DigitalCount = LeftX UMETA(Hidden), - AnalogCount = DisconnectController - DigitalCount UMETA(Hidden) + return static_cast::type>(e); +} + +constexpr int PortCount = 4; + +struct FLibretroInputState +{ + int16_t Data[to_integral(ERetroDeviceID::Size)]{0}; + + FLibretroInputState() {} + + int16_t& operator[](unsigned Index) { return Data[Index]; } + int16_t& operator[](ERetroDeviceID RetroDeviceID) { return Data[to_integral(RetroDeviceID)]; } }; // const for a reason if you make this non-const make sure you don't cause a data race also make it non static too diff --git a/Source/UnrealLibretro/Public/LibretroVRInteractionInterface.h b/Source/UnrealLibretro/Public/LibretroVRInteractionInterface.h new file mode 100644 index 00000000..f54d5586 --- /dev/null +++ b/Source/UnrealLibretro/Public/LibretroVRInteractionInterface.h @@ -0,0 +1,38 @@ +#pragma once + +#include "CoreMinimal.h" +#include "UObject/Interface.h" +#include "LibretroVRInteractionInterface.generated.h" + +// UE Reflection boilerplate +UINTERFACE(BlueprintType, MinimalAPI) +class ULibretroVRInteractionInterface : public UInterface +{ + GENERATED_BODY() +}; + + +class UNREALLIBRETRO_API ILibretroVRInteractionInterface +{ + GENERATED_BODY() + +public: + UFUNCTION(BlueprintCallable, BlueprintNativeEvent) + void TriggerAxis(float Val); + + UFUNCTION(BlueprintCallable, BlueprintNativeEvent) + void TriggerPressed(); + + UFUNCTION(BlueprintCallable, BlueprintNativeEvent) + void TriggerReleased(); + + + UFUNCTION(BlueprintCallable, BlueprintNativeEvent) + void SecondaryTriggerAxis(float Val); + + UFUNCTION(BlueprintCallable, BlueprintNativeEvent) + void SecondaryTriggerPressed(); + + UFUNCTION(BlueprintCallable, BlueprintNativeEvent) + void SecondaryTriggerReleased(); +}; diff --git a/Source/UnrealLibretro/Public/LibretroVRPawn.h b/Source/UnrealLibretro/Public/LibretroVRPawn.h new file mode 100644 index 00000000..568c5f8b --- /dev/null +++ b/Source/UnrealLibretro/Public/LibretroVRPawn.h @@ -0,0 +1,442 @@ +// Port of VRPawn blueprint from UE_5 VRTemplate +// @todo Menu stuff + +#pragma once + +#include "LibretroGrabComponent.h" + +#include "CoreMinimal.h" +#include "Engine/StaticMeshActor.h" +#include "Kismet/KismetSystemLibrary.h" +#include "Kismet/KismetMathLibrary.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "GameFramework/Pawn.h" +#include "Components/SplineMeshComponent.h" +#include "NavigationSystem.h" +#include "Camera/CameraComponent.h" +#include "MotionControllerComponent.h" +#include "HeadMountedDisplayFunctionLibrary.h" +#include "LibretroVRPawn.generated.h" + + +UCLASS() +class UNREALLIBRETRO_API ALibretroVRPawn : public APawn +{ + GENERATED_BODY() + +public: + ALibretroVRPawn() + { + PrimaryActorTick.bCanEverTick = false; + + auto* DefaultRootComponent = CreateDefaultSubobject(TEXT("DefaultSceneRoot")); + SetRootComponent(DefaultRootComponent); + + Camera = CreateDefaultSubobject(TEXT("Camera")); + MotionControllerLeft = CreateDefaultSubobject(TEXT("MotionControllerLeft")); + MotionControllerRight = CreateDefaultSubobject(TEXT("MotionControllerRight")); + + // John: I set defaults in a blueprint subclass instead since I think that's slightly easier to understand + // You can filter for settings that differ from the default in the editor which is a nice feature writing new defaults here would mask that ability + //MotionControllerLeft->bDisplayDeviceModel = true; + //MotionControllerRight->bDisplayDeviceModel = true; + //MotionControllerLeft->MotionSource = "Left"; + //MotionControllerRight->MotionSource = "Right"; + + Camera->SetupAttachment(DefaultRootComponent); + MotionControllerLeft->SetupAttachment(DefaultRootComponent); + MotionControllerRight->SetupAttachment(DefaultRootComponent); + } + + UPROPERTY(BlueprintReadWrite) + FVector ProjectedTeleportLocation; + + UPROPERTY(BlueprintReadWrite) + bool bValidTeleportLocation = false; + + UPROPERTY(BlueprintReadWrite) + bool bTeleportTraceActive = false; + + UPROPERTY(BlueprintReadWrite) + float GrabRadiusFromGripPosition = 6.0f; + + UPROPERTY(BlueprintReadWrite) + float AxisDeadzoneThreshold = 0.7f; + + UPROPERTY(EditAnywhere) + TSubclassOf TeleportVisualizerClass = AActor::StaticClass(); + + UPROPERTY(BlueprintReadWrite) + AActor* TeleportVisualizerReference = nullptr; + + UPROPERTY(BlueprintReadWrite) + float SnapTurnDegrees = -45.0f; + + UPROPERTY(BlueprintReadWrite) + TArray TeleportTracePathPositions; + + // This is a pool of spline mesh components we've created so far not all of them are always used + UPROPERTY(EditAnywhere) + TArray TeleportTraceSplineMeshComponents; + + // You can change Query Extent to get different results, a low value will "help" the player find a teleport location + UPROPERTY(BlueprintReadWrite) + FVector TeleportProjectPointToNavigationQueryExtent; + + //UPROPERTY(BlueprintReadWrite) + //AMenu + + UPROPERTY(EditAnywhere) + UCameraComponent* Camera; + + UPROPERTY(EditAnywhere) + UMotionControllerComponent* MotionControllerLeft; + + UPROPERTY(EditAnywhere) + UMotionControllerComponent* MotionControllerRight; + + // These just aren't available in 4.24 + //UPROPERTY(EditAnywhere) + //UMotionControllerComponent* MotionControllerLeftAim; + // + //UPROPERTY(EditAnywhere) + //UMotionControllerComponent* MotionControllerRightAim; + + UPROPERTY(EditAnywhere) + UStaticMesh* SplineMesh; + + UPROPERTY() + ULibretroGrabComponent *HeldComponentLeft = nullptr; + + UPROPERTY() + ULibretroGrabComponent *HeldComponentRight = nullptr; + +protected: + struct FHand + { + decltype(HeldComponentLeft) &HeldComponent; + decltype(MotionControllerLeft) &MotionController; + }; + + CONSTEXPR FHand GetHand(EControllerHand ControllerHand) + { + if (ControllerHand == EControllerHand::Left) + { + return { HeldComponentLeft, MotionControllerLeft }; + } + else + { + return { HeldComponentRight, MotionControllerRight }; + } + } + + CONSTEXPR FHand GetOtherHand(EControllerHand ControllerHand) + { + if (ControllerHand == EControllerHand::Left) + { + return GetHand(EControllerHand::Right); + } + else + { + return GetHand(EControllerHand::Left); + } + } + + void BeginPlay() override + { + Super::BeginPlay(); + + // Begin Play - Set Tracking Origin to floor + if (UHeadMountedDisplayFunctionLibrary::IsHeadMountedDisplayEnabled()) + { + UHeadMountedDisplayFunctionLibrary::SetTrackingOrigin(EHMDTrackingOrigin::Floor); + } + } + +public: + ULibretroGrabComponent* GetGrabComponentNearMotionController(UMotionControllerComponent* MotionController) + { + ULibretroGrabComponent* NearestGrabComponent = nullptr; + FVector GripPosition = MotionController->GetComponentLocation(); + + if (auto* World = GEngine->GetWorldFromContextObject(this, EGetWorldErrorMode::LogAndReturnNull)) + { + FHitResult HitResult; + bool bHasHitResult = World->SweepSingleByChannel( + HitResult, + GripPosition, + GripPosition, + FQuat::Identity, + ECollisionChannel::ECC_PhysicsBody, + FCollisionShape::MakeSphere(GrabRadiusFromGripPosition), + FCollisionQueryParams() /* bTraceComplex = false */); + + if (bHasHitResult && HitResult.GetActor()) + { + decltype(GripPosition.Size()) NearestComponentDistance = MAX_flt; + + for (auto* Component : HitResult.GetActor()->GetComponents()) + { + if (auto* GrabComponent = Cast(Component)) + { + auto GrabComponentDistance = (GrabComponent->GetComponentLocation() - GripPosition).Size(); + if (GrabComponentDistance <= NearestComponentDistance) + { + NearestGrabComponent = GrabComponent; + NearestComponentDistance = GrabComponentDistance; + } + } + } + } + } + + return NearestGrabComponent; + } + + // Invoke ILibretroVRInteractionInterface calls to the AActor that own's the ULibretroGrabComponent + template + void Trigger() + { + auto Hand = GetHand(TControllerHand); + if (Hand.HeldComponent) + { + if (Hand.HeldComponent->GetOwner()->Implements()) + { + if (bPressed) ILibretroVRInteractionInterface::Execute_TriggerPressed (Hand.HeldComponent->GetOwner()); + else ILibretroVRInteractionInterface::Execute_TriggerReleased(Hand.HeldComponent->GetOwner()); + } + } + } + + template + void TriggerAxis(float Val) + { + auto Hand = GetHand(TControllerHand); + if (Hand.HeldComponent) + { + if (Hand.HeldComponent->GetOwner()->Implements()) + { + ILibretroVRInteractionInterface::Execute_TriggerAxis(Hand.HeldComponent->GetOwner(), Val); + } + } + } + + template + void Grab() + { + auto Hand = GetHand(TControllerHand); + + if (bPressed) + { + if (auto* GrabComponent = GetGrabComponentNearMotionController(Hand.MotionController)) + { + if (GrabComponent->TryGrab(Hand.MotionController)) + { + Hand.HeldComponent = GrabComponent; + + // If other hand was holding this component, clear our reference to it + if (HeldComponentLeft == HeldComponentRight) + { + GetOtherHand(TControllerHand).HeldComponent = nullptr; + } + } + } + } + else + { + if ( Hand.HeldComponent != nullptr + && Hand.HeldComponent->TryRelease(Hand.MotionController)) + { + Hand.HeldComponent = nullptr; + } + } + + } + +protected: bool bCanSnapTurn = true; +public: + void SnapTurn(float Val) + { + bool bPassedDeadZone = FMath::Abs(Val) > AxisDeadzoneThreshold; + if (bPassedDeadZone && bCanSnapTurn) + { + float YawDelta = FMath::Sign(-Val) * SnapTurnDegrees; + + FVector CameraLocation = Camera->GetComponentLocation(); + FTransform CameraRelativeTransform = Camera->GetRelativeTransform(); + + FTransform NewTransform{UKismetMathLibrary::ComposeRotators(GetActorRotation(), {0.0f, YawDelta, 0.0f}), GetActorLocation()}; + + AddActorWorldRotation({0.0f, YawDelta, 0.0f}, false, nullptr, ETeleportType::TeleportPhysics); + SetActorLocation( + /* NewLocation = */ GetActorLocation() + CameraLocation + - (UKismetMathLibrary::ComposeTransforms(CameraRelativeTransform, NewTransform)).GetLocation(), + /* bSweep = */ false, + /* OutSweepHitResult = */ nullptr, + ETeleportType::TeleportPhysics + ); + } + + bCanSnapTurn = !bPassedDeadZone; + } + + void TeleportTrace(FVector StartPos, FVector ForwardVector) + { + float TeleportLaunchSpeed = 650.0f; + float TeleportProjectileRadius = 3.6f; + FPredictProjectilePathResult PredictProjectilePathResult; + UGameplayStatics::PredictProjectilePath(this, + {TeleportProjectileRadius, StartPos, TeleportLaunchSpeed * ForwardVector, /* MaxSimTime = */ MAX_FLT, ECC_WorldStatic}, + PredictProjectilePathResult); + + // John: This is already done in UGameplayStatics::PredictProjectilePath it seems no need to keep it + //PredictProjectilePathResult.PathData.InsertDefaulted(0); + //PredictProjectilePathResult.PathData[0].Location = StartPos; + + TeleportTracePathPositions.SetNumUninitialized(PredictProjectilePathResult.PathData.Num(), false); + for (uint64 i = 0; i < PredictProjectilePathResult.PathData.Num(); i++) + { + TeleportTracePathPositions[i] = PredictProjectilePathResult.PathData[i].Location; + } + + FNavLocation ProjectedNavLocation; + + bool bIsValidTeleportLocation = UNavigationSystemV1::GetNavigationSystem(this)->ProjectPointToNavigation( + PredictProjectilePathResult.HitResult.Location, + ProjectedNavLocation, + TeleportProjectPointToNavigationQueryExtent); + + if (bIsValidTeleportLocation) + { + // Debug Teleport Location + //UKismetSystemLibrary::DrawDebugCylinder(this, + // ProjectedTeleportLocation, + // ProjectedTeleportLocation + FVector{0.0, 0.0, 25.0f}, + // 25.0f); + + float NavMeshCellHeight = 8.0f; // John: This is a member of RecastNavMesh + ProjectedTeleportLocation = ProjectedNavLocation.Location - FVector{0.0f, 0.0f, NavMeshCellHeight}; + + // If the value hasn't changed we don't have to set it or toggle visibility + if (bValidTeleportLocation != bIsValidTeleportLocation) + { + bValidTeleportLocation = bIsValidTeleportLocation; + TeleportVisualizerReference->GetRootComponent()->SetVisibility(bValidTeleportLocation, /* bPropagateToChildren = */ true); + } + } + + TeleportTraceSplineMeshComponents.SetNum(FMath::Max(TeleportTraceSplineMeshComponents.Num(), TeleportTracePathPositions.Num()-1), false); + { // John: I use spline mesh components for drawing the teleport trace rather than Niagara like the VRTemplate does + int i = 0; + for (; i < TeleportTracePathPositions.Num()-1; i++) + { + if (TeleportTraceSplineMeshComponents[i] == nullptr) + { + TeleportTraceSplineMeshComponents[i] = NewObject(this); + + TeleportTraceSplineMeshComponents[i]->SetMobility(EComponentMobility::Movable); + TeleportTraceSplineMeshComponents[i]->SetCollisionEnabled(ECollisionEnabled::NoCollision); + TeleportTraceSplineMeshComponents[i]->SetForwardAxis(ESplineMeshAxis::X, false); + TeleportTraceSplineMeshComponents[i]->bTickInEditor = true; + TeleportTraceSplineMeshComponents[i]->bCastDynamicShadow = false; + TeleportTraceSplineMeshComponents[i]->CastShadow = false; + TeleportTraceSplineMeshComponents[i]->RegisterComponent(); + } + + // Draw teleport trace as line segments + TeleportTraceSplineMeshComponents[i]->SetVisibility(bValidTeleportLocation); + TeleportTraceSplineMeshComponents[i]->SetStaticMesh(SplineMesh); + TeleportTraceSplineMeshComponents[i]->SetStartAndEnd( + TeleportTracePathPositions[i], + TeleportTracePathPositions[i+1] - TeleportTracePathPositions[i], + TeleportTracePathPositions[i+1], + TeleportTracePathPositions[i+1] - TeleportTracePathPositions[i] + ); + } + + // Hide leftover line segments we aren't using + for (; i < TeleportTraceSplineMeshComponents.Num(); i++) + { + TeleportTraceSplineMeshComponents[i]->SetVisibility(false); + } + } + + TeleportVisualizerReference->SetActorLocation(ProjectedTeleportLocation); + } + + void TeleportAxis(float Val) + { + if (Val > AxisDeadzoneThreshold) + { + if (!bTeleportTraceActive) + { + bTeleportTraceActive = true; + + { // StartTeleportTrace(); + if (auto* World = GEngine->GetWorldFromContextObject(this, EGetWorldErrorMode::LogAndReturnNull)) + { + TeleportVisualizerReference = World->SpawnActor(TeleportVisualizerClass); + } + } + } + + TeleportTrace(MotionControllerRight->GetComponentLocation(), MotionControllerRight->GetForwardVector()); + } + else + { + if (bTeleportTraceActive) + { + { // EndTeleportTrace(); + bTeleportTraceActive = false; + + if (TeleportVisualizerReference != nullptr) + { + TeleportVisualizerReference->Destroy(); + TeleportVisualizerReference = nullptr; + } + + for (auto* TeleportTraceSplineMeshComponent : TeleportTraceSplineMeshComponents) + { + TeleportTraceSplineMeshComponent->SetVisibility(false); + } + } + + { // TryTeleport(); + if (bValidTeleportLocation) + { + bValidTeleportLocation = false; + + FVector CameraRelativeLocation = Camera->GetRelativeLocation(); + CameraRelativeLocation.Z = 0.0f; // Ignore height + FRotator VRPawnRotation(0.0f, GetActorRotation().Yaw, 0.0f); + this->K2_TeleportTo( + ProjectedTeleportLocation - VRPawnRotation.RotateVector(CameraRelativeLocation), // Subtract HMD (Camera) for correct Pawn destination + VRPawnRotation); + } + } + } + } + } + + void SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) + { + Super::SetupPlayerInputComponent(PlayerInputComponent); + + InputComponent->BindAxis("LibretroMovementAxisLeft_X", this, &ALibretroVRPawn::SnapTurn); + InputComponent->BindAxis("LibretroMovementAxisRight_Y", this, &ALibretroVRPawn::TeleportAxis); + + InputComponent->BindAxis("LibretroTriggerAxisLeft", this, &ALibretroVRPawn::TriggerAxis); + InputComponent->BindAxis("LibretroTriggerAxisRight", this, &ALibretroVRPawn::TriggerAxis); + + + InputComponent->BindAction("LibretroGrabLeft", IE_Pressed, this, &ALibretroVRPawn::Grab); + InputComponent->BindAction("LibretroGrabRight", IE_Pressed, this, &ALibretroVRPawn::Grab); + InputComponent->BindAction("LibretroGrabLeft", IE_Released, this, &ALibretroVRPawn::Grab); + InputComponent->BindAction("LibretroGrabRight", IE_Released, this, &ALibretroVRPawn::Grab); + + InputComponent->BindAction("LibretroTriggerLeft", IE_Pressed, this, &ALibretroVRPawn::Trigger); + InputComponent->BindAction("LibretroTriggerRight", IE_Pressed, this, &ALibretroVRPawn::Trigger); + InputComponent->BindAction("LibretroTriggerLeft", IE_Released, this, &ALibretroVRPawn::Trigger); + InputComponent->BindAction("LibretroTriggerRight", IE_Released, this, &ALibretroVRPawn::Trigger); + } +}; diff --git a/Source/UnrealLibretro/Public/UnrealLibretro.h b/Source/UnrealLibretro/Public/UnrealLibretro.h index 355fcbfb..c8202ae9 100644 --- a/Source/UnrealLibretro/Public/UnrealLibretro.h +++ b/Source/UnrealLibretro/Public/UnrealLibretro.h @@ -1,9 +1,25 @@ #pragma once #include "Interfaces/IPluginManager.h" +#include "UObject/NameTypes.h" UNREALLIBRETRO_API DECLARE_LOG_CATEGORY_EXTERN(Libretro, Log, All); +#if PLATFORM_WINDOWS && PLATFORM_64BITS +# define PLATFORM_INDEX 0 +#elif PLATFORM_ANDROID_ARM +# define PLATFORM_INDEX 1 +#elif PLATFORM_ANDROID_ARM64 +# define PLATFORM_INDEX 2 +#endif + +static const struct { FString DistributionPath; FString Extension; FString BuildbotPath; FName ImageName; } CoreLibMetadata[] = +{ + { TEXT("Win64/"), "_libretro.dll", "https://buildbot.libretro.com/nightly/windows/x86_64/latest/" , "Launcher.Platform_Windows.Large" }, + { TEXT("Android/armeabi-v7a/"), "_libretro_android.so", "https://buildbot.libretro.com/nightly/android/latest/armeabi-v7a/", "Launcher.Platform_Android.Large" }, + { TEXT("Android/arm64-v8a/"), "_libretro_android.so", "https://buildbot.libretro.com/nightly/android/latest/arm64-v8a/" , "Launcher.Platform_Android.Large" }, +}; + class FUnrealLibretroModule : public IModuleInterface { public: @@ -14,6 +30,11 @@ class FUnrealLibretroModule : public IModuleInterface /** Path Resolution */ + static bool IsCoreName(const FString &CorePath) + { + return FPaths::GetExtension(CorePath).IsEmpty() && !CorePath.IsEmpty() && FPaths::GetPath(CorePath).IsEmpty(); + } + template static FString IfRelativeResolvePathRelativeToThisPluginWithPathExtensions(const FString& Path, PathTypes&&... InPaths) { @@ -27,18 +48,9 @@ class FUnrealLibretroModule : public IModuleInterface static FString ResolveCorePath(FString UnresolvedCorePath) { - if (FPaths::GetExtension(UnresolvedCorePath).IsEmpty()) { - UnresolvedCorePath = FString::Printf( -#if PLATFORM_ANDROID_ARM - TEXT("Android/armeabi-v7a/%s.so") -#elif PLATFORM_ANDROID_ARM64 - TEXT("Android/arm64-v8a/%s.so") -#elif PLATFORM_MAC - TEXT("Mac/%s.dylib") -#elif PLATFORM_WINDOWS && PLATFORM_64BITS - TEXT("Win64\\%s.dll") -#endif - , *UnresolvedCorePath); + if (IsCoreName(UnresolvedCorePath)) + { + UnresolvedCorePath = CoreLibMetadata[PLATFORM_INDEX].DistributionPath + *UnresolvedCorePath + CoreLibMetadata[PLATFORM_INDEX].Extension; } return IfRelativeResolvePathRelativeToThisPluginWithPathExtensions(UnresolvedCorePath, TEXT("MyCores")); @@ -66,3 +78,5 @@ class FUnrealLibretroModule : public IModuleInterface FString RedistDirectory; #endif }; + +#undef PLATFORM_INDEX \ No newline at end of file diff --git a/Source/UnrealLibretro/UnrealLibretro.Build.cs b/Source/UnrealLibretro/UnrealLibretro.Build.cs index 46b3b854..d5504445 100644 --- a/Source/UnrealLibretro/UnrealLibretro.Build.cs +++ b/Source/UnrealLibretro/UnrealLibretro.Build.cs @@ -46,8 +46,9 @@ public UnrealLibretro(ReadOnlyTargetRules Target) : base(Target) "CoreUObject", "Engine", "InputCore", - "Slate", "Projects", + "HeadMountedDisplay", + "NavigationSystem", } ); diff --git a/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.cpp b/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.cpp new file mode 100644 index 00000000..441b8279 --- /dev/null +++ b/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.cpp @@ -0,0 +1,797 @@ +// Slate documentation is practically non-existent... Your best bet is plaintext searching for symbols and Widget Reflector. +// I try to do most stuff inline as lambda's so it's easier to follow (less ctrl+click). This can be hazardous +// if you capture the wrong type of reference in a capture list though so keep that in mind + +#include "LibretroCoreInstanceDetails.h" +#include "LibretroInputDefinitions.h" +#include "UnrealLibretro.h" +#include "UnrealLibretroEditor.h" + +#include "CoreMinimal.h" +#include "PropertyHandle.h" +#include "DetailLayoutBuilder.h" +#include "SlateFwd.h" +#include "Widgets/Images/SImage.h" +#include "Widgets/Input/SEditableTextBox.h" +#include "Widgets/Input/SComboButton.h" +#include "Widgets/Input/SSearchBox.h" +#include "Framework/MultiBox/MultiBoxBuilder.h" +#include "Framework/Application/SlateApplication.h" +#include "IDetailPropertyRow.h" +#include "DetailCategoryBuilder.h" +#include "DetailWidgetRow.h" +#include "HttpModule.h" +#include "PlatformHttp.h" +#include "Interfaces/IHttpResponse.h" +#include "HAL/FileManager.h" +#include "Misc/FileHelper.h" + +TSharedRef FLibretroCoreInstanceDetails::MakeInstance() +{ + return MakeShareable(new FLibretroCoreInstanceDetails); +} + +void FLibretroOptions::OnOptionSelected(int32 Index) +{ + if (Parent->LibretroCoreInstance.IsValid()) + { + // If default is selected remove the key from core options since no key is implicit default + if (Index == 0) + { + if (Parent->LibretroCoreInstance->CoreOptions.Contains(Key)) + { + Parent->LibretroCoreInstance->CoreOptions.Remove(Key); + Parent->MarkEditorNeedsSave(); + } + } + // Otherwise add the selected option + else + { + Parent->LibretroCoreInstance->CoreOptions.Add(Key) = Values[Index]; + Parent->MarkEditorNeedsSave(); + } + } +} + +FText FLibretroOptions::GetSelectedOptionName() const +{ + if (Parent->LibretroCoreInstance.IsValid()) + { + FString *SelectedOptionString = Parent->LibretroCoreInstance->CoreOptions.Find(Key); + if (SelectedOptionString == nullptr) + { + return FText::FromString(Values[0]); + } + else + { + return FText::FromString(*SelectedOptionString); + } + } + + return FText::FromString(TEXT("Error")); +} + +TSharedRef FLibretroOptions::OnGenerateDropdownMenu() +{ + FMenuBuilder MenuBuilder(true, nullptr); + + for (int32 i = 0; i < Values.Num(); i++) + { + FUIAction ItemAction(FExecuteAction::CreateRaw(this, &FLibretroOptions::OnOptionSelected, i)); + MenuBuilder.AddMenuEntry(FText::FromString(Values[i]), TAttribute(), FSlateIcon(), ItemAction); + } + + return MenuBuilder.MakeWidget(); +} + +void FLibretroCoreInstanceDetails::RefreshCoreListViews() +{ + const FText& InSearchText = SearchBox->GetText(); + CoreAlreadyDownloadedListViewRows.Empty(); + CoreAvailableOnBuildbotListViewRows.Empty(); + auto& CoreListViewDataSource = FModuleManager::GetModuleChecked("UnrealLibretroEditor").CoreListViewDataSource; + for (auto& kv : CoreListViewDataSource) + { + if (kv.Key.Contains(InSearchText.ToString(), ESearchCase::IgnoreCase) || InSearchText.IsEmpty()) + { + if (kv.Value.PlatformDownloadedBitField + || kv.Value.DownloadsPending > 0) + { + this->CoreAlreadyDownloadedListViewRows.Add(MakeShared(FText::FromString(kv.Key))); + } + // Add to both lists if only a subset of the available cores for available platforms are in MyCores + if (kv.Value.PlatformAvailableBitField & ~kv.Value.PlatformDownloadedBitField + && kv.Value.DownloadsPending == 0) + { + this->CoreAvailableOnBuildbotListViewRows.Add(MakeShared(FText::FromString(kv.Key))); + } + } + } + + this->CoreAlreadyDownloadedListView->RequestListRefresh(); + this->CoreAvailableOnBuildbotListView->RequestListRefresh(); +} + +void FLibretroCoreInstanceDetails::StartBuildbotBatchDownload(TSharedPtr CoreIdentifierText, ESelectInfo::Type) +{ + if (!CoreIdentifierText.IsValid()) return; + + FString CoreIdentifier = CoreIdentifierText->ToString(); + auto& CoreListViewDataSource = FModuleManager::GetModuleChecked("UnrealLibretroEditor").CoreListViewDataSource; + + for (int PlatformIndex = 0; PlatformIndex < sizeof(CoreLibMetadata) / sizeof(CoreLibMetadata[0]); PlatformIndex++) + { + // Only start downloads for platforms that have cores available + if (~CoreListViewDataSource[CoreIdentifier].PlatformAvailableBitField & (1UL << PlatformIndex)) continue; + + CoreListViewDataSource[CoreIdentifier].DownloadsPending++; + FString CoreBuildbotFilename = CoreIdentifier + CoreLibMetadata[PlatformIndex].Extension + TEXT(".zip"); + auto BuildbotCoreDownloadRequest = FHttpModule::Get().CreateRequest(); + BuildbotCoreDownloadRequest->SetVerb(TEXT("GET")); + BuildbotCoreDownloadRequest->SetURL(CoreLibMetadata[PlatformIndex].BuildbotPath + CoreBuildbotFilename); + + BuildbotCoreDownloadRequest->OnProcessRequestComplete().BindLambda( + [this, PlatformIndex, CoreIdentifier](FHttpRequestPtr HttpRequest, FHttpResponsePtr HttpResponse, bool bSucceeded) + { + if (bSucceeded) + { + uint8* UnzippedData; + size_t UnzippedDataSize; + const char* ErrorString = FUnrealLibretroEditorModule::UnzipArchive(HttpResponse->GetContent(), + &UnzippedData, &UnzippedDataSize); + + if (ErrorString) + { + UE_LOG(Libretro, Warning, TEXT("Unzipping of '%s' failed: %s"), *HttpRequest->GetURL(), *FString(ErrorString)); + } + else + { + auto UnzippedDataArray = TArrayView(UnzippedData, UnzippedDataSize); + auto CoreSavePath = FUnrealLibretroModule::ResolveCorePath(CoreLibMetadata[PlatformIndex].DistributionPath + + CoreIdentifier + CoreLibMetadata[PlatformIndex].Extension); + + if (!FFileHelper::SaveArrayToFile(UnzippedDataArray, *CoreSavePath)) + { + UE_LOG(Libretro, Warning, TEXT("Failed to save downloaded core to path '%s': %s"), *HttpRequest->GetURL(), *FString(ErrorString)); + } + + free(UnzippedData); + FFunctionGraphTask::CreateAndDispatchWhenReady([=]() + { + auto& CoreListViewDataSource = FModuleManager::GetModuleChecked("UnrealLibretroEditor").CoreListViewDataSource; + CoreListViewDataSource[CoreIdentifier].PlatformDownloadedBitField |= (1UL << PlatformIndex); + + if (--CoreListViewDataSource[CoreIdentifier].DownloadsPending == 0) + { + RefreshCoreListViews(); + } + }, TStatId(), nullptr, ENamedThreads::GameThread); + } + } + else + { + UE_LOG(Libretro, Warning, TEXT("Couldn't download core from %s"), *HttpRequest->GetURL()); + } + }); + + BuildbotCoreDownloadRequest->ProcessRequest(); + } + + // Move the row we started downloading to the MyCores Table + RefreshCoreListViews(); +} + +static int32 n = 0; +static FLibretroOptions static_LibretroOptions[100]; + +static TStaticArray, PortCount> static_ControllerDescriptions; + +static bool core_environment(unsigned cmd, void* data) +{ + switch (cmd) { + case RETRO_ENVIRONMENT_SET_VARIABLES: { + const struct retro_variable* arr_var = (const struct retro_variable*)data; + + for (n = 0; n < sizeof(static_LibretroOptions) / sizeof(static_LibretroOptions[0]); n++, arr_var++) { + if (arr_var->key == nullptr) break; + + // Example entry: + // { .key = "foo_option", .value = "Speed hack coprocessor X; false|true" } + static_LibretroOptions[n].Key = arr_var->key; + + const char* base = arr_var->value; + const char* end = base; + + while (*end != ';') end++; + static_LibretroOptions[n].Description = FString(end - base, base); + end++; + + while (*end == ' ') end++; + + static_LibretroOptions[n].Values.Empty(); + for (;;) { + base = end; + while (*end != '|' && *end != '\0') end++; + + static_LibretroOptions[n].Values.Add(FString(end - base, base)); + if (*end == '\0') break; + end++; + } + } + + return true; + } + case RETRO_ENVIRONMENT_SET_CONTROLLER_INFO: { + auto controller_info = (const struct retro_controller_info*)data; + + for (int port = 0; controller_info[port].types != NULL && port < PortCount; port++) { + for (unsigned t = 0; t < controller_info[port].num_types; t++) { + if (controller_info[port].types[t].desc == nullptr) break; // Not part of Libretro API but needed check for some cores + retro_controller_description controller_description = controller_info[port].types[t]; + static_ControllerDescriptions[port].Add({ controller_description.desc, + controller_description.id }); + + UE_LOG(LogTemp, Verbose, TEXT("Supported Controllers: %s"), controller_description.id, UTF8_TO_TCHAR(controller_description.desc)); + } + } + + return true; + } + } + + return false; +} + +void FLibretroCoreInstanceDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) +{ + check(IsInGameThread()); // We access some static data, but as long as we're single threaded theres no data race here + + // This represents a hook to the Libretro Panel and let's us customize the UI elements within it + IDetailCategoryBuilder& LibretroCategory = DetailBuilder.EditCategory("Libretro"); + + TArray> ObjectsCustomized; + DetailBuilder.GetObjectsBeingCustomized(ObjectsCustomized); + if (ObjectsCustomized.Num() > 1) return; // Don't customize the GUI when multiple ULibretroCoreInstances are selected + + LibretroCoreInstance = CastChecked(ObjectsCustomized[0].Get()); + + static_ControllerDescriptions = TStaticArray, PortCount>{}; + n = 0; // To make sure we don't get stale options from the last selected core + FString RomPathValidExtensionsDelimited; // Contains a string like "bin|rom|iso" + if (LibretroCoreInstance.IsValid()) + { + // This next call will probably block for some time thus making the editor lag temporarily + // once it's in cache it should be plenty fast though and this is acceptable in the editor + void* core_handle = FPlatformProcess::GetDllHandle(*FUnrealLibretroModule::ResolveCorePath(LibretroCoreInstance->CorePath)); + if (core_handle != nullptr) { + void (*set_environment)(retro_environment_t) = (decltype(set_environment)) FPlatformProcess::GetDllExport(core_handle, TEXT("retro_set_environment")); + if (set_environment != nullptr) { + // We're hoping the core calls us immediately after setting this callback and gives us it's options + set_environment(core_environment); + ControllerDescriptions = MoveTemp(static_ControllerDescriptions); + } + + retro_system_info system_info = {0}; + void (*get_system_info)(retro_system_info*) = nullptr; + get_system_info = (decltype(get_system_info)) FPlatformProcess::GetDllExport(core_handle, TEXT("retro_get_system_info")); + if (get_system_info != nullptr) { + get_system_info(&system_info); + RomPathValidExtensionsDelimited = system_info.valid_extensions; + SelectedLibraryName = CoreLibraryName = system_info.library_name; + } + + FPlatformProcess::FreeDllHandle(core_handle); + } + } + + IDetailCategoryBuilder& ControllerCategory = DetailBuilder.EditCategory("Libretro Core Controllers"); + + auto &widget = ControllerCategory.AddCustomRow(FText::GetEmpty()) + .NameContent() + [ + SNew(STextBlock) + .Text(FText::FromString(TEXT("retro_system_info::library_name"))) + .Font(IDetailLayoutBuilder::GetDetailFont()) + ] + .ValueContent() + [ + SNew(SComboButton) + .ButtonContent() + [ + SNew(STextBlock) + .Text_Lambda([this]() + { + return FText::FromString(SelectedLibraryName); + }) + .Font(IDetailLayoutBuilder::GetDetailFont()) + ] + .OnGetMenuContent_Lambda([this]() + { + FMenuBuilder MenuBuilder(true, nullptr); + + if (LibretroCoreInstance.IsValid()) + { + TArray Keys; + LibretroCoreInstance->ControllersSetOnLaunch.GetKeys(Keys); + + // Make the CorePath core always selectable + if (!LibretroCoreInstance->ControllersSetOnLaunch.Contains(CoreLibraryName)) + { + Keys.Add(CoreLibraryName); + } + + for (auto &Key : Keys) + { + FUIAction ItemAction(FExecuteAction::CreateLambda([this, Key]{ SelectedLibraryName = Key; })); + MenuBuilder.AddMenuEntry(FText::FromString(Key), TAttribute(), FSlateIcon(), ItemAction); + } + } + + return MenuBuilder.MakeWidget(); + }) + ]; + + for (int Port = 0; Port < PortCount; Port++) + { + ControllerCategory.AddCustomRow(FText::GetEmpty()) +#if ENGINE_MAJOR_VERSION >= 5 + .OverrideResetToDefault(FResetToDefaultOverride::Create + ( + FIsResetToDefaultVisible::CreateLambda([Port, this](TSharedPtr CoreControllerProperty) + { + return LibretroCoreInstance.IsValid() + && SelectedLibraryName == CoreLibraryName + && LibretroCoreInstance->ControllersSetOnLaunch.Contains(SelectedLibraryName) + && LibretroCoreInstance->ControllersSetOnLaunch[SelectedLibraryName][Port].ID != RETRO_DEVICE_DEFAULT; + }), + FResetToDefaultHandler::CreateLambda([Port, this](TSharedPtr CoreControllerProperty) + { + if (LibretroCoreInstance.IsValid()) + { + auto &ControllersSetOnLaunchForSelectedCore = LibretroCoreInstance->ControllersSetOnLaunch.FindChecked(SelectedLibraryName); + + // Set to unspecified default then try to set to specified one if there is one + ControllersSetOnLaunchForSelectedCore[Port] = FLibretroControllerDescription(); + for (auto& ControllerDescription : ControllerDescriptions[Port]) + { + if (ControllerDescription.ID == RETRO_DEVICE_DEFAULT) + { + ControllersSetOnLaunchForSelectedCore[Port] = ControllerDescription; + } + } + + RemoveControllersSetOnLaunchForSelectedCoreIfAllAreDefault(); + } + }) + )) +#endif + // Setting this makes it so our reset to default handlers are called I believe + .PropertyHandleList({ DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(ULibretroCoreInstance, ControllersSetOnLaunch)) }) + .NameContent() + .MinDesiredWidth(150.0f) + [ + SNew(STextBlock) + .Text(FText::FromString(FString::Format(TEXT("Port {0}"), { Port }))) + .Font(IDetailLayoutBuilder::GetDetailFont()) + ] + .ValueContent() + [ + SNew(SComboButton).IsEnabled_Lambda([this]() { return CoreLibraryName == SelectedLibraryName; }) + .OnGetMenuContent_Lambda([this, Port]() + { + FMenuBuilder MenuBuilder(true, nullptr); + + for (int32 j = 0; j < ControllerDescriptions[Port].Num(); j++) + { + FUIAction ItemAction(FExecuteAction::CreateLambda([this, RowPort = Port, j] + { + // Early return if we're not actually changing selected controllers for a port + if ( !LibretroCoreInstance.IsValid() + || !LibretroCoreInstance->ControllersSetOnLaunch.Contains(SelectedLibraryName) + && ControllerDescriptions[RowPort][j].ID == RETRO_DEVICE_DEFAULT + || LibretroCoreInstance->ControllersSetOnLaunch.Contains(SelectedLibraryName) + && ControllerDescriptions[RowPort][j].ID == LibretroCoreInstance->ControllersSetOnLaunch[SelectedLibraryName][RowPort].ID) + { + return; + } + + FLibretroControllerDescriptions &ControllersSetOnLaunchForCore = LibretroCoreInstance->ControllersSetOnLaunch.FindOrAdd(SelectedLibraryName); + + ControllersSetOnLaunchForCore[RowPort] = ControllerDescriptions[RowPort][j]; + + // Copy over whatever special names the Core uses for the default + for (int Port = 0; Port < PortCount; Port++) + { + if (ControllersSetOnLaunchForCore[Port].ID != RETRO_DEVICE_DEFAULT) continue; + for (auto& ControllerDescription : ControllerDescriptions[Port]) + { + if (ControllerDescription.ID == RETRO_DEVICE_DEFAULT) + { + ControllersSetOnLaunchForCore[Port] = ControllerDescription; + } + } + } + + RemoveControllersSetOnLaunchForSelectedCoreIfAllAreDefault(); + + MarkEditorNeedsSave(); + })); + MenuBuilder.AddMenuEntry(FText::FromString(ControllerDescriptions[Port][j].Description), TAttribute(), FSlateIcon(), ItemAction); + } + + return MenuBuilder.MakeWidget(); + }) + .ContentPadding(FMargin(2.0f, 2.0f)) + .ButtonContent() + [ + SNew(STextBlock) + .Text_Lambda([this, Port]() + { + FLibretroControllerDescription DisplayControllerDescription; + + if (LibretroCoreInstance.IsValid()) + { + auto* ControllersSetOnLaunchForCore = LibretroCoreInstance->ControllersSetOnLaunch.Find(SelectedLibraryName); + + // If we have non-default controllers bound to a port + if (ControllersSetOnLaunchForCore) + { + DisplayControllerDescription = (*ControllersSetOnLaunchForCore)[Port]; + } + // Else relay the default description the core told us + else + { + for (auto& ControllerDescription : ControllerDescriptions[Port]) + { + if (ControllerDescription.ID == RETRO_DEVICE_DEFAULT) + { + DisplayControllerDescription = ControllerDescription; + } + } + } + } + + return FText::FromString(FString::Format(TEXT("{0} [ID={1}]"), { DisplayControllerDescription.Description, + DisplayControllerDescription.ID })); + }) + .Font(IDetailLayoutBuilder::GetDetailFont()) + ] + ]; + } + + // Construct Libreto Core Options Panel... It's directly under the Libretro section + IDetailCategoryBuilder& OptionsCategory = DetailBuilder.EditCategory("Libretro Core Options"); + for (int i = 0; i < n; i++) + { + static_LibretroOptions[i].Parent = this; + LibretroOptions.Add(static_LibretroOptions[i]); + + OptionsCategory.AddCustomRow(FText::GetEmpty()) +#if ENGINE_MAJOR_VERSION >= 5 + .OverrideResetToDefault(FResetToDefaultOverride::Create + ( + FIsResetToDefaultVisible::CreateLambda([i, this](TSharedPtr CoreOptionsOverrideProperty) + { + return LibretroCoreInstance.IsValid() ? LibretroCoreInstance->CoreOptions.Contains(LibretroOptions[i].Key) : false; + }), + FResetToDefaultHandler::CreateLambda([i, this](TSharedPtr CoreOptionsOverrideProperty) + { + if (LibretroCoreInstance.IsValid() && LibretroCoreInstance->CoreOptions.Contains(LibretroOptions[i].Key)) + { + LibretroCoreInstance->CoreOptions.FindAndRemoveChecked(LibretroOptions[i].Key); + } + }) + )) +#endif + // Setting this makes it so our reset to default handlers are called I believe + .PropertyHandleList({ DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(ULibretroCoreInstance, CoreOptions)) }) + .NameContent() + .MinDesiredWidth(150.0f) + [ + SNew(STextBlock) + .Text_Raw(&LibretroOptions[i], &FLibretroOptions::GetOptionDescription) + .Font(IDetailLayoutBuilder::GetDetailFont()) + ] + .ValueContent() + [ + SNew(SComboButton) + .OnGetMenuContent_Raw(&LibretroOptions[i], &FLibretroOptions::OnGenerateDropdownMenu) + .ContentPadding(FMargin(2.0f, 2.0f)) + .ButtonContent() + [ + SNew(STextBlock) + .Text_Raw(&LibretroOptions[i], &FLibretroOptions::GetSelectedOptionName) + .Font(IDetailLayoutBuilder::GetDetailFont()) + ] + ]; + } + + CorePathProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(ULibretroCoreInstance, CorePath)); + // Any time we modify the Core Path property we want to regenerate this entire UI since it determines our Libretro Options / Compatible cores + CorePathProperty->SetOnPropertyValueChanged(FSimpleDelegate::CreateRaw(&DetailBuilder, &IDetailLayoutBuilder::ForceRefreshDetails)); + + LibretroCategory.AddProperty("CorePath").CustomWidget() + .NameContent() + [ + SNew(STextBlock) + .Text_Lambda([this]() + { + FString CorePath; + verify(FPropertyAccess::Result::Success == CorePathProperty->GetValueAsFormattedString(CorePath)); + + if (FUnrealLibretroModule::IsCoreName(CorePath)) + { + return FText::FromString(TEXT("Core Name")); + } + else + { + return FText::FromString(TEXT("Core Path")); + } + }) + .Font(IDetailLayoutBuilder::GetDetailFont()) + ] + .ValueContent() + [ + SAssignNew(PresentBuildbotDropdownButton, SComboButton) + //.ToolTipText(FText::FromString(TEXT("Select a Core"))) + .OnGetMenuContent_Lambda([this]() + { + FMenuBuilder MenuBuilder(true, nullptr, TSharedPtr(), false, &FCoreStyle::Get(), true); + + // Add a search box to the top of the dropdown + MenuBuilder.AddWidget( + SNew(SBox) // Use this to coherce width + .WidthOverride(300.0f) + [ + SAssignNew(SearchBox, SSearchBox) + .DelayChangeNotificationsWhileTyping(true) + .OnTextChanged_Lambda([this](auto) { RefreshCoreListViews(); }) + ], + FText(), false); + + auto GenerateTableViewRow = [this](TSharedRef Item, const TSharedRef& OwnerTable) + { + const float SupportedPlatformImageWidthAndHeight = 48.f; + auto& CoreListViewDataSource = FModuleManager::GetModuleChecked("UnrealLibretroEditor").CoreListViewDataSource; + auto& RowDataSource = CoreListViewDataSource[Item.Get().ToString()]; + TSharedRef SupportedPlatformsImageList = SNew(SHorizontalBox); + + bool bForBuildbot = &OwnerTable.Get() == this->CoreAvailableOnBuildbotListView.Get(); //OwnerTable->GetId() == this->CoreAvailableOnBuildbotListView->GetId(); + for (int PlatformIndex = 0; PlatformIndex < sizeof(CoreLibMetadata) / sizeof(CoreLibMetadata[0]); PlatformIndex++) + { + if ( bForBuildbot && ~RowDataSource.PlatformAvailableBitField & (1UL << PlatformIndex)) continue; + if (!bForBuildbot && ~RowDataSource.PlatformDownloadedBitField & (1UL << PlatformIndex)) continue; + + SupportedPlatformsImageList->AddSlot() + .MaxWidth(SupportedPlatformImageWidthAndHeight) + [ + SNew(SImage) + .Image(FEditorStyle::GetBrush(CoreLibMetadata[PlatformIndex].ImageName)) + .ToolTipText(FText::FromString(bForBuildbot ? CoreLibMetadata[PlatformIndex].BuildbotPath + : CoreLibMetadata[PlatformIndex].DistributionPath)) + ]; + } + + FText RowText = FText::FromString(FString(RowDataSource.DownloadsPending == 0 ? "" : "Downloading... ") + Item.Get().ToString()); + return + SNew(STableRow< TSharedRef >, OwnerTable) + [ + SNew(SBox) // Use this to coherce height + .HAlign(HAlign_Fill) + //.VAlign(VAlign_Center) + .HeightOverride(SupportedPlatformImageWidthAndHeight) + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .VAlign(VAlign_Center) + [ + SNew(STextBlock) + .Text(RowText) + .Margin(2.f) + ] + + SHorizontalBox::Slot() + .VAlign(VAlign_Center) + [ + SupportedPlatformsImageList + ] + ] + ]; + }; + + MenuBuilder.BeginSection("MyCoresSection", FText::FromString(TEXT("MyCores"))); + MenuBuilder.AddWidget( + SNew(SBox) // Use this to coherce height + .MaxDesiredHeight(300.f) + .Padding(4.f) + [ + SAssignNew(CoreAlreadyDownloadedListView, SListView>) + .ListItemsSource(&this->CoreAlreadyDownloadedListViewRows) + .SelectionMode(ESelectionMode::Single) + .OnGenerateRow_Lambda(GenerateTableViewRow) + .OnSelectionChanged_Lambda([this](TSharedPtr NewValue, ESelectInfo::Type) + { + auto& CoreListViewDataSource = FModuleManager::GetModuleChecked("UnrealLibretroEditor").CoreListViewDataSource; + auto& RowDataSource = CoreListViewDataSource[NewValue->ToString()]; + + if (RowDataSource.DownloadsPending == 0) + { + CorePathProperty->SetValueFromFormattedString(NewValue->ToString()); + this->PresentBuildbotDropdownButton->SetIsOpen(false); // Close presented dropdown + } + + }) + ], FText(), false); + + MenuBuilder.EndSection(); + MenuBuilder.BeginSection("BuildbotLibretroComSection", FText::FromString(TEXT("buildbot.libretro.com"))); + + MenuBuilder.AddWidget( + SNew(SBox) // Use this to coherce height + .MaxDesiredHeight(300.f) + .Padding(4.f) + [ + SAssignNew(CoreAvailableOnBuildbotListView, SListView>) + .ListItemsSource(&this->CoreAvailableOnBuildbotListViewRows) + .SelectionMode(ESelectionMode::Single) + .ItemHeight(30.f) + .OnGenerateRow_Lambda(GenerateTableViewRow) + .OnSelectionChanged(this, &FLibretroCoreInstanceDetails::StartBuildbotBatchDownload) + ], + FText(), false + ); + + //FUIAction ItemAction(FExecuteAction::CreateLambda([]() {})); + //MenuBuilder.AddMenuEntry(FText::FromString(TEXT("Number 9 Large")), TAttribute(), FSlateIcon(), ItemAction); + MenuBuilder.EndSection(); + + RefreshCoreListViews(); + + // Copied boilerplate that focuses the keyboard on the search box when the drop down is presented + TSharedRef CoreBuildbotDropdownWidget = MenuBuilder.MakeWidget(); + CoreBuildbotDropdownWidget->RegisterActiveTimer(0.f, FWidgetActiveTimerDelegate::CreateLambda([this](double InCurrentTime, float InDeltaTime) + { + if (SearchBox.IsValid()) + { + FWidgetPath WidgetToFocusPath; + FSlateApplication::Get().GeneratePathToWidgetUnchecked(SearchBox.ToSharedRef(), WidgetToFocusPath); + FSlateApplication::Get().SetKeyboardFocus(WidgetToFocusPath, EFocusCause::SetDirectly); + WidgetToFocusPath.GetWindow()->SetWidgetToFocusOnActivate(SearchBox); + + return EActiveTimerReturnType::Stop; + } + + return EActiveTimerReturnType::Continue; + })); + + + return CoreBuildbotDropdownWidget; + }) + .ContentPadding(FMargin(2.0f, 2.0f)) + .ButtonContent() + [ + SNew(SEditableTextBox) + //.MinDesiredWidth(InArgs._MinDesiredValueWidth) + .RevertTextOnEscape(true) + .SelectAllTextWhenFocused(true) + .Text_Lambda([this]() + { + //return FText::FromString(this->LibretroCoreInstance->CorePath); // This causes a segfault not sure why exactly + FText CorePath; + if (FPropertyAccess::Result::Success == CorePathProperty->GetValueAsDisplayText(CorePath)) + { + return CorePath; + } + else + { + return FText::FromString(""); + } + }) + .OnTextCommitted_Lambda([this](const FText& NewValue, ETextCommit::Type) + { + CorePathProperty->SetValueFromFormattedString(NewValue.ToString()); + }) + ] + ]; + + TArray ValidExtensions; + RomPathValidExtensionsDelimited.ParseIntoArray(ValidExtensions, TEXT("|"), false); + + TSharedRef RomPathProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(ULibretroCoreInstance, RomPath)); + FDetailWidgetRow& RomPathWidget = LibretroCategory.AddProperty("RomPath").CustomWidget(); + + FText LeftRomPathText = FText::FromString(TEXT("Rom Path [") + RomPathValidExtensionsDelimited + TEXT("]")); + FText RomPathText; + RomPathProperty->GetValueAsFormattedText(RomPathText); + RomPathWidget + .PropertyHandleList({ RomPathProperty }) + .NameContent() + [ + SNew(STextBlock) + .Text(LeftRomPathText) + .ToolTipText(LeftRomPathText) + .Font(IDetailLayoutBuilder::GetDetailFont()) + ] + .ValueContent() + [ + SAssignNew(PresentRomListDropdownButton, SComboButton) + .OnGetMenuContent_Lambda([this, ValidExtensions, RomPathProperty]() + { + FMenuBuilder MenuBuilder(true, nullptr, TSharedPtr(), false, &FCoreStyle::Get(), true); + + auto MyROMsPath = FUnrealLibretroModule::ResolveROMPath(""); + TArray RomPaths; + + IFileManager::Get().FindFiles(RomPaths, *MyROMsPath); + + // Remove all ROMs with an incompatible extension... FilterByPredicate is O(n) and simple + RomPaths = RomPaths.FilterByPredicate([&](FString RomPath) { + return ValidExtensions.ContainsByPredicate([&](FString ValidExtension) { + return ValidExtension.Compare(FPaths::GetExtension(RomPath), ESearchCase::IgnoreCase) == 0; }); + }); + + RomPathsText.Empty(); + for (auto& Path : RomPaths) + { + RomPathsText.Add(MakeShared(FText::FromString(FPaths::GetCleanFilename(Path)))); + } + + MenuBuilder.AddWidget( + SNew(SBox) // Use this to coherce height + .MaxDesiredHeight(300.f) + .Padding(4.f) + [ + SNew(SListView>) + .ListItemsSource(&RomPathsText) + .SelectionMode(ESelectionMode::Single) + .OnGenerateRow_Lambda([this](TSharedRef Item, const TSharedRef& OwnerTable) + { + return SNew(STableRow< TSharedRef >, OwnerTable) + [ + SNew(SBox) + .HAlign(HAlign_Fill) + //.VAlign(VAlign_Center) + .HeightOverride(30.f) + [ + SNew(STextBlock) + .Text(Item.Get()) + .Margin(2.f) + ] + ]; + }) + .OnSelectionChanged_Lambda([this, RomPathProperty](TSharedPtr NewValue, ESelectInfo::Type) + { + RomPathProperty->SetValueFromFormattedString(NewValue.Get()->ToString()); + this->PresentRomListDropdownButton->SetIsOpen(false); // Close presented dropdown + }) + ], FText(), false); + + return MenuBuilder.MakeWidget(); + }) + .ToolTipText(RomPathText) + .ButtonContent() + [ + SNew(SEditableTextBox) + .Text(RomPathText) + .Font(IDetailLayoutBuilder::GetDetailFont()) + .OnTextCommitted_Lambda([RomPathProperty](const FText& NewText, ETextCommit::Type CommitType) + { + RomPathProperty->SetValueFromFormattedString(NewText.ToString()); + }) + .ForegroundColor_Lambda([RomPathProperty, ValidExtensions]() + { + FString RomPath; + RomPathProperty->GetValueAsFormattedString(RomPath); + + bool bExtensionIsValid = ValidExtensions.ContainsByPredicate([&](FString Extension) + { return Extension.Compare(FPaths::GetExtension(RomPath), ESearchCase::IgnoreCase) == 0; }); + + if (bExtensionIsValid && FPaths::FileExists(FUnrealLibretroModule::ResolveROMPath(RomPath))) + { + return FSlateColor::UseForeground(); + } + + return FSlateColor(FLinearColor(1.0f, 0.5f, 0.0f)); + }) + ] + ]; +} \ No newline at end of file diff --git a/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.h b/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.h new file mode 100644 index 00000000..fd414ab1 --- /dev/null +++ b/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.h @@ -0,0 +1,100 @@ +#pragma once + +#include "LibretroCoreInstance.h" + +#include "CoreMinimal.h" +#include "Engine/Engine.h" +#include "Algo/AllOf.h" +#include "Layout/Visibility.h" +#include "Widgets/SWidget.h" +#include "Widgets/Views/SListView.h" +#include "IDetailCustomization.h" +#include "Misc/NotifyHook.h" + +class IPropertyHandle; +class SComboButton; + +struct FLibretroOptions +{ + class FLibretroCoreInstanceDetails* Parent; + + FString Key; + FString Description; + TArray Values; + + FText GetSelectedOptionName() const; + FText GetOptionDescription() const { return FText::FromString(Description); } + void OnOptionSelected(int32 Index); + TSharedRef OnGenerateDropdownMenu(); +}; + + +/** + * @brief Editor custom GUI code for the LibretroCoreInstance type + * + * Features: + * - Set Core options through editor GUI + * - Set controllers for each port + * - Download Cores from buildbot.libretro.com automatically for each platform + * - Toggle label between "Core Path" and "Core Name" depending on the CorePath string set + * - Pick compatible ROM's from dropdown for current core + */ +class FLibretroCoreInstanceDetails : public IDetailCustomization +{ + +public: + /** Makes a new instance of this detail layout class for a specific detail view requesting it */ + static TSharedRef MakeInstance(); + + TSharedPtr CorePathProperty; + + TArray> CoreAlreadyDownloadedListViewRows; + TArray> CoreAvailableOnBuildbotListViewRows; + + TSharedPtr>> CoreAlreadyDownloadedListView; + TSharedPtr>> CoreAvailableOnBuildbotListView; + + TArray> RomPathsText; + + TSharedPtr PresentBuildbotDropdownButton; + TSharedPtr PresentRomListDropdownButton; + TSharedPtr SearchBox; + + void RefreshCoreListViews(); + void StartBuildbotBatchDownload(TSharedPtr CoreIdentifier, ESelectInfo::Type); + + /** IDetailCustomization interface */ + virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override; + + TArray> LibretroOptions; + TStaticArray, PortCount> ControllerDescriptions; + + TWeakObjectPtr LibretroCoreInstance; + FString CoreLibraryName; + FString SelectedLibraryName; + + FORCEINLINE_DEBUGGABLE void RemoveControllersSetOnLaunchForSelectedCoreIfAllAreDefault() + { + check(LibretroCoreInstance.IsValid()); + + // If all controllers were set to default no longer bookkeep bound controllers since its not necessary + // The important side effect here is that it is reflected to the user in the gui since they'll only care + // to view which cores are configured with non-default controllers + if (Algo::AllOf(LibretroCoreInstance->ControllersSetOnLaunch[SelectedLibraryName].ControllerDescription, + [](auto& D) { return D.ID == RETRO_DEVICE_DEFAULT; })) + { + LibretroCoreInstance->ControllersSetOnLaunch.Remove(SelectedLibraryName); + } + } + + FORCEINLINE_DEBUGGABLE void MarkEditorNeedsSave() + { + check(LibretroCoreInstance.IsValid()); + +#if ENGINE_MAJOR_VERSION >= 5 || ENGINE_MINOR_VERSION >= 26 + LibretroCoreInstance->GetPackage()->SetDirtyFlag(true); +#else + LibretroCoreInstance->MarkPackageDirty(); +#endif + } +}; \ No newline at end of file diff --git a/Source/UnrealLibretroEditor/Private/UnrealLibretroEditor.cpp b/Source/UnrealLibretroEditor/Private/UnrealLibretroEditor.cpp new file mode 100644 index 00000000..938df7ce --- /dev/null +++ b/Source/UnrealLibretroEditor/Private/UnrealLibretroEditor.cpp @@ -0,0 +1,109 @@ +#include "UnrealLibretroEditor.h" +#include "LibretroCoreInstanceDetails.h" + +#include "Modules/ModuleManager.h" +#include "PropertyEditorModule.h" +#include "DetailCustomizations.h" +#include "HttpModule.h" +#include "PlatformHttp.h" +#include "Interfaces/IHttpResponse.h" + +#include "miniz.h" + +#define LOCTEXT_NAMESPACE "FUnrealLibretroEditorModule" + +const char* FUnrealLibretroEditorModule::UnzipArchive(const TArray& ZippedData, uint8** UnzippedData, size_t* UnzippedDataSize) +{ + mz_zip_archive zip_archive = {0}; + + if (!mz_zip_reader_init_mem(&zip_archive, ZippedData.GetData(), ZippedData.Num(), 0)) goto cleanup; + + // Custom failure case we expect only 1 file in the archive + if (mz_zip_reader_get_num_files(&zip_archive) != 1) + { + mz_zip_reader_end(&zip_archive); + return "Zip file contains more than one file"; + } + + mz_zip_archive_file_stat file_stat; + if (!mz_zip_reader_file_stat(&zip_archive, 0, &file_stat)) goto cleanup; + + // Actual decompression happens here + *UnzippedDataSize = file_stat.m_uncomp_size; + *UnzippedData = (uint8 *) mz_zip_reader_extract_file_to_heap(&zip_archive, file_stat.m_filename, UnzippedDataSize, 0); + if (*UnzippedData == nullptr) goto cleanup; + +cleanup: + mz_zip_error zip_error = mz_zip_get_last_error(&zip_archive); + + // Close the archive, freeing any resources it was using + mz_zip_reader_end(&zip_archive); + + return zip_error == MZ_ZIP_NO_ERROR ? nullptr : mz_zip_get_error_string(zip_error); +} + + +void FUnrealLibretroEditorModule::StartupModule() +{ + // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module + FPropertyEditorModule& PropertyModule = FModuleManager::GetModuleChecked(FName("PropertyEditor")); + PropertyModule.RegisterCustomClassLayout("LibretroCoreInstance", FOnGetDetailCustomizationInstance::CreateStatic(&FLibretroCoreInstanceDetails::MakeInstance)); + + PropertyModule.NotifyCustomizationModuleChanged(); + + // Download Libretro Core list for each platform + for (int PlatformIndex = 0; PlatformIndex < sizeof(CoreLibMetadata) / sizeof(CoreLibMetadata[0]); PlatformIndex++) + { + auto BuildbotRequest = FHttpModule::Get().CreateRequest(); + BuildbotRequest->SetVerb(TEXT("GET")); + BuildbotRequest->SetURL(CoreLibMetadata[PlatformIndex].BuildbotPath + TEXT(".index-extended")); + + BuildbotRequest->OnProcessRequestComplete().BindLambda( + [this, PlatformIndex](FHttpRequestPtr HttpRequest, FHttpResponsePtr HttpResponse, bool bSucceeded) + { + if (bSucceeded) + { + check(IsInGameThread()); // Make sure we're handling the result on the GameThread because we're modifying datastructures that Slate uses too + + // Each row in this array looks something like this "2022-10-21 bb0926bb boom3_libretro.dll.zip" + // CoreIdentifer = "boom3" for this example + TArray CoreIndexRows; + HttpResponse->GetContentAsString().ParseIntoArrayLines(CoreIndexRows); + + for (auto CoreIndexRow : CoreIndexRows) + { + TArray CoreIndexColumnEntries; + CoreIndexRow.ParseIntoArrayWS(CoreIndexColumnEntries); + + auto CoreIdentiferEnd = CoreIndexColumnEntries.Last().Find(CoreLibMetadata[PlatformIndex].Extension, ESearchCase::CaseSensitive); + auto CoreIdentifer = FString(CoreIdentiferEnd, *CoreIndexColumnEntries.Last()); + CoreListViewDataSource.FindOrAdd(CoreIdentifer).PlatformAvailableBitField |= (1UL << PlatformIndex); + + // The logic here is naive I make no attempt to periodically poll the directory for external file creation/deletion + auto RelativeCorePath = CoreLibMetadata[PlatformIndex].DistributionPath + CoreIdentifer + CoreLibMetadata[PlatformIndex].Extension; + if (FPaths::FileExists(FUnrealLibretroModule::ResolveCorePath(RelativeCorePath))) + { + CoreListViewDataSource[CoreIdentifer].PlatformDownloadedBitField |= (1UL << PlatformIndex); + } + } + } + else + { + UE_LOG(Libretro, Warning, TEXT("Failed to get core list for platform")); + } + }); + + BuildbotRequest->ProcessRequest(); + } +} + +void FUnrealLibretroEditorModule::ShutdownModule() +{ + FPropertyEditorModule& PropertyModule = FModuleManager::GetModuleChecked("PropertyEditor"); + PropertyModule.UnregisterCustomClassLayout("LibretroCoreInstance"); + PropertyModule.NotifyCustomizationModuleChanged(); +} + +#undef LOCTEXT_NAMESPACE + +IMPLEMENT_MODULE(FUnrealLibretroEditorModule, UnrealLibretroEditor) diff --git a/Source/UnrealLibretroEditor/Public/UnrealLibretroEditor.h b/Source/UnrealLibretroEditor/Public/UnrealLibretroEditor.h new file mode 100644 index 00000000..75272099 --- /dev/null +++ b/Source/UnrealLibretroEditor/Public/UnrealLibretroEditor.h @@ -0,0 +1,29 @@ +#pragma once + +#include "UnrealLibretro.h" +#include "Modules/ModuleInterface.h" +#include "CoreMinimal.h" + +struct FCoreListViewRowDataSource +{ + uint32 PlatformAvailableBitField; + uint32 PlatformDownloadedBitField; + int32 DownloadsPending; +}; + +static_assert(sizeof(CoreLibMetadata) / sizeof(CoreLibMetadata[0]) < 8 * sizeof(FCoreListViewRowDataSource::PlatformAvailableBitField), + "PlatformAvailableBitField size is exceeded"); + +class FUnrealLibretroEditorModule : public IModuleInterface +{ +public: + TMap CoreListViewDataSource; + + // This should work for DEFLATE and gz2 but not much else I think + // Note: Supports only single file zips + static const char* UnzipArchive(const TArray& ZippedData, uint8** UnzippedData, size_t* UnzippedDataSize); + + /** IModuleInterface implementation */ + virtual void StartupModule() override; + virtual void ShutdownModule() override; +}; diff --git a/Source/UnrealLibretroEditor/UnrealLibretroEditor.Build.cs b/Source/UnrealLibretroEditor/UnrealLibretroEditor.Build.cs new file mode 100644 index 00000000..2b0fd356 --- /dev/null +++ b/Source/UnrealLibretroEditor/UnrealLibretroEditor.Build.cs @@ -0,0 +1,37 @@ +using System; +using UnrealBuildTool; + +// We need a separate module for editor stuff since it's prohibited by Unreal to package editor modules when packaging for distribution +public class UnrealLibretroEditor : ModuleRules +{ + public UnrealLibretroEditor(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + bEnforceIWYU = true; + + PrivateIncludePaths.AddRange( + new string[] + { + System.IO.Path.Combine(ModuleDirectory, "miniz"), + }); + + + PublicDependencyModuleNames.AddRange( + new string[] + { + "UnrealLibretro", + "Core", + "CoreUObject", + "Engine", + "InputCore", + + // Editor stuff + "EditorWidgets", + "EditorStyle", + "SlateCore", + "Slate", + "HTTP", + "Projects", + }); + } +} \ No newline at end of file diff --git a/UnrealLibretro.uplugin b/UnrealLibretro.uplugin index d017ffc9..60261e6c 100644 --- a/UnrealLibretro.uplugin +++ b/UnrealLibretro.uplugin @@ -19,7 +19,13 @@ "Name": "UnrealLibretro", "Type": "Runtime", "LoadingPhase": "Default", - "WhitelistPlatforms": [ "Win64", "Mac", "Android" ] + "WhitelistPlatforms": [ "Win64", "Android" ] + }, + { + "Name": "UnrealLibretroEditor", + "Type": "Runtime", + "LoadingPhase": "Default", + "WhitelistPlatforms": [ "Win64" ] } ] } \ No newline at end of file From 81ede17af9715b924f1fb127996bb2228c00374c Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Thu, 2 Feb 2023 17:40:54 -0800 Subject: [PATCH 09/79] Add buildbot.libretro.com certificate Older versions of Unreal need this --- Content/Certificates/cacert.pem | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 Content/Certificates/cacert.pem diff --git a/Content/Certificates/cacert.pem b/Content/Certificates/cacert.pem new file mode 100644 index 00000000..5ffb7223 --- /dev/null +++ b/Content/Certificates/cacert.pem @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFOTCCBN6gAwIBAgIQCVEl0L8aLtKKFM0pSNRsvzAKBggqhkjOPQQDAjBKMQsw +CQYDVQQGEwJVUzEZMBcGA1UEChMQQ2xvdWRmbGFyZSwgSW5jLjEgMB4GA1UEAxMX +Q2xvdWRmbGFyZSBJbmMgRUNDIENBLTMwHhcNMjIwNjA2MDAwMDAwWhcNMjMwNjA1 +MjM1OTU5WjB1MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQG +A1UEBxMNU2FuIEZyYW5jaXNjbzEZMBcGA1UEChMQQ2xvdWRmbGFyZSwgSW5jLjEe +MBwGA1UEAxMVc25pLmNsb3VkZmxhcmVzc2wuY29tMFkwEwYHKoZIzj0CAQYIKoZI +zj0DAQcDQgAEePTa/TYolHcb/b4Zo8ii1HjCXxJJj5cgK+nc4g0/RMEvv5iutRFh +flhSEG1LgArSNbMETO5+g2uC5XF4f9sFz6OCA3kwggN1MB8GA1UdIwQYMBaAFKXO +N+rrsHUOlGeItEX62SQQh5YfMB0GA1UdDgQWBBSocjRgpn4+Lxhq/rqczqLW2T6A +8TA+BgNVHREENzA1ghVzbmkuY2xvdWRmbGFyZXNzbC5jb22CDGxpYnJldHJvLmNv +bYIOKi5saWJyZXRyby5jb20wDgYDVR0PAQH/BAQDAgeAMB0GA1UdJQQWMBQGCCsG +AQUFBwMBBggrBgEFBQcDAjB7BgNVHR8EdDByMDegNaAzhjFodHRwOi8vY3JsMy5k +aWdpY2VydC5jb20vQ2xvdWRmbGFyZUluY0VDQ0NBLTMuY3JsMDegNaAzhjFodHRw +Oi8vY3JsNC5kaWdpY2VydC5jb20vQ2xvdWRmbGFyZUluY0VDQ0NBLTMuY3JsMD4G +A1UdIAQ3MDUwMwYGZ4EMAQICMCkwJwYIKwYBBQUHAgEWG2h0dHA6Ly93d3cuZGln +aWNlcnQuY29tL0NQUzB2BggrBgEFBQcBAQRqMGgwJAYIKwYBBQUHMAGGGGh0dHA6 +Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBABggrBgEFBQcwAoY0aHR0cDovL2NhY2VydHMu +ZGlnaWNlcnQuY29tL0Nsb3VkZmxhcmVJbmNFQ0NDQS0zLmNydDAMBgNVHRMBAf8E +AjAAMIIBfwYKKwYBBAHWeQIEAgSCAW8EggFrAWkAdwDoPtDaPvUGNTLnVyi8iWvJ +A9PL0RFr7Otp4Xd9bQa9bgAAAYE7JrvnAAAEAwBIMEYCIQDdXY3Qi6ezOnQpXlpW +WwvKbidIgj5shSTjsKLwSDEpQAIhAPX1/av7KCCovC3Zj8jBU27tPb2R1f/WoGLe +TvpSC7iuAHYANc8ZG7+xbFe/D61MbULLu7YnICZR6j/hKu+oA8M71kwAAAGBOya8 +AAAABAMARzBFAiEAosF1xZN7DT0OjeAaE4dujooC2uN1e6wjdPqJVrdEOjcCIH0N +FPl0qGPcF95YbuqMp+55yNjzQwFqoNB/n+u+P90fAHYAs3N3B+GEUPhjhtYFqdwR +CUp5LbFnDAuH3PADDnk2pZoAAAGBOya8RAAABAMARzBFAiBTnXq2iWMmI3VAZqW9 +0aCJJ3ySQFCLKQMu0rPHAD/yjAIhAI2+G4rkdzv9CWlHMZgl8p0LFcXbEU5Fx+Ht +4OmmIcK5MAoGCCqGSM49BAMCA0kAMEYCIQDz5L+8RkgQsDE0ReI8mCQXz18fdaAv +XqIeU+U4ktBFGgIhALdy1SFyPROeOlKGu9UhOGUFWo0xOu9CRjl0EHCvSI/j +-----END CERTIFICATE----- From 3db4338199cfa8021b78bec397f3121fc65b0d9b Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Fri, 17 Feb 2023 18:10:26 -0800 Subject: [PATCH 10/79] Add method of getting bound controllers on core --- .../Private/LibretroCoreInstance.cpp | 18 +++++++++++++ Source/UnrealLibretro/Private/sdlarch.cpp | 25 ++++++++++++++----- Source/UnrealLibretro/Private/sdlarch.h | 18 +++++++++---- .../Public/LibretroCoreInstance.h | 8 ++++++ Source/UnrealLibretro/Public/UnrealLibretro.h | 15 +++++++++++ .../Private/LibretroCoreInstanceDetails.cpp | 11 +------- 6 files changed, 74 insertions(+), 21 deletions(-) diff --git a/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp b/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp index ef75bc0c..4f01e2b4 100644 --- a/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp +++ b/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp @@ -19,6 +19,24 @@ ULibretroCoreInstance::ULibretroCoreInstance() PrimaryComponentTick.bCanEverTick = true; } +void ULibretroCoreInstance::GetController(int Port, int64& ID, FString& Description) +{ + NOT_LAUNCHED_GUARD + + // This if statement guards against a datarace on LibretroContext::ControllerDescriptions + if (CoreInstance.GetValue()->CoreState.load(std::memory_order_acquire) != LibretroContext::ECoreState::Starting) + { + ID = CoreInstance.GetValue()->devices[Port].load(std::memory_order_relaxed); + for (auto & ControllerDescription : CoreInstance.GetValue()->ControllerDescriptions[Port]) + { + if (ControllerDescription.ID == ID) + { + Description = ControllerDescription.Description; + break; + } + } + } +} void ULibretroCoreInstance::ConnectController(APlayerController* PlayerController, int Port, diff --git a/Source/UnrealLibretro/Private/sdlarch.cpp b/Source/UnrealLibretro/Private/sdlarch.cpp index 2b857ee4..3e303c41 100644 --- a/Source/UnrealLibretro/Private/sdlarch.cpp +++ b/Source/UnrealLibretro/Private/sdlarch.cpp @@ -447,6 +447,7 @@ static bool GLLogCall(const char* function, const char* file, int line) RHICmdList.EnqueueLambda([=](FRHICommandList&) { + // Potentially this should be a TryLock() so you don't preempt the render thread although it's unlikely that would happen FScopeLock UploadTextureToRenderHardware(&this->Unreal.FrameUpload.CriticalSection); GDynamicRHI->RHIUpdateTexture2D( this->Unreal.TextureRHI.GetReference(), @@ -764,6 +765,8 @@ bool LibretroContext::core_environment(unsigned cmd, void *data) { case RETRO_ENVIRONMENT_SET_CONTROLLER_INFO: { auto controller_info = (const struct retro_controller_info*)data; + FUnrealLibretroModule::EnvironmentParseControllerInfo(controller_info, ControllerDescriptions); + return true; } case RETRO_ENVIRONMENT_GET_PREFERRED_HW_RENDER: { @@ -833,6 +836,10 @@ void LibretroContext::core_audio_sample(int16_t left, int16_t right) { core_audio_write(buf, (size_t)1); } +void LibretroContext::set_controller_port_device(unsigned port, unsigned device) { + devices[port] = device; // The core doesn't keep track of this so we have to + _internal_set_controller_port_device(port, device); +} void LibretroContext::load(const char *sofile) { void (*set_environment)(retro_environment_t) = NULL; @@ -852,7 +859,6 @@ void LibretroContext::load(const char *sofile) { load_retro_sym(api_version); load_retro_sym(get_system_info); load_retro_sym(get_system_av_info); - load_retro_sym(set_controller_port_device); load_retro_sym(reset); load_retro_sym(run); load_retro_sym(load_game); @@ -863,6 +869,8 @@ void LibretroContext::load(const char *sofile) { load_retro_sym(serialize); load_retro_sym(unserialize); + load_sym(_internal_set_controller_port_device, retro_set_controller_port_device); + load_sym(set_environment, retro_set_environment); load_sym(set_video_refresh, retro_set_video_refresh); load_sym(set_input_poll, retro_set_input_poll); @@ -1001,20 +1009,20 @@ LibretroContext* LibretroContext::Launch(ULibretroCoreInstance* LibretroCoreInst auto* ControllerDescription = ControllersSetOnLaunch.Find(l->system.library_name); if (ControllerDescription) { - l->libretro_api.set_controller_port_device(Port, (*ControllerDescription)[Port].ID); + l->set_controller_port_device(Port, (*ControllerDescription)[Port].ID); } else { // This is needed for some cores nestopia specifically is one example // otherwise nothing will be bound for some reason - l->libretro_api.set_controller_port_device(Port, RETRO_DEVICE_DEFAULT); + l->set_controller_port_device(Port, RETRO_DEVICE_DEFAULT); } } // This does load the game but does many other things as well. If hardware rendering is needed it loads OpenGL resources from the OS and this also initializes the unreal engine resources for audio and video. l->load_game(TCHAR_TO_UTF8(*game)); - + l->CoreState.store(ECoreState::Running, std::memory_order_release); LoadedCallback(l, l->libretro_api); // This simplifies the logic in core_video_refresh, It stops us from erroring it doesn't error when we try to unmap this pixel buffer in core_video_refresh @@ -1148,8 +1156,13 @@ void LibretroContext::Shutdown(LibretroContext* Instance) void LibretroContext::Pause(bool ShouldPause) { - this->CoreState.store(ShouldPause ? ECoreState::Paused : ECoreState::Running, - std::memory_order_relaxed); + // We enqueue the state change because otherwise we might prematurely unset the Starting state + // Alternatively you could add one more state to accomplish this, but this is fine for now + EnqueueTask([=](auto&&) + { + CoreState.store(ShouldPause ? ECoreState::Paused : ECoreState::Running, std::memory_order_relaxed); + }); + } void LibretroContext::EnqueueTask(TUniqueFunction LibretroAPITask) diff --git a/Source/UnrealLibretro/Private/sdlarch.h b/Source/UnrealLibretro/Private/sdlarch.h index 3508211e..f0e243bb 100644 --- a/Source/UnrealLibretro/Private/sdlarch.h +++ b/Source/UnrealLibretro/Private/sdlarch.h @@ -93,7 +93,6 @@ struct libretro_api_t { unsigned (*api_version)(void); void (*get_system_info)(struct retro_system_info* info); void (*get_system_av_info)(struct retro_system_av_info* info); - void (*set_controller_port_device)(unsigned port, unsigned device); void (*reset)(void); void (*run)(void); size_t (*serialize_size)(void); @@ -139,22 +138,31 @@ struct LibretroContext { */ FLibretroInputState InputState[PortCount]; + std::atomic devices[PortCount]; + // Only safe to access from any thread once CoreState is not Starting + TStaticArray, PortCount> ControllerDescriptions; + struct retro_system_info system = { 0 }; + void set_controller_port_device(unsigned port, unsigned device); + TUniqueFunction::Type> CoreEnvironmentCallback; -protected: - LibretroContext() {} - ~LibretroContext() {} enum class ECoreState : int8 { + Starting, Running, Paused, Shutdown }; - std::atomic CoreState{ ECoreState::Running }; + std::atomic CoreState{ ECoreState::Starting }; + +protected: + LibretroContext() {} + ~LibretroContext() {} + void (*_internal_set_controller_port_device)(unsigned port, unsigned device); libretro_api_t libretro_api = { 0 }; struct libretro_callbacks_t* libretro_callbacks = nullptr; TQueue, EQueueMode::Spsc> LibretroAPITasks; // TQueue has acquire-release semantics on Enqueue and Dequeue so this should be thread-safe diff --git a/Source/UnrealLibretro/Public/LibretroCoreInstance.h b/Source/UnrealLibretro/Public/LibretroCoreInstance.h index 5cfdd782..14e55e2b 100644 --- a/Source/UnrealLibretro/Public/LibretroCoreInstance.h +++ b/Source/UnrealLibretro/Public/LibretroCoreInstance.h @@ -112,6 +112,14 @@ class UNREALLIBRETRO_API ULibretroCoreInstance : public UActorComponent UFUNCTION(BlueprintCallable, Category = "Libretro|IneffectiveBeforeLaunch") void Pause(bool ShouldPause = true); + /** + * @brief Effectively a getter version of `retro_set_controller_port_device` + * + * @see `retro_set_controller_port_device` in libretro.h + */ + UFUNCTION(BlueprintCallable, Category = "Libretro|IneffectiveBeforeCoreIsReady") + void GetController(int Port, int64 &ID, FString &Description); + /** * @brief Allows a user to control the Game. * diff --git a/Source/UnrealLibretro/Public/UnrealLibretro.h b/Source/UnrealLibretro/Public/UnrealLibretro.h index c8202ae9..86276b62 100644 --- a/Source/UnrealLibretro/Public/UnrealLibretro.h +++ b/Source/UnrealLibretro/Public/UnrealLibretro.h @@ -1,5 +1,6 @@ #pragma once +#include "LibretroCoreInstance.h" #include "Interfaces/IPluginManager.h" #include "UObject/NameTypes.h" @@ -71,6 +72,20 @@ class FUnrealLibretroModule : public IModuleInterface return IfRelativeResolvePathRelativeToThisPluginWithPathExtensions(UnresolvedSavePath, TEXT("Saves"), TEXT("SRAM"), FPaths::GetCleanFilename(UnresolvedRomPath)); } + static void EnvironmentParseControllerInfo(const retro_controller_info* controller_info, + TStaticArray, PortCount>& ControllerDescriptions) + { + for (int port = 0; controller_info[port].types != NULL && port < PortCount; port++) { + for (unsigned t = 0; t < controller_info[port].num_types; t++) { + if (controller_info[port].types[t].desc == nullptr) break; // Not part of Libretro API but needed check for some cores + + retro_controller_description controller_description = controller_info[port].types[t]; + ControllerDescriptions[port].Add({ controller_description.desc, + controller_description.id }); + } + } + } + private: void* SDLHandle; diff --git a/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.cpp b/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.cpp index 441b8279..d8200428 100644 --- a/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.cpp +++ b/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.cpp @@ -224,16 +224,7 @@ static bool core_environment(unsigned cmd, void* data) case RETRO_ENVIRONMENT_SET_CONTROLLER_INFO: { auto controller_info = (const struct retro_controller_info*)data; - for (int port = 0; controller_info[port].types != NULL && port < PortCount; port++) { - for (unsigned t = 0; t < controller_info[port].num_types; t++) { - if (controller_info[port].types[t].desc == nullptr) break; // Not part of Libretro API but needed check for some cores - retro_controller_description controller_description = controller_info[port].types[t]; - static_ControllerDescriptions[port].Add({ controller_description.desc, - controller_description.id }); - - UE_LOG(LogTemp, Verbose, TEXT("Supported Controllers: %s"), controller_description.id, UTF8_TO_TCHAR(controller_description.desc)); - } - } + FUnrealLibretroModule::EnvironmentParseControllerInfo(controller_info, static_ControllerDescriptions); return true; } From 3101a59dbd6b5c733065244989ddd9ae02b4bd78 Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Mon, 20 Feb 2023 14:05:14 -0800 Subject: [PATCH 11/79] Refactor some code using ChatGPT This is a tiny bit safer and more clear though less performant --- .../Private/LibretroCoreInstanceDetails.cpp | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.cpp b/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.cpp index d8200428..5ae9c996 100644 --- a/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.cpp +++ b/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.cpp @@ -192,31 +192,29 @@ static bool core_environment(unsigned cmd, void* data) case RETRO_ENVIRONMENT_SET_VARIABLES: { const struct retro_variable* arr_var = (const struct retro_variable*)data; - for (n = 0; n < sizeof(static_LibretroOptions) / sizeof(static_LibretroOptions[0]); n++, arr_var++) { + for (n = 0; n < sizeof(static_LibretroOptions) / sizeof(static_LibretroOptions[0]); arr_var++) { if (arr_var->key == nullptr) break; // Example entry: // { .key = "foo_option", .value = "Speed hack coprocessor X; false|true" } - static_LibretroOptions[n].Key = arr_var->key; - - const char* base = arr_var->value; - const char* end = base; + const FString Value = arr_var->value; - while (*end != ';') end++; - static_LibretroOptions[n].Description = FString(end - base, base); - end++; + // Find the position of the semicolon that separates the description and values + int32 SemicolonIndex; + if (!Value.FindChar(';', SemicolonIndex)) { + UE_LOG(Libretro, Warning, TEXT("Failed to parse libretro core option '%s'. No semicolon"), *Value); + continue; // Skip this Option + } - while (*end == ' ') end++; + // Extract the description substring + static_LibretroOptions[n].Description = Value.Left(SemicolonIndex); - static_LibretroOptions[n].Values.Empty(); - for (;;) { - base = end; - while (*end != '|' && *end != '\0') end++; + // Extract the values substring and split it into individual values + FString PipeDelimitedValueString = Value.Mid(SemicolonIndex + 1).TrimStart(); - static_LibretroOptions[n].Values.Add(FString(end - base, base)); - if (*end == '\0') break; - end++; - } + PipeDelimitedValueString.ParseIntoArray(static_LibretroOptions[n].Values, TEXT("|"), false); + static_LibretroOptions[n].Key = arr_var->key; + n++; } return true; From 17c94ce9056a7e44f7c94c9ac6e85461b2cdf098 Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Fri, 28 Apr 2023 19:36:43 -0700 Subject: [PATCH 12/79] Remove old input handling code --- .../LibretroBlueprintFunctionLibrary.cpp | 7 - .../Private/LibretroCoreInstance.cpp | 159 ++---------------- .../Public/LibretroBlueprintFunctionLibrary.h | 7 +- .../Public/LibretroCoreInstance.h | 70 +------- .../Public/LibretroInputDefinitions.h | 9 + 5 files changed, 29 insertions(+), 223 deletions(-) diff --git a/Source/UnrealLibretro/Private/LibretroBlueprintFunctionLibrary.cpp b/Source/UnrealLibretro/Private/LibretroBlueprintFunctionLibrary.cpp index 44c6f416..4c17de30 100644 --- a/Source/UnrealLibretro/Private/LibretroBlueprintFunctionLibrary.cpp +++ b/Source/UnrealLibretro/Private/LibretroBlueprintFunctionLibrary.cpp @@ -28,13 +28,6 @@ UActorComponent* ULibretroBlueprintFunctionLibrary::HasComponent(AActor* Actor, return Component; } -TMap ULibretroBlueprintFunctionLibrary::CombineInputMaps(const TMap& InMap1, const TMap& InMap2) -{ - auto OutMap(InMap1); - OutMap.Append(InMap2); - return OutMap; -} - bool ULibretroBlueprintFunctionLibrary::IsSupportUVFromHitResultsEnabledInConfig() { return UPhysicsSettings::Get()->bSupportUVFromHitResults; diff --git a/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp b/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp index 4f01e2b4..8b93260e 100644 --- a/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp +++ b/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp @@ -37,50 +37,6 @@ void ULibretroCoreInstance::GetController(int Port, int64& ID, FString& Descript } } } - -void ULibretroCoreInstance::ConnectController(APlayerController* PlayerController, - int Port, - TMap ControllerBindings, - FOnControllerDisconnected OnControllerDisconnected, - bool ForwardAllKeyboardInputToCoreIfPossible) -{ - check(Port >= 0 && Port < PortCount); - - DisconnectController(Port); - - Bindings[Port] = ControllerBindings; - Controller[Port] = MakeWeakObjectPtr(PlayerController); - Disconnected[Port] = OnControllerDisconnected; - - ForwardKeyboardInput[Port] = ForwardAllKeyboardInputToCoreIfPossible; -} - -void ULibretroCoreInstance::DisconnectController(int Port) -{ - check(Port >= 0 && Port < PortCount); - - if (Controller[Port].IsValid()) - { - if (CoreInstance.IsSet()) - { - CoreInstance.GetValue()->EnqueueTask([&InputStatePort = this->CoreInstance.GetValue()->InputState[Port]](libretro_api_t& libretro_api) - { - InputStatePort = FLibretroInputState(); - - if (libretro_api.keyboard_event) - { - for (int k = RETROK_FIRST; k < RETROK_LAST; k++) - { - libretro_api.keyboard_event(false, k, 0, RETROKMOD_NONE); - } - } - }); - } - - Disconnected[Port].ExecuteIfBound(Controller[Port].Get(), Port); - Controller[Port] = nullptr; - } -} void ULibretroCoreInstance::Launch() { @@ -299,11 +255,6 @@ void ULibretroCoreInstance::SetInputDigital(int Port, bool Pressed, ERetroDevice { NOT_LAUNCHED_GUARD - if (Controller[Port] != nullptr) -{ - UE_LOG(Libretro, Warning, TEXT("This function '%s' is incompatible with ConnectController this call will have no effect"), __func__); - } - CoreInstance.GetValue()->EnqueueTask([=, CoreInstance = CoreInstance.GetValue()](auto) { CoreInstance->InputState[Port][Input] = Pressed; @@ -314,114 +265,31 @@ void ULibretroCoreInstance::SetInputAnalog(int Port, int _16BitSignedInteger, ER { NOT_LAUNCHED_GUARD - if (Controller[Port] != nullptr) - { - UE_LOG(Libretro, Warning, TEXT("This function '%s' is incompatible with ConnectController this call will have no effect"), __func__); - } - CoreInstance.GetValue()->EnqueueTask([=, CoreInstance = CoreInstance.GetValue()](auto) { CoreInstance->InputState[Port][Input] = _16BitSignedInteger; }); } -void ULibretroCoreInstance::GunPullTrigger(int Port, FVector2D XY) -{ - NOT_LAUNCHED_GUARD - - CoreInstance.GetValue()->EnqueueTask([=, CoreInstance = CoreInstance.GetValue()](auto) - { - // I think mame wants both these to be set - CoreInstance->InputState[Port][ERetroDeviceID::PointerPressed] = true; - CoreInstance->InputState[Port][ERetroDeviceID::PointerCount] = 1; - - // Translate into libretro coordinate system [0.0, 1.0] -> [-0x7fff, 0x7fff] which is just a shift and scale the rounding is pedantic - CoreInstance->InputState[Port][ERetroDeviceID::PointerX] = FMath::RoundHalfToEven(0x7FFF * 2.f * (XY[0] - 0.5f)); - CoreInstance->InputState[Port][ERetroDeviceID::PointerY] = FMath::RoundHalfToEven(0x7FFF * 2.f * (XY[1] - 0.5f)); - }); -} - -void ULibretroCoreInstance::GunPullTriggerOffscreen(int Port) -{ - NOT_LAUNCHED_GUARD - - CoreInstance.GetValue()->EnqueueTask([=](auto) - { - CoreInstance.GetValue()->InputState[Port][ERetroDeviceID::LightgunIsOffscreen] = true; - CoreInstance.GetValue()->InputState[Port][ERetroDeviceID::PointerPressed] = false; - CoreInstance.GetValue()->InputState[Port][ERetroDeviceID::PointerCount] = 1; - }); -} - -void ULibretroCoreInstance::GunReleaseTrigger(int Port) -{ - NOT_LAUNCHED_GUARD - - CoreInstance.GetValue()->EnqueueTask([=](auto) - { - CoreInstance.GetValue()->InputState[Port][ERetroDeviceID::LightgunIsOffscreen] = false; - CoreInstance.GetValue()->InputState[Port][ERetroDeviceID::PointerPressed] = false; - CoreInstance.GetValue()->InputState[Port][ERetroDeviceID::PointerCount] = 0; - }); -} - void ULibretroCoreInstance::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) { -#if ENGINE_MAJOR_VERSION <= 4 && ENGINE_MINOR_VERSION <= 25 - #define IsAxis1D IsFloatAxis -#endif - - if (CoreInstance.IsSet()) + if ( CoreInstance.IsSet() + && KeyboardInputSourcePlayerController) { - for (int Port = 0; Port < PortCount; Port++) + for (int i = 0; i < count_key_bindings; i++) { - if (!Controller[Port].IsValid()) continue; - - FLibretroInputState NextInputState{}; - for (auto ControllerBinding : Bindings[Port]) - { - if (ControllerBinding.Key.IsAxis1D()) - { - // Both Y-Axes should be inverted because of Libretro convention however Unreal has a quirk where the Y-Axis of the right stick is inverted by default for some reason - // LX LY RX RY - float StickAxisInvert[] = { 1, -1, 1, 1 }; - int StickAxisInvertIndex = to_integral(ControllerBinding.Value) - to_integral(ERetroDeviceID::AnalogLeftX); - // Some cores support 0x7FFF to -0x7FFF others to -0x8000 - int16_t retro_value = FMath::RoundHalfToEven(StickAxisInvert[StickAxisInvertIndex] * 0x7FFF * Controller[Port]->PlayerInput->GetKeyValue(ControllerBinding.Key)); - NextInputState[ControllerBinding.Value] = FMath::Clamp(NextInputState[ControllerBinding.Value] + retro_value, -0x7FFF, 0x7FFF); - } - else - { - NextInputState[ControllerBinding.Value] |= static_cast(Controller[Port]->PlayerInput->IsPressed(ControllerBinding.Key)); - } - } - - if (Bindings[Port].Num() > 0) - { - CoreInstance.GetValue()->EnqueueTask([&InputStatePort = this->CoreInstance.GetValue()->InputState[Port], NextInputState](auto) - { - InputStatePort = NextInputState; - }); - } - - if (ForwardKeyboardInput[Port]) + if ( KeyboardInputSourcePlayerController->PlayerInput->WasJustPressed(key_bindings[i].Unreal) + || KeyboardInputSourcePlayerController->PlayerInput->WasJustReleased(key_bindings[i].Unreal)) { - for (int i = 0; i < count_key_bindings; i++) + this->CoreInstance.GetValue()->EnqueueTask( + [=, down = KeyboardInputSourcePlayerController->PlayerInput->WasJustPressed(key_bindings[i].Unreal)] + (libretro_api_t libretro_api) { - if ( Controller[Port]->PlayerInput->WasJustPressed(key_bindings[i].Unreal) - || Controller[Port]->PlayerInput->WasJustReleased(key_bindings[i].Unreal)) + if (libretro_api.keyboard_event) { - this->CoreInstance.GetValue()->EnqueueTask( - [=, down = Controller[Port]->PlayerInput->WasJustPressed(key_bindings[i].Unreal)] - (libretro_api_t libretro_api) - { - if (libretro_api.keyboard_event) - { - libretro_api.keyboard_event(down, key_bindings[i].libretro, 0, RETROKMOD_NONE); - } - }); + libretro_api.keyboard_event(down, key_bindings[i].libretro, 0, RETROKMOD_NONE); } - } + }); } } } @@ -429,11 +297,6 @@ void ULibretroCoreInstance::TickComponent(float DeltaTime, enum ELevelTick TickT void ULibretroCoreInstance::BeginDestroy() { - for (int Port = 0; Port < PortCount; Port++) - { - DisconnectController(Port); - } - if (this->CoreInstance.IsSet()) { // Save SRam diff --git a/Source/UnrealLibretro/Public/LibretroBlueprintFunctionLibrary.h b/Source/UnrealLibretro/Public/LibretroBlueprintFunctionLibrary.h index 7d483c69..3e139ee1 100644 --- a/Source/UnrealLibretro/Public/LibretroBlueprintFunctionLibrary.h +++ b/Source/UnrealLibretro/Public/LibretroBlueprintFunctionLibrary.h @@ -31,9 +31,6 @@ class UNREALLIBRETRO_API ULibretroBlueprintFunctionLibrary : public UBlueprintFu UFUNCTION(BlueprintCallable, Category = "Libretro|Util|Actor", meta = (ExpandEnumAsExecs = "Branch", DeterminesOutputType = "ComponentClass")) static UActorComponent* HasComponent(AActor* Actor, TSubclassOf ComponentClass, EBranchNames &Branch); - UFUNCTION(BlueprintPure, Category = "Libretro|Util|Map", meta = (BlueprintThreadSafe)) - static TMap CombineInputMaps(const TMap &InMap1, const TMap &InMap2); - - UFUNCTION(BlueprintPure, Category = "Libretro|Util") - static bool IsSupportUVFromHitResultsEnabledInConfig(); + UFUNCTION(BlueprintPure, Category = "Libretro|Util") + static bool IsSupportUVFromHitResultsEnabledInConfig(); }; diff --git a/Source/UnrealLibretro/Public/LibretroCoreInstance.h b/Source/UnrealLibretro/Public/LibretroCoreInstance.h index 14e55e2b..2b6527cf 100644 --- a/Source/UnrealLibretro/Public/LibretroCoreInstance.h +++ b/Source/UnrealLibretro/Public/LibretroCoreInstance.h @@ -33,9 +33,7 @@ struct FLibretroControllerDescriptions }; DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnCoreIsReady, const class UTextureRenderTarget2D*, LibretroFramebuffer, const class USoundWave*, AudioBuffer); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnCoreFramebufferResize); - -DECLARE_DYNAMIC_DELEGATE_TwoParams(FOnControllerDisconnected, const class APlayerController*, PlayerController, const int, Port); +DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnCoreFramebufferResize); UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) ) @@ -118,34 +116,7 @@ class UNREALLIBRETRO_API ULibretroCoreInstance : public UActorComponent * @see `retro_set_controller_port_device` in libretro.h */ UFUNCTION(BlueprintCallable, Category = "Libretro|IneffectiveBeforeCoreIsReady") - void GetController(int Port, int64 &ID, FString &Description); - - /** - * @brief Allows a user to control the Game. - * - * **Note:** This isn't an end all be all solution to handling input. Depending on the kind of functionality you want you might want to write your own solution. You can look at LibretroInputComponent to get an idea of how this might be done. - * - * @param PlayerController - The Player Controller that will control the Port. You will probably have to disable input handling if PlayerController is controlling a pawn I don't disable it automatically or use the possession system Unreal uses. - * @param Port - Should be set between 0-3. 0 would control first player. - * @param ControllerBindings - Bindings between Unreal Keys and Libretro's abstract controller interface. Be careful, the Libretro controller abstraction is counterintuitive since it uses the SNES layout you can read more about it [here](https://retropie.org.uk/docs/RetroArch-Configuration/). - * @param OnControllerDisconnected - Called when DisconnectController(Port) is called and is called automatically if the LibretroComponent is destroyed. - */ - UFUNCTION(BlueprintCallable, Category = "Libretro") - void ConnectController(APlayerController* PlayerController, - int Port, - TMapControllerBindings, - FOnControllerDisconnected OnControllerDisconnected, - bool ForwardAllKeyboardInputToCoreIfPossible); - - /** - * @brief Stops user attached to the port from controlling the game. - * - * Causes this Libretro instance to no longer receive input from PlayerController attached to the port and calls the associated On Controller Disconnected delegate. - * - * @param Port - Should be set between 0-3. 0 would disconnect first player. - */ - UFUNCTION(BlueprintCallable, Category = "Libretro") - void DisconnectController(int Port); + void GetController(int Port, int64 &ID, FString &Description); /** * @brief Sets the state of a button for the Libretro core @@ -155,7 +126,7 @@ class UNREALLIBRETRO_API ULibretroCoreInstance : public UActorComponent * */ UFUNCTION(BlueprintCallable, Category = "Libretro|IneffectiveBeforeLaunch") - void SetInputDigital(int Port, bool Pressed, ERetroDeviceID Input); + void SetInputDigital(int Port, bool Activated, ERetroDeviceID Input); /** * @brief Sets the analog state of an input for the Libretro core @@ -167,32 +138,6 @@ class UNREALLIBRETRO_API ULibretroCoreInstance : public UActorComponent UFUNCTION(BlueprintCallable, Category = "Libretro|IneffectiveBeforeLaunch") void SetInputAnalog(int Port, int _16BitSignedInteger, ERetroDeviceID Input); - /** - * @brief Equivalent to pulling a trigger on a nes zapper or arcade gun - * - * @param XY - The top-left of the screen is coordinate (0, 0) bottom-right is (1, 1), x is the horizontal axis - * - * These are convenience functions you can try fiddling around with the Lightgun and Pointer input directly in SetInputDigital too - * - * There are some gotchas here: - * - on nestopia if you only pull the trigger for one frame it won't register - * - zapper on nes requires Port=1 which is the second port because of zero based indexing - */ - UFUNCTION(BlueprintCallable, Category = "Libretro") - void GunPullTrigger(int Port, FVector2D XY); - - /** - * @brief This is used for reloading in lots of games - */ - UFUNCTION(BlueprintCallable, Category = "Libretro") - void GunPullTriggerOffscreen(int Port); - - /** - * @brief Equivalent to releasing a trigger on a nes zapper or arcade gun - */ - UFUNCTION(BlueprintCallable, Category = "Libretro") - void GunReleaseTrigger(int Port); - /** * @brief Where the Libretro Core's frame is drawn * @@ -259,6 +204,10 @@ class UNREALLIBRETRO_API ULibretroCoreInstance : public UActorComponent UPROPERTY(BlueprintReadOnly, Category = Libretro) bool bFrameBottomLeftOrigin; + /** Forward keyboard input from this APlayerController to the libretro core if the core can receive it */ + UPROPERTY(BlueprintReadWrite, Category = Libretro) + APlayerController* KeyboardInputSourcePlayerController{nullptr}; + protected: // @todo: It'd be nice if I could use something like std::wrapped_reference however Unreal doesn't offer an equivalent for now @@ -268,9 +217,4 @@ class UNREALLIBRETRO_API ULibretroCoreInstance : public UActorComponent UPROPERTY() USoundWave* AudioBuffer; - - TStaticArray, PortCount> Bindings; - TStaticArray, PortCount> Controller{ nullptr }; - TStaticArray ForwardKeyboardInput{ false }; - TStaticArray Disconnected{ FOnControllerDisconnected() }; }; diff --git a/Source/UnrealLibretro/Public/LibretroInputDefinitions.h b/Source/UnrealLibretro/Public/LibretroInputDefinitions.h index ce821cd1..6a077b13 100644 --- a/Source/UnrealLibretro/Public/LibretroInputDefinitions.h +++ b/Source/UnrealLibretro/Public/LibretroInputDefinitions.h @@ -69,6 +69,15 @@ enum class ERetroDeviceID : uint8 PointerX, PointerY, PointerPressed, + PointerX1 UMETA(Hidden), + PointerY1 UMETA(Hidden), + PointerPressed1 UMETA(Hidden), + PointerX2 UMETA(Hidden), + PointerY2 UMETA(Hidden), + PointerPressed2 UMETA(Hidden), + PointerX3 UMETA(Hidden), + PointerY3 UMETA(Hidden), + PointerPressed3 UMETA(Hidden), PointerCount, Size UMETA(Hidden), From 039c8dbe61a103592efae015098f2ef13ba6ebe7 Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Sun, 30 Apr 2023 22:26:23 -0700 Subject: [PATCH 13/79] Eliminate SDL2 dependency --- .../UnrealLibretro/Private/UnrealLibretro.cpp | 79 ++++++---------- Source/UnrealLibretro/Private/sdlarch.cpp | 94 +++++++++++-------- Source/UnrealLibretro/Private/sdlarch.h | 19 +--- Source/UnrealLibretro/Public/UnrealLibretro.h | 43 ++++++++- Source/UnrealLibretro/UnrealLibretro.Build.cs | 12 +-- setup.sh | 2 +- 6 files changed, 131 insertions(+), 118 deletions(-) diff --git a/Source/UnrealLibretro/Private/UnrealLibretro.cpp b/Source/UnrealLibretro/Private/UnrealLibretro.cpp index 9739f854..acc0d93e 100644 --- a/Source/UnrealLibretro/Private/UnrealLibretro.cpp +++ b/Source/UnrealLibretro/Private/UnrealLibretro.cpp @@ -3,66 +3,49 @@ #include "Misc/MessageDialog.h" #include "Modules/ModuleManager.h" -#if PLATFORM_APPLE -#include -#endif - -#if PLATFORM_WINDOWS -#include "Windows/PreWindowsApi.h" -#include "Windows/SDL2/SDL.h" -#include "Windows/PostWindowsApi.h" -#endif - #include "LibretroSettings.h" DEFINE_LOG_CATEGORY(Libretro) #define LOCTEXT_NAMESPACE "FUnrealLibretroModule" +#if PLATFORM_WINDOWS +void* OpenGLDLL; +PFN_wglCreateContext _wglCreateContext; +PFN_wglDeleteContext _wglDeleteContext; +PFN_wglMakeCurrent _wglMakeCurrent; +PFN_wglGetProcAddress _wglGetProcAddress; +#endif + void FUnrealLibretroModule::StartupModule() { // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module - - FString BaseDir = IPluginManager::Get().FindPlugin("UnrealLibretro")->GetBaseDir(); - - FString LibraryPath; -#if PLATFORM_WINDOWS - LibraryPath = FPaths::Combine(*BaseDir, TEXT("Binaries/Win64/ThirdParty/SDL2.dll")); - RedistDirectory = FPaths::Combine(*BaseDir, TEXT("Binaries/Win64/ThirdParty/libretro/")); -#elif PLATFORM_MAC - LibraryPath = FPaths::Combine(*BaseDir, TEXT("Binaries/Mac/ThirdParty/libSDL2.dylib")); -#endif // PLATFORM_WINDOWS - #define LIBRETRO_NOTE " Note: disable UnrealLibretro or delete the UnrealLibretro plugin to make this error go away." \ " You can also post an issue to github." #define LIBRETRO_MODULE_LOAD_ERROR(msg) FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("LibretroError", msg LIBRETRO_NOTE)); \ UE_LOG(Libretro, Fatal, TEXT(msg LIBRETRO_NOTE)); -#if !PLATFORM_ANDROID - // SDL is needed to get OpenGL contexts and windows from the OS in a sane way. I tried looking for an official Unreal way to do it, but I couldn't really find one SDL is so portable though it shouldn't matter - SDLHandle = !LibraryPath.IsEmpty() ? FPlatformProcess::GetDllHandle(*LibraryPath) : nullptr; +#if PLATFORM_WINDOWS + FString BaseDir = IPluginManager::Get().FindPlugin("UnrealLibretro")->GetBaseDir(); + RedistDirectory = FPaths::Combine(*BaseDir, TEXT("Binaries/Win64/ThirdParty/libretro/")); + FPlatformProcess::AddDllDirectory(*RedistDirectory); - if (!SDLHandle) + OpenGLDLL = FPlatformProcess::GetDllHandle(TEXT("opengl32.dll")); + if (!OpenGLDLL) { - LIBRETRO_MODULE_LOAD_ERROR("Failed to load SDL2.dll"); + LIBRETRO_MODULE_LOAD_ERROR("Couldn't load opengl32.dll"); } -#if PLATFORM_APPLE - dispatch_sync(dispatch_get_main_queue(), ^ { -#endif - int load_sdl_error = SDL_Init(SDL_INIT_VIDEO); -#if PLATFORM_APPLE - }); -#endif - - if (load_sdl_error) - { - LIBRETRO_MODULE_LOAD_ERROR("Failed to initialize SDL2"); - } -#endif + wglCreateContext = (decltype(wglCreateContext))FPlatformProcess::GetDllExport(OpenGLDLL, TEXT("wglCreateContext")); + wglGetProcAddress = (decltype(wglGetProcAddress))FPlatformProcess::GetDllExport(OpenGLDLL, TEXT("wglGetProcAddress")); + wglDeleteContext = (decltype(wglDeleteContext))FPlatformProcess::GetDllExport(OpenGLDLL, TEXT("wglDeleteContext")); + wglMakeCurrent = (decltype(wglMakeCurrent))FPlatformProcess::GetDllExport(OpenGLDLL, TEXT("wglMakeCurrent")); -#if PLATFORM_WINDOWS - FPlatformProcess::AddDllDirectory(*RedistDirectory); + WNDCLASS wc = { 0 }; + wc.lpfnWndProc = DefWindowProc; + wc.hInstance = GetModuleHandle(NULL); + wc.lpszClassName = TEXT("my_opengl_class"); + RegisterClass(&wc); #endif } @@ -75,16 +58,12 @@ void FUnrealLibretroModule::ShutdownModule() // I could also fix the shutdown_audio hack as well as remove the numerous weak pointers in LibretroContext. // I'm nervous how much the engine will block the game thread on that condition though so that still might not be a solution. #if 0 -#if PLATFORM_APPLE - dispatch_sync(dispatch_get_main_queue(), ^ { -#endif - SDL_Quit(); -#if PLATFORM_APPLE - }); -#endif - FPlatformProcess::FreeDllHandle(SDLHandle); - SDLHandle = nullptr; +#if PLATFORM_WINDOWS + FPlatformProcess::FreeDllHandle(OpenGLDLL); + OpenGLDLL = nullptr; + UnregisterClass(TEXT("my_opengl_class"), GetModuleHandle(NULL)); +#endif // @todo Remove RedistDirectory from Searchpath #endif diff --git a/Source/UnrealLibretro/Private/sdlarch.cpp b/Source/UnrealLibretro/Private/sdlarch.cpp index 3e303c41..d4df64d1 100644 --- a/Source/UnrealLibretro/Private/sdlarch.cpp +++ b/Source/UnrealLibretro/Private/sdlarch.cpp @@ -27,7 +27,7 @@ extern "C" #endif #if PLATFORM_ANDROID -#define SDL_GL_GetProcAddress eglGetProcAddress +#define GL_GET_PROC_ADDRESS eglGetProcAddress #include // requires ndk r5 or newer #include // requires ndk r5 or newer @@ -172,50 +172,60 @@ static bool GLLogCall(const char* function, const char* file, int line) if (!eglMakeCurrent(core.gl.egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, core.gl.egl_context)) { UE_LOG(Libretro, Fatal, TEXT("eglMakeCurrent() returned error %d"), eglGetError()); } -#else - SDL_GL_ResetAttributes(); // SDL state isn't thread local unlike OpenGL. So Libretro Cores could potentially interfere with eachother's Attributes since you're setting globals. +#elif PLATFORM_WINDOWS + // Use wgl to create a hidden window to get an OpenGL context + core.gl.window = CreateWindowEx( + 0, TEXT("my_opengl_class"), TEXT("OpenGL Window"), + WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_POPUP, + 0, 0, 1, 1, + NULL, NULL, GetModuleHandle(NULL), NULL + ); - if (core.hw.context_type == RETRO_HW_CONTEXT_OPENGL_CORE || core.hw.version_major >= 3) { - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, core.hw.version_major); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, core.hw.version_minor); + if (!core.gl.window) { + UE_LOG(Libretro, Fatal, TEXT("Failed to create window")); } - switch (core.hw.context_type) { - case RETRO_HW_CONTEXT_OPENGL_CORE: - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); - break; - case RETRO_HW_CONTEXT_OPENGLES2: - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); - break; - case RETRO_HW_CONTEXT_OPENGL: - if (core.hw.version_major >= 3) - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); - break; - default: - UE_LOG(Libretro, Fatal, TEXT("Unsupported hw context %i. (only OPENGL, OPENGL_CORE and OPENGLES2 supported)"), core.hw.context_type); - } + core.gl.hdc = GetDC(core.gl.window); - // Might be able to use this instead SWindow::GetNativeWindow() - core.gl.window = SDL_CreateWindow("sdlarch", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 0, 0, SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN); // @todo This is fine on windows, but creating a window from a background thread will crash on some versions Linux if you don't enable a special flag and everytime on MacOS + PIXELFORMATDESCRIPTOR pfd = { + sizeof(PIXELFORMATDESCRIPTOR), 1, + PFD_SUPPORT_OPENGL, + PFD_TYPE_RGBA, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + PFD_MAIN_PLANE, 0, 0, 0, 0 + }; - if (!core.gl.window) - UE_LOG(Libretro, Fatal, TEXT("Failed to create window: %s"), ANSI_TO_TCHAR(SDL_GetError())); + int pixel_format = ChoosePixelFormat(core.gl.hdc, &pfd); + if (!pixel_format) { + UE_LOG(Libretro, Fatal, TEXT("Failed to choose pixel format")); + } -#if defined(DEBUG_OPENGL_CALLBACK) - SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); -#endif - core.gl.context = SDL_GL_CreateContext(core.gl.window); + if (!SetPixelFormat(core.gl.hdc, pixel_format, &pfd)) { + UE_LOG(Libretro, Fatal, TEXT("Failed to set pixel format")); + } + core.gl.context = wglCreateContext(core.gl.hdc); + if (!core.gl.context) { + UE_LOG(Libretro, Fatal, TEXT("Failed to create OpenGL context")); + } - if (!core.gl.context) - UE_LOG(Libretro, Fatal, TEXT("Failed to create OpenGL context: %s"), ANSI_TO_TCHAR(SDL_GetError())); + if (!wglMakeCurrent(core.gl.hdc, core.gl.context)) { + UE_LOG(Libretro, Fatal, TEXT("Failed to activate OpenGL context")); + } +#else + // @todo Other platforms don't have routines to get OpenGL contexts currently + // GLFW's interface has an oversight that windows and contexts are created together (You could expose some of it's internals to work around this) + // and SDL is kind of bloated and wasn't flexible enough for Android to work with it + // My current thoughts are use Google ANGLE since then the EGL code used above for PLATFORM_ANDROID should work without modification + // ANGLE potentially could allow for easier interop with Unreal's RHI since you can run ANGLE on top of it if its DX12 or Vulkan + // However the main issue with ANGLE is that it seems the Libretro Cores need to be built for it in mind and I don't know if that feature is being actively developed right now + checkNoEntry(); #endif #pragma warning(push) #pragma warning(disable:4191) // Initialize all entry points. - #define GET_GL_PROCEDURES(Type,Func) Func = (Type)SDL_GL_GetProcAddress(#Func); + #define GET_GL_PROCEDURES(Type,Func) Func = (Type)GL_GET_PROC_ADDRESS(#Func); ENUM_GL_PROCEDURES(GET_GL_PROCEDURES); ENUM_GL_WIN32_INTEROP_PROCEDURES(GET_GL_PROCEDURES); @@ -228,7 +238,7 @@ static bool GLLogCall(const char* function, const char* file, int line) ENUM_GL_WIN32_INTEROP_PROCEDURES(CHECK_GL_PROCEDURES); this->gl_win32_interop_supported_by_driver = false; // bFoundAllEntryPoints; Not ready - glGetError = (PFNGLGETERRORPROC) SDL_GL_GetProcAddress("glGetError"); + glGetError = (PFNGLGETERRORPROC)GL_GET_PROC_ADDRESS("glGetError"); check(glGetError); // Restore warning C4191. @@ -697,7 +707,7 @@ bool LibretroContext::core_environment(unsigned cmd, void *data) { hw->get_current_framebuffer = libretro_callbacks->c_get_current_framebuffer; #pragma warning(push) #pragma warning(disable:4191) - hw->get_proc_address = (retro_hw_get_proc_address_t)SDL_GL_GetProcAddress; + hw->get_proc_address = (retro_hw_get_proc_address_t)GL_GET_PROC_ADDRESS; #pragma warning(pop) core.hw = *hw; core.using_opengl = true; @@ -1095,14 +1105,18 @@ LibretroContext* LibretroContext::Launch(ULibretroCoreInstance* LibretroCoreInst }, TStatId(), nullptr, ENamedThreads::GameThread); { -#if !PLATFORM_ANDROID +#if PLATFORM_WINDOWS + // On windows the thread that created a window MUST also destroy it https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-destroywindow#:~:text=A%20thread%20cannot%20use%20DestroyWindow%20to%20destroy%20a%20window%20created%20by%20a%20different%20thread if (l->core.gl.window) { - verify(SDL_GL_MakeCurrent(l->core.gl.window, NULL) == 0); - SDL_DestroyWindow(l->core.gl.window); // @todo: In SDLarch's code SDL_Quit was here and that implicitly destroyed some things like windows. So I'm not sure if I'm exhaustively destroying everything that it destroyed yet. In order to fix this you could probably just run SDL_Quit here and step with the debugger to see all the stuff it destroys. + wglMakeCurrent(l->core.gl.hdc, NULL); + verify(ReleaseDC(l->core.gl.window, l->core.gl.hdc)); + verify(DestroyWindow(l->core.gl.window)); } #endif - + // The double nested command enqueue is based on boilerplate I found elsewhere in the engine + // Since render commands are executed fifo we only delete shared resources after the render thread is done with them + // The actual render command execution is done on the RHI thread so we have to synchronize there as well ENQUEUE_RENDER_COMMAND(LibretroCleanupResourcesSharedWithRenderThread)([l](FRHICommandListImmediate& RHICmdList) { RHICmdList.EnqueueLambda([l](FRHICommandList&) @@ -1114,12 +1128,12 @@ LibretroContext* LibretroContext::Launch(ULibretroCoreInstance* LibretroCoreInst FMemory::Free(l->core.software.bgra_buffers[i]); } } -#if !PLATFORM_ANDROID +#if PLATFORM_WINDOWS if (l->core.gl.context) { - SDL_GL_DeleteContext(l->core.gl.context); /** implicitly releases resources like fbos, pbos, and textures */ + wglDeleteContext(l->core.gl.context); /** implicitly releases resources like fbos, pbos, and textures */ } -#else +#elif PLATFORM_ANDROID if (l->core.gl.egl_context) { verify(eglDestroyContext(l->core.gl.egl_display, diff --git a/Source/UnrealLibretro/Private/sdlarch.h b/Source/UnrealLibretro/Private/sdlarch.h index f0e243bb..d13565a8 100644 --- a/Source/UnrealLibretro/Private/sdlarch.h +++ b/Source/UnrealLibretro/Private/sdlarch.h @@ -19,18 +19,6 @@ static_assert(RETRO_API_VERSION == 1, "Retro API version changed"); #include "LibretroInputDefinitions.h" #include "RawAudioSoundWave.h" - -// Third party libraries -#if PLATFORM_WINDOWS -#include "Windows/AllowWindowsPlatformTypes.h" -#include "Windows/PreWindowsApi.h" -#include "Windows/SDL2/SDL.h" -#include "Windows/PostWindowsApi.h" -#include "Windows/HideWindowsPlatformTypes.h" -#elif PLATFORM_APPLE -#include "Mac/SDL2/SDL.h" -#endif - #if PLATFORM_WINDOWS #include "Windows/PreWindowsApi.h" #endif @@ -191,9 +179,10 @@ struct LibretroContext { #if PLATFORM_ANDROID void* egl_context; void* egl_display; -#else - SDL_Window* window; - SDL_GLContext context; +#elif PLATFORM_WINDOWS + HWND window; + HGLRC context; + HDC hdc; #endif GLuint texture; diff --git a/Source/UnrealLibretro/Public/UnrealLibretro.h b/Source/UnrealLibretro/Public/UnrealLibretro.h index 86276b62..5964f418 100644 --- a/Source/UnrealLibretro/Public/UnrealLibretro.h +++ b/Source/UnrealLibretro/Public/UnrealLibretro.h @@ -21,6 +21,48 @@ static const struct { FString DistributionPath; FString Extension; FString Build { TEXT("Android/arm64-v8a/"), "_libretro_android.so", "https://buildbot.libretro.com/nightly/android/latest/arm64-v8a/" , "Launcher.Platform_Android.Large" }, }; +#if PLATFORM_WINDOWS +#include "Windows/AllowWindowsPlatformTypes.h" +#include "Windows/PreWindowsApi.h" +#include + +typedef HGLRC(WINAPI* PFN_wglCreateContext)(HDC); +typedef BOOL(WINAPI* PFN_wglDeleteContext)(HGLRC); +typedef PROC(WINAPI* PFN_wglGetProcAddress)(LPCSTR); +typedef HDC(WINAPI* PFN_wglGetCurrentDC)(void); +typedef HGLRC(WINAPI* PFN_wglGetCurrentContext)(void); +typedef BOOL(WINAPI* PFN_wglMakeCurrent)(HDC, HGLRC); +typedef BOOL(WINAPI* PFN_wglShareLists)(HGLRC, HGLRC); + +#define wglCreateContext _wglCreateContext +#define wglDeleteContext _wglDeleteContext +#define wglMakeCurrent _wglMakeCurrent +#define wglGetProcAddress _wglGetProcAddress + +#define GL_GET_PROC_ADDRESS Win32GLGetProcAddress + +extern void* OpenGLDLL; +extern PFN_wglCreateContext _wglCreateContext; +extern PFN_wglDeleteContext _wglDeleteContext; +extern PFN_wglMakeCurrent _wglMakeCurrent; +extern PFN_wglGetProcAddress _wglGetProcAddress; + +static void* Win32GLGetProcAddress(const char* procname) +{ + void* proc = wglGetProcAddress(procname); + + if (!proc) + { + proc = FPlatformProcess::GetDllExport(OpenGLDLL, ANSI_TO_TCHAR(procname)); + } + + return proc; +} + +#include "Windows/PostWindowsApi.h" +#include "Windows/HideWindowsPlatformTypes.h" +#endif + class FUnrealLibretroModule : public IModuleInterface { public: @@ -87,7 +129,6 @@ class FUnrealLibretroModule : public IModuleInterface } private: - void* SDLHandle; #ifdef PLATFORM_WINDOWS FString RedistDirectory; diff --git a/Source/UnrealLibretro/UnrealLibretro.Build.cs b/Source/UnrealLibretro/UnrealLibretro.Build.cs index d5504445..a1ba0716 100644 --- a/Source/UnrealLibretro/UnrealLibretro.Build.cs +++ b/Source/UnrealLibretro/UnrealLibretro.Build.cs @@ -16,20 +16,10 @@ public UnrealLibretro(ReadOnlyTargetRules Target) : base(Target) RuntimeDependencies.Add("$(PluginDir)/Saves/*" ); RuntimeDependencies.Add("$(PluginDir)/System/*" ); - - if (Target.Platform.Equals(UnrealTargetPlatform.Mac)) - { - RuntimeDependencies.Add("$(PluginDir)/MyCores/Mac/*"); - RuntimeDependencies.Add("$(PluginDir)/Binaries/Mac/ThirdParty/libSDL2*.dylib"); // globbed because libSDL2.dylib is an alias - PublicAdditionalLibraries.Add("$(PluginDir)/Binaries/Mac/ThirdParty/libSDL2.dylib"); - } - else if (Target.Platform.Equals(UnrealTargetPlatform.Win64)) + if (Target.Platform.Equals(UnrealTargetPlatform.Win64)) { RuntimeDependencies.Add("$(PluginDir)/MyCores/Win64/*"); - PublicAdditionalLibraries.Add("$(PluginDir)/Binaries/Win64/ThirdParty/SDL2.lib"); - RuntimeDependencies.Add("$(PluginDir)/Binaries/Win64/ThirdParty/SDL2.dll"); RuntimeDependencies.Add("$(PluginDir)/Binaries/Win64/ThirdParty/libretro/*"); - PublicDelayLoadDLLs.Add("SDL2.dll"); } else if (Target.Platform.Equals(UnrealTargetPlatform.Android)) { diff --git a/setup.sh b/setup.sh index 0eb1467d..940d3853 100755 --- a/setup.sh +++ b/setup.sh @@ -23,4 +23,4 @@ cp /tmp/RetroArch-Win64/*.dll Binaries/Win64/ThirdParty/libretro # Acquire and move unversioned data from unversioned branch (Note this will break if you don't have a really new version of git) git fetch -git restore --source origin/unversioned Content/ Binaries/ Source/UnrealLibretro/Private/Windows/SDL2 Source/UnrealLibretro/Private/Mac/SDL2 +git restore --source origin/unversioned Content/ From 239ac5549ea07fd966c58d5a58ac20d153597f65 Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Mon, 1 May 2023 20:06:49 -0700 Subject: [PATCH 14/79] Normalize line endings --- .gitattributes | 24 +- .../UnrealLibretro/Private/LambdaRunnable.cpp | 4 +- .../Private/LibretroCoreInstance.cpp | 630 ++--- .../UnrealLibretro/Private/UnrealLibretro.cpp | 148 +- Source/UnrealLibretro/Private/sdlarch.cpp | 2246 ++++++++--------- Source/UnrealLibretro/Private/sdlarch.h | 476 ++-- .../Public/LibretroCoreInstance.h | 188 +- .../Public/LibretroInputDefinitions.h | 182 +- Source/UnrealLibretro/Public/UnrealLibretro.h | 166 +- Source/UnrealLibretro/UnrealLibretro.Build.cs | 6 +- UnrealLibretro.uplugin | 60 +- setup.sh | 2 +- 12 files changed, 2066 insertions(+), 2066 deletions(-) diff --git a/.gitattributes b/.gitattributes index 665e862d..2a9883b9 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,13 +1,13 @@ -# This is quite implicit in what its doing. But its saying for all files (the * wildcard) if it thinks its a text file. It will try to automatically manage their line endings. Specifically it should make committed files have LF line endings most of the time. -* text=auto - -*.png binary -*.uasset binary -*.umap binary - -*.sln eol=crlf -*.vcxproj eol=crlf -*.vcxproj.filters eol=crlf -*.cs eol=crlf - +# This is quite implicit in what its doing. But its saying for all files (the * wildcard) if it thinks its a text file. It will try to automatically manage their line endings. Specifically it should make committed files have LF line endings most of the time. +* text=auto + +*.png binary +*.uasset binary +*.umap binary + +*.sln eol=crlf +*.vcxproj eol=crlf +*.vcxproj.filters eol=crlf +*.cs eol=crlf + *.sh eol=lf \ No newline at end of file diff --git a/Source/UnrealLibretro/Private/LambdaRunnable.cpp b/Source/UnrealLibretro/Private/LambdaRunnable.cpp index 10a041db..062c6fe5 100644 --- a/Source/UnrealLibretro/Private/LambdaRunnable.cpp +++ b/Source/UnrealLibretro/Private/LambdaRunnable.cpp @@ -19,8 +19,8 @@ FLambdaRunnable::FLambdaRunnable(FString ThreadName, TUniqueFunction< void()> In FLambdaRunnable::~FLambdaRunnable() { - Thread->Suspend(false); - Thread->WaitForCompletion(); + Thread->Suspend(false); + Thread->WaitForCompletion(); delete Thread; } diff --git a/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp b/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp index 8b93260e..1216c862 100644 --- a/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp +++ b/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp @@ -1,315 +1,315 @@ - -#include "LibretroCoreInstance.h" - -#include "libretro/libretro.h" - -#include "Misc/FileHelper.h" -#include "Components/AudioComponent.h" -#include "GameFramework/PlayerInput.h" - -#include "UnrealLibretro.h" -#include "LibretroInputDefinitions.h" -#include "RawAudioSoundWave.h" -#include "sdlarch.h" - -#define NOT_LAUNCHED_GUARD if (!CoreInstance.IsSet()) return; - -ULibretroCoreInstance::ULibretroCoreInstance() -{ - PrimaryComponentTick.bCanEverTick = true; -} - -void ULibretroCoreInstance::GetController(int Port, int64& ID, FString& Description) -{ - NOT_LAUNCHED_GUARD - - // This if statement guards against a datarace on LibretroContext::ControllerDescriptions - if (CoreInstance.GetValue()->CoreState.load(std::memory_order_acquire) != LibretroContext::ECoreState::Starting) - { - ID = CoreInstance.GetValue()->devices[Port].load(std::memory_order_relaxed); - for (auto & ControllerDescription : CoreInstance.GetValue()->ControllerDescriptions[Port]) - { - if (ControllerDescription.ID == ID) - { - Description = ControllerDescription.Description; - break; - } - } - } -} - -void ULibretroCoreInstance::Launch() -{ - Shutdown(); - - auto _CorePath = IFileManager::Get().ConvertToAbsolutePathForExternalAppForWrite(*FUnrealLibretroModule::ResolveCorePath(this->CorePath)); - auto _RomPath = IFileManager::Get().ConvertToAbsolutePathForExternalAppForWrite(*FUnrealLibretroModule::ResolveROMPath (this->RomPath)); - -#if PLATFORM_WINDOWS - _RomPath.ReplaceCharInline('/', '\\'); -#endif - - if (!IPlatformFile::GetPlatformPhysical().FileExists(*_CorePath)) - { - UE_LOG(Libretro, Warning, TEXT("Failed to launch Libretro core '%s'. Couldn't find core at path '%s'"), *_CorePath, *_CorePath); - return; - } - else if (!IPlatformFile::GetPlatformPhysical().FileExists(*_RomPath) && !IPlatformFile::GetPlatformPhysical().DirectoryExists(*_RomPath)) - { - UE_LOG(Libretro, Warning, TEXT("Failed to launch Libretro core '%s'. Couldn't find ROM at path '%s'"), *_CorePath, *_RomPath); - return; - } - - if (!RenderTarget) - { - RenderTarget = NewObject(); - } - - AudioBuffer = NewObject(); - - RenderTarget->Filter = TF_Nearest; // @todo remove this - - // @todo Figure out if this is actually a problem then fix it maybe - // Sometimes it can be practical to make the UV's oversized so we don't want it to wrap - // however this might make debugging a little more confusing if you have a UV transformation issue because the texture might be rendered - // as completely black and not reflected or tiled or something <-- I actually immediately ran into this issue because the logic here was - // broken because I think all my UV's are negative - //RenderTarget->AddressX = TA_Clamp; - //RenderTarget->AddressY = TA_Clamp; - - this->CoreInstance = LibretroContext::Launch(this, _CorePath, _RomPath, RenderTarget, static_cast(AudioBuffer), - [weakThis = MakeWeakObjectPtr(this), SRAMPath = FUnrealLibretroModule::ResolveSRAMPath(_RomPath, SRAMPath), - ControllersSetOnLaunch = this->ControllersSetOnLaunch] - (LibretroContext *_CoreInstance, libretro_api_t &libretro_api) - { // Core has loaded - - // Load save data into core @todo this is just a weird place to hook this in - auto File = IPlatformFile::GetPlatformPhysical().OpenRead(*SRAMPath); - if (File && libretro_api.get_memory_size(RETRO_MEMORY_SAVE_RAM)) - { - File->Read((uint8*)libretro_api.get_memory_data(RETRO_MEMORY_SAVE_RAM), - libretro_api.get_memory_size(RETRO_MEMORY_SAVE_RAM)); - File->~IFileHandle(); // must be called explicitly - } - - // Notify delegate - FGraphEventRef Task = FFunctionGraphTask::CreateAndDispatchWhenReady( - [weakThis, - bottom_left_origin = _CoreInstance->LibretroThread_bottom_left_origin, - geometry = _CoreInstance->LibretroThread_geometry]() - { - if (weakThis.IsValid()) - { - weakThis->OnCoreIsReady.Broadcast(weakThis->RenderTarget, - weakThis->AudioBuffer); - - weakThis->bFrameBottomLeftOrigin = bottom_left_origin; - weakThis->FrameWidth = geometry.base_width; - weakThis->FrameHeight = geometry.base_height; - - weakThis->OnCoreFrameBufferResize.Broadcast(); - - weakThis->AudioComponent->SetSound(weakThis->AudioBuffer); - weakThis->AudioComponent->Play(); - } - }, TStatId(), nullptr, ENamedThreads::GameThread); - }); - - // @todo theres a data race with how I assign this - this->CoreInstance.GetValue()->CoreEnvironmentCallback = [weakThis = MakeWeakObjectPtr(this), CoreInstance = this->CoreInstance.GetValue()](unsigned cmd, void* data)->bool - { - switch (cmd) - { - case RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO: { - FFunctionGraphTask::CreateAndDispatchWhenReady( - [weakThis, - system_av_info = *(const struct retro_system_av_info*)data]() - { - if (weakThis.IsValid()) - { - { // @hack to change audio playback sample-rate - auto AudioQueue = static_cast(weakThis->AudioBuffer)->AudioQueue; - weakThis->AudioBuffer = NewObject(); - weakThis->AudioBuffer->SetSampleRate(system_av_info.timing.sample_rate); - weakThis->AudioBuffer->NumChannels = 2; - static_cast(weakThis->AudioBuffer)->AudioQueue = AudioQueue; - weakThis->AudioComponent->SetSound(weakThis->AudioBuffer); - } - - weakThis->FrameWidth = system_av_info.geometry.base_width; - weakThis->FrameHeight = system_av_info.geometry.base_height; - weakThis->OnCoreFrameBufferResize.Broadcast(); - } - }, TStatId(), nullptr, ENamedThreads::GameThread); - - return true; - } - case RETRO_ENVIRONMENT_SET_ROTATION: { - FFunctionGraphTask::CreateAndDispatchWhenReady( - [weakThis, - rotation = *(const unsigned*)data]() - { - if (weakThis.IsValid()) - { - weakThis->FrameRotation = rotation / 4.f; - weakThis->OnCoreFrameBufferResize.Broadcast(); - } - }, TStatId(), nullptr, ENamedThreads::GameThread); - - return true; - } - case RETRO_ENVIRONMENT_SET_GEOMETRY: { - auto geometry = (const struct retro_game_geometry*) data; - - FFunctionGraphTask::CreateAndDispatchWhenReady( - [weakThis, - geometry = *(const struct retro_game_geometry*)data]() - { - if (weakThis.IsValid()) - { - weakThis->FrameWidth = geometry.base_width; - weakThis->FrameHeight = geometry.base_height; - weakThis->OnCoreFrameBufferResize.Broadcast(); - } - }, TStatId(), nullptr, ENamedThreads::GameThread); - - return true; - } - } - - return false; - }; -} - -void ULibretroCoreInstance::Pause(bool ShouldPause) -{ - NOT_LAUNCHED_GUARD - - CoreInstance.GetValue()->Pause(ShouldPause); - Paused = ShouldPause; -} - -void ULibretroCoreInstance::Shutdown() -{ - - NOT_LAUNCHED_GUARD - - LibretroContext::Shutdown(CoreInstance.GetValue()); - CoreInstance.Reset(); -} - -void ULibretroCoreInstance::LoadState(const FString& FilePath) -{ - NOT_LAUNCHED_GUARD - - CoreInstance.GetValue()->EnqueueTask( - [CorePath = this->CorePath, SaveStatePath = FUnrealLibretroModule::ResolveSaveStatePath(RomPath, FilePath)] - (auto libretro_api) - { - TArray SaveStateBuffer; - - if (!FFileHelper::LoadFileToArray(SaveStateBuffer, *SaveStatePath)) - { - UE_LOG(Libretro, Warning, TEXT("Couldn't load save state '%s' error code:%u"), *SaveStatePath, FPlatformMisc::GetLastError()); - return; // We just assume failure means the file did not exist and we do nothing - } - - if (SaveStateBuffer.Num() != libretro_api.serialize_size()) // because of emulator versions these might not match up also some Libretro cores don't follow spec so the size can change between calls to serialize_size - { - UE_LOG(Libretro, Warning, TEXT("Save state file size specified by '%s' did not match the save state size in folder. File Size : %d Core Size: %zu. Going to try to load it anyway."), *CorePath, SaveStateBuffer.Num(), libretro_api.serialize_size()) - } - - libretro_api.unserialize(SaveStateBuffer.GetData(), SaveStateBuffer.Num()); - }); -} - -void ULibretroCoreInstance::SaveState(const FString& FilePath) -{ - NOT_LAUNCHED_GUARD - - this->CoreInstance.GetValue()->EnqueueTask - ( - [SaveStatePath = FUnrealLibretroModule::ResolveSaveStatePath(RomPath, FilePath)](libretro_api_t& libretro_api) - { - TArray SaveStateBuffer; // @dynamic - SaveStateBuffer.Reserve(libretro_api.serialize_size() + 2); // The plus two is a slight optimization based on how SaveArrayToFile works - SaveStateBuffer.AddUninitialized(libretro_api.serialize_size()); - libretro_api.serialize(static_cast(SaveStateBuffer.GetData()), libretro_api.serialize_size()); - FFileHelper::SaveArrayToFile(SaveStateBuffer, *SaveStatePath); - } - ); -} - -#include "Scalability.h" - -void ULibretroCoreInstance::BeginPlay() -{ - Super::BeginPlay(); - - /*if (Scalability::GetQualityLevels().AntiAliasingQuality) { - FMessageDialog::Open(EAppMsgType::Ok, FText::AsCultureInvariant("You have temporal anti-aliasing enabled. The emulated games will look will look blurry and laggy if you leave this enabled. If you happen to know how to fix this let me know. I tried enabling responsive AA on the material to prevent this, but that didn't work.")); - }*/ -} - -void ULibretroCoreInstance::SetInputDigital(int Port, bool Pressed, ERetroDeviceID Input) -{ - NOT_LAUNCHED_GUARD - - CoreInstance.GetValue()->EnqueueTask([=, CoreInstance = CoreInstance.GetValue()](auto) - { - CoreInstance->InputState[Port][Input] = Pressed; - }); -} - -void ULibretroCoreInstance::SetInputAnalog(int Port, int _16BitSignedInteger, ERetroDeviceID Input) -{ - NOT_LAUNCHED_GUARD - - CoreInstance.GetValue()->EnqueueTask([=, CoreInstance = CoreInstance.GetValue()](auto) - { - CoreInstance->InputState[Port][Input] = _16BitSignedInteger; - }); -} - -void ULibretroCoreInstance::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) -{ - if ( CoreInstance.IsSet() - && KeyboardInputSourcePlayerController) - { - for (int i = 0; i < count_key_bindings; i++) - { - if ( KeyboardInputSourcePlayerController->PlayerInput->WasJustPressed(key_bindings[i].Unreal) - || KeyboardInputSourcePlayerController->PlayerInput->WasJustReleased(key_bindings[i].Unreal)) - { - this->CoreInstance.GetValue()->EnqueueTask( - [=, down = KeyboardInputSourcePlayerController->PlayerInput->WasJustPressed(key_bindings[i].Unreal)] - (libretro_api_t libretro_api) - { - if (libretro_api.keyboard_event) - { - libretro_api.keyboard_event(down, key_bindings[i].libretro, 0, RETROKMOD_NONE); - } - }); - } - } - } -} - -void ULibretroCoreInstance::BeginDestroy() -{ - if (this->CoreInstance.IsSet()) - { - // Save SRam - this->CoreInstance.GetValue()->EnqueueTask( - [SRAMPath = FUnrealLibretroModule::ResolveSRAMPath(RomPath, SRAMPath)](auto libretro_api) - { - auto SRAMBuffer = TArrayView((uint8*)libretro_api.get_memory_data(RETRO_MEMORY_SAVE_RAM), - libretro_api.get_memory_size(RETRO_MEMORY_SAVE_RAM)); - FFileHelper::SaveArrayToFile(SRAMBuffer, *SRAMPath); - }); - - Shutdown(); - } - - Super::BeginDestroy(); -} + +#include "LibretroCoreInstance.h" + +#include "libretro/libretro.h" + +#include "Misc/FileHelper.h" +#include "Components/AudioComponent.h" +#include "GameFramework/PlayerInput.h" + +#include "UnrealLibretro.h" +#include "LibretroInputDefinitions.h" +#include "RawAudioSoundWave.h" +#include "sdlarch.h" + +#define NOT_LAUNCHED_GUARD if (!CoreInstance.IsSet()) return; + +ULibretroCoreInstance::ULibretroCoreInstance() +{ + PrimaryComponentTick.bCanEverTick = true; +} + +void ULibretroCoreInstance::GetController(int Port, int64& ID, FString& Description) +{ + NOT_LAUNCHED_GUARD + + // This if statement guards against a datarace on LibretroContext::ControllerDescriptions + if (CoreInstance.GetValue()->CoreState.load(std::memory_order_acquire) != LibretroContext::ECoreState::Starting) + { + ID = CoreInstance.GetValue()->devices[Port].load(std::memory_order_relaxed); + for (auto & ControllerDescription : CoreInstance.GetValue()->ControllerDescriptions[Port]) + { + if (ControllerDescription.ID == ID) + { + Description = ControllerDescription.Description; + break; + } + } + } +} + +void ULibretroCoreInstance::Launch() +{ + Shutdown(); + + auto _CorePath = IFileManager::Get().ConvertToAbsolutePathForExternalAppForWrite(*FUnrealLibretroModule::ResolveCorePath(this->CorePath)); + auto _RomPath = IFileManager::Get().ConvertToAbsolutePathForExternalAppForWrite(*FUnrealLibretroModule::ResolveROMPath (this->RomPath)); + +#if PLATFORM_WINDOWS + _RomPath.ReplaceCharInline('/', '\\'); +#endif + + if (!IPlatformFile::GetPlatformPhysical().FileExists(*_CorePath)) + { + UE_LOG(Libretro, Warning, TEXT("Failed to launch Libretro core '%s'. Couldn't find core at path '%s'"), *_CorePath, *_CorePath); + return; + } + else if (!IPlatformFile::GetPlatformPhysical().FileExists(*_RomPath) && !IPlatformFile::GetPlatformPhysical().DirectoryExists(*_RomPath)) + { + UE_LOG(Libretro, Warning, TEXT("Failed to launch Libretro core '%s'. Couldn't find ROM at path '%s'"), *_CorePath, *_RomPath); + return; + } + + if (!RenderTarget) + { + RenderTarget = NewObject(); + } + + AudioBuffer = NewObject(); + + RenderTarget->Filter = TF_Nearest; // @todo remove this + + // @todo Figure out if this is actually a problem then fix it maybe + // Sometimes it can be practical to make the UV's oversized so we don't want it to wrap + // however this might make debugging a little more confusing if you have a UV transformation issue because the texture might be rendered + // as completely black and not reflected or tiled or something <-- I actually immediately ran into this issue because the logic here was + // broken because I think all my UV's are negative + //RenderTarget->AddressX = TA_Clamp; + //RenderTarget->AddressY = TA_Clamp; + + this->CoreInstance = LibretroContext::Launch(this, _CorePath, _RomPath, RenderTarget, static_cast(AudioBuffer), + [weakThis = MakeWeakObjectPtr(this), SRAMPath = FUnrealLibretroModule::ResolveSRAMPath(_RomPath, SRAMPath), + ControllersSetOnLaunch = this->ControllersSetOnLaunch] + (LibretroContext *_CoreInstance, libretro_api_t &libretro_api) + { // Core has loaded + + // Load save data into core @todo this is just a weird place to hook this in + auto File = IPlatformFile::GetPlatformPhysical().OpenRead(*SRAMPath); + if (File && libretro_api.get_memory_size(RETRO_MEMORY_SAVE_RAM)) + { + File->Read((uint8*)libretro_api.get_memory_data(RETRO_MEMORY_SAVE_RAM), + libretro_api.get_memory_size(RETRO_MEMORY_SAVE_RAM)); + File->~IFileHandle(); // must be called explicitly + } + + // Notify delegate + FGraphEventRef Task = FFunctionGraphTask::CreateAndDispatchWhenReady( + [weakThis, + bottom_left_origin = _CoreInstance->LibretroThread_bottom_left_origin, + geometry = _CoreInstance->LibretroThread_geometry]() + { + if (weakThis.IsValid()) + { + weakThis->OnCoreIsReady.Broadcast(weakThis->RenderTarget, + weakThis->AudioBuffer); + + weakThis->bFrameBottomLeftOrigin = bottom_left_origin; + weakThis->FrameWidth = geometry.base_width; + weakThis->FrameHeight = geometry.base_height; + + weakThis->OnCoreFrameBufferResize.Broadcast(); + + weakThis->AudioComponent->SetSound(weakThis->AudioBuffer); + weakThis->AudioComponent->Play(); + } + }, TStatId(), nullptr, ENamedThreads::GameThread); + }); + + // @todo theres a data race with how I assign this + this->CoreInstance.GetValue()->CoreEnvironmentCallback = [weakThis = MakeWeakObjectPtr(this), CoreInstance = this->CoreInstance.GetValue()](unsigned cmd, void* data)->bool + { + switch (cmd) + { + case RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO: { + FFunctionGraphTask::CreateAndDispatchWhenReady( + [weakThis, + system_av_info = *(const struct retro_system_av_info*)data]() + { + if (weakThis.IsValid()) + { + { // @hack to change audio playback sample-rate + auto AudioQueue = static_cast(weakThis->AudioBuffer)->AudioQueue; + weakThis->AudioBuffer = NewObject(); + weakThis->AudioBuffer->SetSampleRate(system_av_info.timing.sample_rate); + weakThis->AudioBuffer->NumChannels = 2; + static_cast(weakThis->AudioBuffer)->AudioQueue = AudioQueue; + weakThis->AudioComponent->SetSound(weakThis->AudioBuffer); + } + + weakThis->FrameWidth = system_av_info.geometry.base_width; + weakThis->FrameHeight = system_av_info.geometry.base_height; + weakThis->OnCoreFrameBufferResize.Broadcast(); + } + }, TStatId(), nullptr, ENamedThreads::GameThread); + + return true; + } + case RETRO_ENVIRONMENT_SET_ROTATION: { + FFunctionGraphTask::CreateAndDispatchWhenReady( + [weakThis, + rotation = *(const unsigned*)data]() + { + if (weakThis.IsValid()) + { + weakThis->FrameRotation = rotation / 4.f; + weakThis->OnCoreFrameBufferResize.Broadcast(); + } + }, TStatId(), nullptr, ENamedThreads::GameThread); + + return true; + } + case RETRO_ENVIRONMENT_SET_GEOMETRY: { + auto geometry = (const struct retro_game_geometry*) data; + + FFunctionGraphTask::CreateAndDispatchWhenReady( + [weakThis, + geometry = *(const struct retro_game_geometry*)data]() + { + if (weakThis.IsValid()) + { + weakThis->FrameWidth = geometry.base_width; + weakThis->FrameHeight = geometry.base_height; + weakThis->OnCoreFrameBufferResize.Broadcast(); + } + }, TStatId(), nullptr, ENamedThreads::GameThread); + + return true; + } + } + + return false; + }; +} + +void ULibretroCoreInstance::Pause(bool ShouldPause) +{ + NOT_LAUNCHED_GUARD + + CoreInstance.GetValue()->Pause(ShouldPause); + Paused = ShouldPause; +} + +void ULibretroCoreInstance::Shutdown() +{ + + NOT_LAUNCHED_GUARD + + LibretroContext::Shutdown(CoreInstance.GetValue()); + CoreInstance.Reset(); +} + +void ULibretroCoreInstance::LoadState(const FString& FilePath) +{ + NOT_LAUNCHED_GUARD + + CoreInstance.GetValue()->EnqueueTask( + [CorePath = this->CorePath, SaveStatePath = FUnrealLibretroModule::ResolveSaveStatePath(RomPath, FilePath)] + (auto libretro_api) + { + TArray SaveStateBuffer; + + if (!FFileHelper::LoadFileToArray(SaveStateBuffer, *SaveStatePath)) + { + UE_LOG(Libretro, Warning, TEXT("Couldn't load save state '%s' error code:%u"), *SaveStatePath, FPlatformMisc::GetLastError()); + return; // We just assume failure means the file did not exist and we do nothing + } + + if (SaveStateBuffer.Num() != libretro_api.serialize_size()) // because of emulator versions these might not match up also some Libretro cores don't follow spec so the size can change between calls to serialize_size + { + UE_LOG(Libretro, Warning, TEXT("Save state file size specified by '%s' did not match the save state size in folder. File Size : %d Core Size: %zu. Going to try to load it anyway."), *CorePath, SaveStateBuffer.Num(), libretro_api.serialize_size()) + } + + libretro_api.unserialize(SaveStateBuffer.GetData(), SaveStateBuffer.Num()); + }); +} + +void ULibretroCoreInstance::SaveState(const FString& FilePath) +{ + NOT_LAUNCHED_GUARD + + this->CoreInstance.GetValue()->EnqueueTask + ( + [SaveStatePath = FUnrealLibretroModule::ResolveSaveStatePath(RomPath, FilePath)](libretro_api_t& libretro_api) + { + TArray SaveStateBuffer; // @dynamic + SaveStateBuffer.Reserve(libretro_api.serialize_size() + 2); // The plus two is a slight optimization based on how SaveArrayToFile works + SaveStateBuffer.AddUninitialized(libretro_api.serialize_size()); + libretro_api.serialize(static_cast(SaveStateBuffer.GetData()), libretro_api.serialize_size()); + FFileHelper::SaveArrayToFile(SaveStateBuffer, *SaveStatePath); + } + ); +} + +#include "Scalability.h" + +void ULibretroCoreInstance::BeginPlay() +{ + Super::BeginPlay(); + + /*if (Scalability::GetQualityLevels().AntiAliasingQuality) { + FMessageDialog::Open(EAppMsgType::Ok, FText::AsCultureInvariant("You have temporal anti-aliasing enabled. The emulated games will look will look blurry and laggy if you leave this enabled. If you happen to know how to fix this let me know. I tried enabling responsive AA on the material to prevent this, but that didn't work.")); + }*/ +} + +void ULibretroCoreInstance::SetInputDigital(int Port, bool Pressed, ERetroDeviceID Input) +{ + NOT_LAUNCHED_GUARD + + CoreInstance.GetValue()->EnqueueTask([=, CoreInstance = CoreInstance.GetValue()](auto) + { + CoreInstance->InputState[Port][Input] = Pressed; + }); +} + +void ULibretroCoreInstance::SetInputAnalog(int Port, int _16BitSignedInteger, ERetroDeviceID Input) +{ + NOT_LAUNCHED_GUARD + + CoreInstance.GetValue()->EnqueueTask([=, CoreInstance = CoreInstance.GetValue()](auto) + { + CoreInstance->InputState[Port][Input] = _16BitSignedInteger; + }); +} + +void ULibretroCoreInstance::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) +{ + if ( CoreInstance.IsSet() + && KeyboardInputSourcePlayerController) + { + for (int i = 0; i < count_key_bindings; i++) + { + if ( KeyboardInputSourcePlayerController->PlayerInput->WasJustPressed(key_bindings[i].Unreal) + || KeyboardInputSourcePlayerController->PlayerInput->WasJustReleased(key_bindings[i].Unreal)) + { + this->CoreInstance.GetValue()->EnqueueTask( + [=, down = KeyboardInputSourcePlayerController->PlayerInput->WasJustPressed(key_bindings[i].Unreal)] + (libretro_api_t libretro_api) + { + if (libretro_api.keyboard_event) + { + libretro_api.keyboard_event(down, key_bindings[i].libretro, 0, RETROKMOD_NONE); + } + }); + } + } + } +} + +void ULibretroCoreInstance::BeginDestroy() +{ + if (this->CoreInstance.IsSet()) + { + // Save SRam + this->CoreInstance.GetValue()->EnqueueTask( + [SRAMPath = FUnrealLibretroModule::ResolveSRAMPath(RomPath, SRAMPath)](auto libretro_api) + { + auto SRAMBuffer = TArrayView((uint8*)libretro_api.get_memory_data(RETRO_MEMORY_SAVE_RAM), + libretro_api.get_memory_size(RETRO_MEMORY_SAVE_RAM)); + FFileHelper::SaveArrayToFile(SRAMBuffer, *SRAMPath); + }); + + Shutdown(); + } + + Super::BeginDestroy(); +} diff --git a/Source/UnrealLibretro/Private/UnrealLibretro.cpp b/Source/UnrealLibretro/Private/UnrealLibretro.cpp index acc0d93e..8ed8281d 100644 --- a/Source/UnrealLibretro/Private/UnrealLibretro.cpp +++ b/Source/UnrealLibretro/Private/UnrealLibretro.cpp @@ -1,74 +1,74 @@ -#include "UnrealLibretro.h" - -#include "Misc/MessageDialog.h" -#include "Modules/ModuleManager.h" - -#include "LibretroSettings.h" - -DEFINE_LOG_CATEGORY(Libretro) - -#define LOCTEXT_NAMESPACE "FUnrealLibretroModule" - -#if PLATFORM_WINDOWS -void* OpenGLDLL; -PFN_wglCreateContext _wglCreateContext; -PFN_wglDeleteContext _wglDeleteContext; -PFN_wglMakeCurrent _wglMakeCurrent; -PFN_wglGetProcAddress _wglGetProcAddress; -#endif - -void FUnrealLibretroModule::StartupModule() -{ - // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module -#define LIBRETRO_NOTE " Note: disable UnrealLibretro or delete the UnrealLibretro plugin to make this error go away." \ - " You can also post an issue to github." -#define LIBRETRO_MODULE_LOAD_ERROR(msg) FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("LibretroError", msg LIBRETRO_NOTE)); \ - UE_LOG(Libretro, Fatal, TEXT(msg LIBRETRO_NOTE)); - -#if PLATFORM_WINDOWS - FString BaseDir = IPluginManager::Get().FindPlugin("UnrealLibretro")->GetBaseDir(); - RedistDirectory = FPaths::Combine(*BaseDir, TEXT("Binaries/Win64/ThirdParty/libretro/")); - FPlatformProcess::AddDllDirectory(*RedistDirectory); - - OpenGLDLL = FPlatformProcess::GetDllHandle(TEXT("opengl32.dll")); - if (!OpenGLDLL) - { - LIBRETRO_MODULE_LOAD_ERROR("Couldn't load opengl32.dll"); - } - - wglCreateContext = (decltype(wglCreateContext))FPlatformProcess::GetDllExport(OpenGLDLL, TEXT("wglCreateContext")); - wglGetProcAddress = (decltype(wglGetProcAddress))FPlatformProcess::GetDllExport(OpenGLDLL, TEXT("wglGetProcAddress")); - wglDeleteContext = (decltype(wglDeleteContext))FPlatformProcess::GetDllExport(OpenGLDLL, TEXT("wglDeleteContext")); - wglMakeCurrent = (decltype(wglMakeCurrent))FPlatformProcess::GetDllExport(OpenGLDLL, TEXT("wglMakeCurrent")); - - WNDCLASS wc = { 0 }; - wc.lpfnWndProc = DefWindowProc; - wc.hInstance = GetModuleHandle(NULL); - wc.lpszClassName = TEXT("my_opengl_class"); - RegisterClass(&wc); -#endif -} - -void FUnrealLibretroModule::ShutdownModule() -{ - // @todo For now I skip resource cleanup. It could be added back if I added isReadyForFinishDestroy(bool) to ULibretroCoreInstance - // in conjunction with waiting for the LibretroContext to destruct since UE uses the outstanding UObjects from this module visible through - // the reflection system (UProperty, etc) to determine when it is safe to shutdown this module. - // This is because LibretroContext depends on the dlls and paths loaded by this module and is destructed asynchronously and is not a UObject. - // I could also fix the shutdown_audio hack as well as remove the numerous weak pointers in LibretroContext. - // I'm nervous how much the engine will block the game thread on that condition though so that still might not be a solution. -#if 0 - -#if PLATFORM_WINDOWS - FPlatformProcess::FreeDllHandle(OpenGLDLL); - OpenGLDLL = nullptr; - UnregisterClass(TEXT("my_opengl_class"), GetModuleHandle(NULL)); -#endif - - // @todo Remove RedistDirectory from Searchpath -#endif -} - -#undef LOCTEXT_NAMESPACE - -IMPLEMENT_MODULE(FUnrealLibretroModule, UnrealLibretro) +#include "UnrealLibretro.h" + +#include "Misc/MessageDialog.h" +#include "Modules/ModuleManager.h" + +#include "LibretroSettings.h" + +DEFINE_LOG_CATEGORY(Libretro) + +#define LOCTEXT_NAMESPACE "FUnrealLibretroModule" + +#if PLATFORM_WINDOWS +void* OpenGLDLL; +PFN_wglCreateContext _wglCreateContext; +PFN_wglDeleteContext _wglDeleteContext; +PFN_wglMakeCurrent _wglMakeCurrent; +PFN_wglGetProcAddress _wglGetProcAddress; +#endif + +void FUnrealLibretroModule::StartupModule() +{ + // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module +#define LIBRETRO_NOTE " Note: disable UnrealLibretro or delete the UnrealLibretro plugin to make this error go away." \ + " You can also post an issue to github." +#define LIBRETRO_MODULE_LOAD_ERROR(msg) FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("LibretroError", msg LIBRETRO_NOTE)); \ + UE_LOG(Libretro, Fatal, TEXT(msg LIBRETRO_NOTE)); + +#if PLATFORM_WINDOWS + FString BaseDir = IPluginManager::Get().FindPlugin("UnrealLibretro")->GetBaseDir(); + RedistDirectory = FPaths::Combine(*BaseDir, TEXT("Binaries/Win64/ThirdParty/libretro/")); + FPlatformProcess::AddDllDirectory(*RedistDirectory); + + OpenGLDLL = FPlatformProcess::GetDllHandle(TEXT("opengl32.dll")); + if (!OpenGLDLL) + { + LIBRETRO_MODULE_LOAD_ERROR("Couldn't load opengl32.dll"); + } + + wglCreateContext = (decltype(wglCreateContext))FPlatformProcess::GetDllExport(OpenGLDLL, TEXT("wglCreateContext")); + wglGetProcAddress = (decltype(wglGetProcAddress))FPlatformProcess::GetDllExport(OpenGLDLL, TEXT("wglGetProcAddress")); + wglDeleteContext = (decltype(wglDeleteContext))FPlatformProcess::GetDllExport(OpenGLDLL, TEXT("wglDeleteContext")); + wglMakeCurrent = (decltype(wglMakeCurrent))FPlatformProcess::GetDllExport(OpenGLDLL, TEXT("wglMakeCurrent")); + + WNDCLASS wc = { 0 }; + wc.lpfnWndProc = DefWindowProc; + wc.hInstance = GetModuleHandle(NULL); + wc.lpszClassName = TEXT("my_opengl_class"); + RegisterClass(&wc); +#endif +} + +void FUnrealLibretroModule::ShutdownModule() +{ + // @todo For now I skip resource cleanup. It could be added back if I added isReadyForFinishDestroy(bool) to ULibretroCoreInstance + // in conjunction with waiting for the LibretroContext to destruct since UE uses the outstanding UObjects from this module visible through + // the reflection system (UProperty, etc) to determine when it is safe to shutdown this module. + // This is because LibretroContext depends on the dlls and paths loaded by this module and is destructed asynchronously and is not a UObject. + // I could also fix the shutdown_audio hack as well as remove the numerous weak pointers in LibretroContext. + // I'm nervous how much the engine will block the game thread on that condition though so that still might not be a solution. +#if 0 + +#if PLATFORM_WINDOWS + FPlatformProcess::FreeDllHandle(OpenGLDLL); + OpenGLDLL = nullptr; + UnregisterClass(TEXT("my_opengl_class"), GetModuleHandle(NULL)); +#endif + + // @todo Remove RedistDirectory from Searchpath +#endif +} + +#undef LOCTEXT_NAMESPACE + +IMPLEMENT_MODULE(FUnrealLibretroModule, UnrealLibretro) diff --git a/Source/UnrealLibretro/Private/sdlarch.cpp b/Source/UnrealLibretro/Private/sdlarch.cpp index d4df64d1..7f0023ea 100644 --- a/Source/UnrealLibretro/Private/sdlarch.cpp +++ b/Source/UnrealLibretro/Private/sdlarch.cpp @@ -1,43 +1,43 @@ - -#include "sdlarch.h" -extern "C" -{ -#include "gfx/scaler/pixconv.h" -} - -#include "LibretroCoreInstance.h" -#include "UnrealLibretro.h" // For Libretro debug log category -#include "LibretroSettings.h" -#include "LibretroInputDefinitions.h" -#include "LambdaRunnable.h" - -#include "HAL/FileManager.h" - -#if PLATFORM_APPLE -#include -#endif - -#if PLATFORM_WINDOWS -#include "Windows/AllowWindowsPlatformTypes.h" -#include "Windows/PreWindowsApi.h" -#include -#include -#include "Windows/PostWindowsApi.h" -#include "Windows/HideWindowsPlatformTypes.h" -#endif - -#if PLATFORM_ANDROID -#define GL_GET_PROC_ADDRESS eglGetProcAddress - -#include // requires ndk r5 or newer -#include // requires ndk r5 or newer -#endif - -// Android errors trying to use debug context for some reason even with EGL 1.5 -#if defined(DEBUG_OPENGL) && !PLATFORM_ANDROID -#define DEBUG_OPENGL_CALLBACK -#endif - + +#include "sdlarch.h" +extern "C" +{ +#include "gfx/scaler/pixconv.h" +} + +#include "LibretroCoreInstance.h" +#include "UnrealLibretro.h" // For Libretro debug log category +#include "LibretroSettings.h" +#include "LibretroInputDefinitions.h" +#include "LambdaRunnable.h" + +#include "HAL/FileManager.h" + +#if PLATFORM_APPLE +#include +#endif + +#if PLATFORM_WINDOWS +#include "Windows/AllowWindowsPlatformTypes.h" +#include "Windows/PreWindowsApi.h" +#include +#include +#include "Windows/PostWindowsApi.h" +#include "Windows/HideWindowsPlatformTypes.h" +#endif + +#if PLATFORM_ANDROID +#define GL_GET_PROC_ADDRESS eglGetProcAddress + +#include // requires ndk r5 or newer +#include // requires ndk r5 or newer +#endif + +// Android errors trying to use debug context for some reason even with EGL 1.5 +#if defined(DEBUG_OPENGL) && !PLATFORM_ANDROID +#define DEBUG_OPENGL_CALLBACK +#endif + // MY EYEEEEESSS.... Even though this looks heavily obfuscated what this actually accomplishes is relatively simple. It allows us to run multiple libretro cores at once. // We have to do it this way because when libretro calls a callback we implemented there really isn't any suitable way to tell which core the call came from. // So we just statically generate a bunch of callback functions with macros and write their function pointers into an array of libretro_callbacks_t's and issue them at runtime. @@ -74,820 +74,820 @@ extern struct libretro_callbacks_t { REP100(FUNC_WRAP_DEF) libretro_callbacks_t libretro_callbacks_table[] = { REP100(FUNC_WRAP_INIT) }; - -#define load_sym(V, S) do {\ - if (0 == ((*(void**)&V) = FPlatformProcess::GetDllExport(libretro_api.handle, TEXT(#S)))) \ - UE_LOG(Libretro, Fatal, TEXT("Failed to load symbol '" #S "'': %u"), FPlatformMisc::GetLastError()); \ - } while (0) -#define load_retro_sym(S) load_sym(libretro_api.S, retro_##S) - - -void glDebugOutput(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam){ - - switch (severity) { - case GL_DEBUG_SEVERITY_HIGH: - UE_LOG(Libretro, Error, TEXT("OpenGL Debug: %s"), ANSI_TO_TCHAR(message)); - break; - case GL_DEBUG_SEVERITY_MEDIUM: - UE_LOG(Libretro, Warning, TEXT("OpenGL Debug: %s"), ANSI_TO_TCHAR(message)); - break; - case GL_DEBUG_SEVERITY_LOW: - UE_LOG(Libretro, Log, TEXT("OpenGL Debug: %s"), ANSI_TO_TCHAR(message)); - break; - case GL_DEBUG_SEVERITY_NOTIFICATION: - default: - UE_LOG(Libretro, Verbose, TEXT("OpenGL Debug: %s"), ANSI_TO_TCHAR(message)); - break; - } - -} - -static PFNGLGETERRORPROC glGetError; - -#if defined(DEBUG_OPENGL) && !defined(DEBUG_OPENGL_CALLBACK) -#define LogGLErrors(x) GLClearErrors();\ - x;\ - GLLogCall(#x, __FILE__, __LINE__) -#else -#define LogGLErrors(x) x -#endif - -static void GLClearErrors() -{ - /* loop while there are errors and until GL_NO_ERROR is returned */ - while (glGetError() != GL_NO_ERROR); -} - -static bool GLLogCall(const char* function, const char* file, int line) -{ - while (GLenum error = glGetError()) - { - UE_LOG(Libretro, Error, TEXT("OpenGL: %s:%s:%d: GLenum (%d)"), ANSI_TO_TCHAR(function), ANSI_TO_TCHAR(file), line, error); - return false; - } - - return true; -} - - void LibretroContext::create_window() { -#if PLATFORM_ANDROID - // Get an OpenGL context via EGL - const EGLint attribs[] = { - EGL_BLUE_SIZE, 8, - EGL_GREEN_SIZE, 8, - EGL_RED_SIZE, 8, - EGL_NONE - }; - - EGLConfig config; - EGLint numConfigs; - - if ((core.gl.egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY)) == EGL_NO_DISPLAY) { - UE_LOG(Libretro, Fatal, TEXT("eglGetDisplay() returned error %d"), eglGetError()); - } - - if (!eglInitialize(core.gl.egl_display, 0, 0)) { - UE_LOG(Libretro, Fatal, TEXT("eglInitialize() returned error %d"), eglGetError()); - } - - if (!eglChooseConfig(core.gl.egl_display, attribs, &config, 1, &numConfigs)) { - UE_LOG(Libretro, Fatal, TEXT("eglChooseConfig() returned error %d"), eglGetError()); - } - - const EGLint attrib_list[] = { - EGL_CONTEXT_MAJOR_VERSION, (EGLint) core.hw.version_major, - EGL_CONTEXT_MINOR_VERSION, (EGLint) core.hw.version_minor, -#if defined(DEBUG_OPENGL_CALLBACK) - EGL_CONTEXT_OPENGL_DEBUG, EGL_TRUE, -#endif - EGL_NONE - }; - - UE_LOG(Libretro, Log, TEXT("EGL: Trying to load OpenGL ES version %d.%d"), core.hw.version_major, core.hw.version_minor); - if (!(core.gl.egl_context = eglCreateContext(core.gl.egl_display, config, 0, attrib_list))) { - UE_LOG(Libretro, Fatal, TEXT("eglCreateContext() returned error %d"), eglGetError()); - } - - //static_assert(EGL_KHR_surfaceless_context, TEXT("This check may break as a false positive. ndk r21 defines this as a macro might need to be queried at runtime on other platforms")); - if (!eglMakeCurrent(core.gl.egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, core.gl.egl_context)) { - UE_LOG(Libretro, Fatal, TEXT("eglMakeCurrent() returned error %d"), eglGetError()); - } -#elif PLATFORM_WINDOWS - // Use wgl to create a hidden window to get an OpenGL context - core.gl.window = CreateWindowEx( - 0, TEXT("my_opengl_class"), TEXT("OpenGL Window"), - WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_POPUP, - 0, 0, 1, 1, - NULL, NULL, GetModuleHandle(NULL), NULL - ); - - if (!core.gl.window) { - UE_LOG(Libretro, Fatal, TEXT("Failed to create window")); - } - - core.gl.hdc = GetDC(core.gl.window); - - PIXELFORMATDESCRIPTOR pfd = { - sizeof(PIXELFORMATDESCRIPTOR), 1, - PFD_SUPPORT_OPENGL, - PFD_TYPE_RGBA, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - PFD_MAIN_PLANE, 0, 0, 0, 0 - }; - - int pixel_format = ChoosePixelFormat(core.gl.hdc, &pfd); - if (!pixel_format) { - UE_LOG(Libretro, Fatal, TEXT("Failed to choose pixel format")); - } - - if (!SetPixelFormat(core.gl.hdc, pixel_format, &pfd)) { - UE_LOG(Libretro, Fatal, TEXT("Failed to set pixel format")); - } - - core.gl.context = wglCreateContext(core.gl.hdc); - if (!core.gl.context) { - UE_LOG(Libretro, Fatal, TEXT("Failed to create OpenGL context")); - } - - if (!wglMakeCurrent(core.gl.hdc, core.gl.context)) { - UE_LOG(Libretro, Fatal, TEXT("Failed to activate OpenGL context")); - } -#else - // @todo Other platforms don't have routines to get OpenGL contexts currently - // GLFW's interface has an oversight that windows and contexts are created together (You could expose some of it's internals to work around this) - // and SDL is kind of bloated and wasn't flexible enough for Android to work with it - // My current thoughts are use Google ANGLE since then the EGL code used above for PLATFORM_ANDROID should work without modification - // ANGLE potentially could allow for easier interop with Unreal's RHI since you can run ANGLE on top of it if its DX12 or Vulkan - // However the main issue with ANGLE is that it seems the Libretro Cores need to be built for it in mind and I don't know if that feature is being actively developed right now - checkNoEntry(); -#endif - - #pragma warning(push) - #pragma warning(disable:4191) - - // Initialize all entry points. - #define GET_GL_PROCEDURES(Type,Func) Func = (Type)GL_GET_PROC_ADDRESS(#Func); - ENUM_GL_PROCEDURES(GET_GL_PROCEDURES); - ENUM_GL_WIN32_INTEROP_PROCEDURES(GET_GL_PROCEDURES); - - // Check that all of the entry points have been initialized. - bool bFoundAllEntryPoints = true; - #define CHECK_GL_PROCEDURES(Type,Func) if (Func == NULL) { bFoundAllEntryPoints = false; UE_LOG(Libretro, Warning, TEXT("Failed to find entry point for %s"), TEXT(#Func)); } - ENUM_GL_PROCEDURES(CHECK_GL_PROCEDURES); - checkf(bFoundAllEntryPoints, TEXT("Failed to find all OpenGL entry points.")); - - ENUM_GL_WIN32_INTEROP_PROCEDURES(CHECK_GL_PROCEDURES); - this->gl_win32_interop_supported_by_driver = false; // bFoundAllEntryPoints; Not ready - - glGetError = (PFNGLGETERRORPROC)GL_GET_PROC_ADDRESS("glGetError"); - check(glGetError); - - // Restore warning C4191. - #pragma warning(pop) -#if defined(DEBUG_OPENGL_CALLBACK) - GLint opengl_flags; - GLint major_version; - GLint minor_version; - bool is_OpenGL_ES = strncmp("OpenGL ES", (char *)glGetString(GL_SHADING_LANGUAGE_VERSION), strlen("OpenGL ES")) == 0; - glGetIntegerv(GL_CONTEXT_FLAGS, &opengl_flags); - glGetIntegerv(GL_MAJOR_VERSION, &major_version); - glGetIntegerv(GL_MINOR_VERSION, &minor_version); - if ( major_version >= 4 && minor_version >= 3 && !is_OpenGL_ES - || major_version >= 3 && minor_version >= 2 && is_OpenGL_ES - && (opengl_flags & GL_CONTEXT_FLAG_DEBUG_BIT)) - { - glEnable(GL_DEBUG_OUTPUT); - glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); - glDebugMessageCallback(glDebugOutput, nullptr); - glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE); - } -#endif - - UE_LOG(Libretro, Log, TEXT("GL_SHADING_LANGUAGE_VERSION: %s\n"), ANSI_TO_TCHAR((char*)glGetString(GL_SHADING_LANGUAGE_VERSION))); - UE_LOG(Libretro, Log, TEXT("GL_VERSION: %s\n"), ANSI_TO_TCHAR((char*)glGetString(GL_VERSION))); -} - - void LibretroContext::video_configure(const struct retro_game_geometry *geom) { - if (!core.gl.pixel_format) { - auto data = RETRO_PIXEL_FORMAT_0RGB1555; - this->core_environment(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &data); - } - - // Unreal Resource init - void *SharedHandle = nullptr; - - uint64_t SizeInBytes, MipLevels; - GLenum handleType; - FTaskGraphInterface::Get().WaitUntilTaskCompletes( - FFunctionGraphTask::CreateAndDispatchWhenReady([&] - { - const unsigned CapacityMilliseconds = 50; - const unsigned CapacityFrames = CapacityMilliseconds * (core.av.timing.sample_rate / 1000.0); - Unreal.AudioQueue = MakeShared, ESPMode::ThreadSafe>(CapacityFrames); // @todo move to audio init when the hack below is removed - - // Make sure the game objects haven't been GCed - if (!UnrealSoundBuffer.IsValid() || !UnrealRenderTarget.IsValid()) - { // @hack until we acquire our own resources and don't rely on getting them by proxy through a UObject - ENQUEUE_RENDER_COMMAND(LibretroInitDummyRHIFramebuffer) - ([this](FRHICommandListImmediate& RHICmdList) - { - FRHIResourceCreateInfo Info{ TEXT("Dummy Texture for now") }; - this->Unreal.TextureRHI = RHICreateTexture2D(core.av.geometry.max_width, - core.av.geometry.max_height, - PF_R8G8B8A8, - 1, - 1, - TexCreate_CPUWritable | TexCreate_Dynamic, - Info); - }); - } - else - { - // Video Init - UnrealRenderTarget->bGPUSharedFlag = true; // Allows us to share this rendertarget with other applications and APIs in this case OpenGL - UnrealRenderTarget->InitCustomFormat(core.av.geometry.max_width, - core.av.geometry.max_height, - PF_R8G8B8A8, - false); - ENQUEUE_RENDER_COMMAND(LibretroInitRHIFramebuffer) - ([&](FRHICommandListImmediate& RHICmdList) - { - const auto Resource = static_cast(UnrealRenderTarget->GetRenderTargetResource()); - this->Unreal.TextureRHI = Resource->GetTextureRHI(); - - if (gl_win32_interop_supported_by_driver) - { -#if PLATFORM_WINDOWS - if ((FString)GDynamicRHI->GetName() == TEXT("D3D12")) - { - auto UE4D3DDevice = static_cast(GDynamicRHI->RHIGetNativeDevice()); - static FThreadSafeCounter NamingIdx; - ID3D12Resource* ResolvedTexture = (ID3D12Resource*)this->Unreal.TextureRHI->GetTexture2D()->GetNativeResource(); - D3D12_RESOURCE_DESC TextureAttributes = ResolvedTexture->GetDesc(); - D3D12_RESOURCE_ALLOCATION_INFO TextureMemoryUsage = UE4D3DDevice->GetResourceAllocationInfo(0b0, 1, &TextureAttributes); - verify(!FAILED(UE4D3DDevice->CreateSharedHandle(ResolvedTexture, NULL, GENERIC_ALL, *FString::Printf(TEXT("OpenGLSharedFramebuffer_UnrealLibretro_%u"), NamingIdx.Increment()), &SharedHandle))); - check(SharedHandle); - - MipLevels = TextureAttributes.MipLevels; - SizeInBytes = TextureMemoryUsage.SizeInBytes; - handleType = GL_HANDLE_TYPE_D3D12_RESOURCE_EXT; - } -#endif - } - }); - FlushRenderingCommands(); - - // Audio init - UnrealSoundBuffer->SetSampleRate(core.av.timing.sample_rate); - UnrealSoundBuffer->NumChannels = 2; - UnrealSoundBuffer->AudioQueue = Unreal.AudioQueue; - } - - }, TStatId(), nullptr, ENamedThreads::GameThread) - ); // mfence - - // Libretro Core resource init - if (core.using_opengl) { - if (gl_win32_interop_supported_by_driver && SharedHandle != nullptr) { -#if PLATFORM_WINDOWS - UE_LOG(Libretro, Log, TEXT("Sharing RHI RenderTarget memory with OpenGL")) - glCreateMemoryObjectsEXT(1, &core.gl.rhi_interop_memory); - glImportMemoryWin32HandleEXT(core.gl.rhi_interop_memory, SizeInBytes, handleType, SharedHandle); - glCreateTextures(GL_TEXTURE_2D, 1, &core.gl.texture); - glTextureStorageMem2DEXT(core.gl.texture, MipLevels, GL_RGBA8, core.av.geometry.max_width, - core.av.geometry.max_height, - core.gl.rhi_interop_memory, 0); - - // NOTE: ID3D12Device::CreateSharedHandle gives as an NT Handle, and so we need to call CloseHandle on it - if ((FString)GDynamicRHI->GetName() == TEXT("D3D12")) - { - verify(CloseHandle(SharedHandle)); - } -#endif - } else { // RHI Interop not supported fallback to creating OpenGL framebuffer - glGenTextures(1, &core.gl.texture); - - glBindTexture(GL_TEXTURE_2D, core.gl.texture); - LogGLErrors(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, geom->max_width, - geom->max_height, - 0, - core.gl.pixel_format, - core.gl.pixel_type, - NULL)); - glBindTexture(GL_TEXTURE_2D, 0); - } - - - glGenFramebuffers(1, &core.gl.framebuffer); - glBindFramebuffer(GL_FRAMEBUFFER, core.gl.framebuffer); - - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, core.gl.texture, 0); - - if ( core.hw.depth - && core.hw.stencil) { - glGenRenderbuffers(1, &core.gl.renderbuffer); - glBindRenderbuffer(GL_RENDERBUFFER, core.gl.renderbuffer); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, geom->max_width, - geom->max_height); - - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, core.gl.renderbuffer); - } - else if (core.hw.depth) { - glGenRenderbuffers(1, &core.gl.renderbuffer); - glBindRenderbuffer(GL_RENDERBUFFER, core.gl.renderbuffer); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, geom->max_width, - geom->max_height); - - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, core.gl.renderbuffer); - } - - glBindRenderbuffer(GL_RENDERBUFFER, 0); - - check(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); - - glClearColor(0, 0, 0, 1); - glClear(GL_COLOR_BUFFER_BIT); - - glBindFramebuffer(GL_FRAMEBUFFER, 0); - - { - glGenBuffers(2, core.gl.pixel_buffer_objects); - for (int i = 0; i < sizeof(core.gl.pixel_buffer_objects) / sizeof(GLuint); i++) - { - glBindBuffer(GL_PIXEL_PACK_BUFFER, core.gl.pixel_buffer_objects[i]); - glBufferData(GL_PIXEL_PACK_BUFFER, 4 * geom->max_width * geom->max_height, 0, GL_DYNAMIC_READ); - } - glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); - } - } else { - for (auto i : { 0, 1 }) { - core.software.bgra_buffers[i] = FMemory::Malloc(4 * core.av.geometry.max_width - * core.av.geometry.max_height, PLATFORM_CACHE_LINE_SIZE); - } - } - - core.hw.context_reset(); -} - -#include "Async/TaskGraphInterfaces.h" -// Stripped down code for profiling purposes https://godbolt.org/z/c57esx - void LibretroContext::core_video_refresh(const void *data, unsigned width, unsigned height, unsigned pitch) { - DECLARE_SCOPE_CYCLE_COUNTER(TEXT("PrepareFrameBufferForRenderThread"), STAT_LibretroPrepareFrameBufferForRenderThread, STATGROUP_UnrealLibretro); - - unsigned SrcPitch = 4 * core.av.geometry.max_width; - - auto prepare_frame_for_upload_to_unreal_RHI = [&](void* const buffer) - { - void* old_buffer; - { - FScopeLock SwapPointer(&this->Unreal.FrameUpload.CriticalSection); - old_buffer = this->Unreal.FrameUpload.ClientBuffer; - this->Unreal.FrameUpload.ClientBuffer = buffer; - } - - if (!old_buffer) - { - ENQUEUE_RENDER_COMMAND(CopyToUnrealFramebufferTask)( // @todo this triggers an assert on MacOS you can get around it by enqueuing through the TaskGraph instead no idea why this is the case - [this, - MipIndex = 0, - SrcPitch, - Region = FUpdateTextureRegion2D(0, 0, 0, 0, width, height)] - (FRHICommandListImmediate& RHICmdList) - { - check(this->Unreal.TextureRHI.GetReference()); - - RHICmdList.EnqueueLambda([=](FRHICommandList&) - { - // Potentially this should be a TryLock() so you don't preempt the render thread although it's unlikely that would happen - FScopeLock UploadTextureToRenderHardware(&this->Unreal.FrameUpload.CriticalSection); - GDynamicRHI->RHIUpdateTexture2D( - this->Unreal.TextureRHI.GetReference(), - MipIndex, - Region, - SrcPitch, - (uint8*)this->Unreal.FrameUpload.ClientBuffer); - this->Unreal.FrameUpload.ClientBuffer = nullptr; - } - ); - } - ); - } - }; - - if (data && data != RETRO_HW_FRAME_BUFFER_VALID) { - DECLARE_SCOPE_CYCLE_COUNTER(TEXT("CPUConvertAndCopyFramebuffer"), STAT_LibretroCPUConvertAndCopyFramebuffer, STATGROUP_UnrealLibretro); - - auto bgra_buffer = core.software.bgra_buffers[core.free_framebuffer_index = !core.free_framebuffer_index]; - - switch (core.gl.pixel_type) { - case GL_UNSIGNED_SHORT_5_6_5: { - conv_rgb565_abgr8888(bgra_buffer, data, - width, height, - SrcPitch, pitch); - } - break; - case GL_UNSIGNED_SHORT_5_5_5_1: { - checkNoEntry(); - } - break; - case GL_UNSIGNED_BYTE: { - conv_argb8888_abgr8888(bgra_buffer, data, - width, height, - SrcPitch, pitch); - } - break; - default: - checkNoEntry(); - } - - prepare_frame_for_upload_to_unreal_RHI(bgra_buffer); - } - else if (data == RETRO_HW_FRAME_BUFFER_VALID) { - check(core.using_opengl && core.gl.pixel_type == GL_UNSIGNED_BYTE); - - if (core.gl.rhi_interop_memory) { - // @todo I make no attempt to synchronize the core's drawing operations with RHI reads, some cores will work but others have synchronization issues - // It seems like a good reference resource on how to do this is either ITextureShareItem Engine/Source/Programs/TextureShare/TextureShareSDK - } else { - // OpenGL is asynchronous and because of GPU driver reasons (work is executed FIFO for some drivers) - // if we try reading the framebuffer we'll block here and consequently the framerate will be capped by Unreal Engines framerate - // which will cause stuttering if its too low since most emulated games logic is tied to the framerate so we async copy the framebuffer and check a fence later - - switch (glClientWaitSync(core.gl.fence, GL_SYNC_FLUSH_COMMANDS_BIT, 0)) { - case GL_TIMEOUT_EXPIRED: - UE_LOG(Libretro, Verbose, TEXT("Frame didn't render in time will try copying next time...")) - break; - case GL_ALREADY_SIGNALED: - case GL_CONDITION_SATISFIED: - { - DECLARE_SCOPE_CYCLE_COUNTER(TEXT("GPUAsyncCopy"), STAT_LibretroGPUAsyncCopy, STATGROUP_UnrealLibretro); - { // Hand off previously copied frame to Unreal - glBindBuffer(GL_PIXEL_PACK_BUFFER, core.gl.pixel_buffer_objects[!core.free_framebuffer_index]); - - void* frame_buffer = glMapBufferRange(GL_PIXEL_PACK_BUFFER, - 0, // Offset - 4 * core.av.geometry.max_width * core.av.geometry.max_height, - GL_MAP_READ_BIT); - check(frame_buffer); - prepare_frame_for_upload_to_unreal_RHI(frame_buffer); - } - - { // Download Libretro Core frame from OpenGL asynchronously - LogGLErrors(glBindFramebuffer(GL_READ_FRAMEBUFFER, core.gl.framebuffer)); - LogGLErrors(glBindBuffer(GL_PIXEL_PACK_BUFFER, core.gl.pixel_buffer_objects[core.free_framebuffer_index])); - LogGLErrors(glReadBuffer(GL_COLOR_ATTACHMENT0)); - verify(glUnmapBuffer(GL_PIXEL_PACK_BUFFER) == GL_TRUE); - { // Async copy bound framebuffer color component into bound pbo - GLint mip_level = 0; - void* offset_into_pbo_where_data_is_written = 0x0; - // This call is async always and a DMA transfer on most platforms - LogGLErrors(glReadPixels(0, 0, - core.av.geometry.max_width, // @enhancement Only copy the portion of the buffer we need rather than the max possible potential size - core.av.geometry.max_height, - GL_RGBA, - GL_UNSIGNED_BYTE, - offset_into_pbo_where_data_is_written)); - } - glDeleteSync(core.gl.fence); - core.gl.fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - } - - core.free_framebuffer_index = !core.free_framebuffer_index; - - LogGLErrors(glBindFramebuffer(GL_FRAMEBUFFER, 0)); - LogGLErrors(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0)); - break; - } - case GL_WAIT_FAILED: - default: - checkNoEntry(); - } - } - } - else { - // *Duplicate frame* - return; - } -} - -size_t LibretroContext::core_audio_write(const int16_t *buf, size_t frames) { - unsigned FramesEnqueued = 0; - while (FramesEnqueued < frames && Unreal.AudioQueue->Enqueue(((int32*)buf)[FramesEnqueued])) { - FramesEnqueued++; - } - - if (FramesEnqueued != frames) { - UE_LOG(Libretro, Verbose, TEXT("Buffer underrun: %u"), frames - FramesEnqueued); - } - - return frames; - -} - - -static void core_log(enum retro_log_level level, const char *fmt, ...) { - char buffer[4096] = {0}; - static const char * levelstr[] = { "dbg", "inf", "wrn", "err" }; - va_list va; - - va_start(va, fmt); - vsnprintf(buffer, sizeof(buffer), fmt, va); - va_end(va); - - switch (level) { - case RETRO_LOG_DEBUG: - UE_LOG(Libretro, Log, TEXT("%s"), ANSI_TO_TCHAR(buffer)); - break; - case RETRO_LOG_INFO: - UE_LOG(Libretro, Log, TEXT("%s"), ANSI_TO_TCHAR(buffer)); - break; - case RETRO_LOG_WARN: - UE_LOG(Libretro, Warning, TEXT("%s"), ANSI_TO_TCHAR(buffer)); - break; - case RETRO_LOG_ERROR: - UE_LOG(Libretro, Warning, TEXT("%s"), ANSI_TO_TCHAR(buffer)); - break; - } - -} - -bool LibretroContext::core_environment(unsigned cmd, void *data) { - bool delegate_status{false}; - if (CoreEnvironmentCallback) - { - delegate_status = CoreEnvironmentCallback(cmd, data); - } - - switch (cmd) { - case RETRO_ENVIRONMENT_GET_VARIABLE: { - retro_variable* var = (struct retro_variable*)data; - - std::string key(var->key); - - if (core.settings.find(key) != core.settings.end()) { - var->value = core.settings.at(key).c_str(); - return true; - } - - return false; - } - case RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE: { - const struct retro_variable* var = (const struct retro_variable*)data; - //checkNoEntry(); - return false; - } - case RETRO_ENVIRONMENT_SET_VARIABLES: { - const struct retro_variable* arr_var = (const struct retro_variable*)data; - - do { - // Initialize key - const std::string key(arr_var->key); - - // Don't override custom options already set - if (core.settings.count(key) == 0) { - // Parse and initialize default setting, First delimited setting is default by Libretro convention - auto advance_past_space = [](const char* x) { while (*x == ' ') { x++; } return x; }; - auto past_comment = advance_past_space(strchr(arr_var->value, ';') + 1); - const char *delimiter_ptr = strchr(arr_var->value, '|'); - if (delimiter_ptr == nullptr) delimiter_ptr = strchr(arr_var->value, '\0'); - std::string default_setting(past_comment, delimiter_ptr - past_comment); - - // Write setting to table - core.settings[key] = default_setting; - } - - } while ((++arr_var)->key); - - return true; - } - case RETRO_ENVIRONMENT_GET_LOG_INTERFACE: { - struct retro_log_callback *cb = (struct retro_log_callback *)data; - cb->log = core_log; - return true; - } - case RETRO_ENVIRONMENT_GET_CAN_DUPE: { - bool *bval = (bool *)data; - *bval = true; - return true; - } - - case RETRO_ENVIRONMENT_SET_PIXEL_FORMAT: { - const enum retro_pixel_format *format = (enum retro_pixel_format *)data; - - if (*format > RETRO_PIXEL_FORMAT_RGB565) - return false; - - if (core.gl.texture) - UE_LOG(Libretro, Fatal, TEXT("Tried to change pixel format after initialization.")); - - switch (*format) { - case RETRO_PIXEL_FORMAT_0RGB1555: - core.gl.pixel_type = GL_UNSIGNED_SHORT_5_5_5_1; - core.gl.pixel_format = GL_BGRA; - core.gl.bits_per_pixel = sizeof(uint16_t); - break; - case RETRO_PIXEL_FORMAT_XRGB8888: - core.gl.pixel_type = GL_UNSIGNED_BYTE; - core.gl.pixel_format = GL_RGBA; - core.gl.bits_per_pixel = sizeof(uint32_t); - break; - case RETRO_PIXEL_FORMAT_RGB565: - core.gl.pixel_type = GL_UNSIGNED_SHORT_5_6_5; - core.gl.pixel_format = GL_RGB; - core.gl.bits_per_pixel = sizeof(uint16_t); - break; - default: - UE_LOG(Libretro, Fatal, TEXT("Unknown pixel type %u"), *format); - } - - return true; - } - case RETRO_ENVIRONMENT_SET_HW_RENDER: { - struct retro_hw_render_callback *hw = (struct retro_hw_render_callback*)data; - check(hw->context_type < RETRO_HW_CONTEXT_VULKAN); + +#define load_sym(V, S) do {\ + if (0 == ((*(void**)&V) = FPlatformProcess::GetDllExport(libretro_api.handle, TEXT(#S)))) \ + UE_LOG(Libretro, Fatal, TEXT("Failed to load symbol '" #S "'': %u"), FPlatformMisc::GetLastError()); \ + } while (0) +#define load_retro_sym(S) load_sym(libretro_api.S, retro_##S) + + +void glDebugOutput(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam){ + + switch (severity) { + case GL_DEBUG_SEVERITY_HIGH: + UE_LOG(Libretro, Error, TEXT("OpenGL Debug: %s"), ANSI_TO_TCHAR(message)); + break; + case GL_DEBUG_SEVERITY_MEDIUM: + UE_LOG(Libretro, Warning, TEXT("OpenGL Debug: %s"), ANSI_TO_TCHAR(message)); + break; + case GL_DEBUG_SEVERITY_LOW: + UE_LOG(Libretro, Log, TEXT("OpenGL Debug: %s"), ANSI_TO_TCHAR(message)); + break; + case GL_DEBUG_SEVERITY_NOTIFICATION: + default: + UE_LOG(Libretro, Verbose, TEXT("OpenGL Debug: %s"), ANSI_TO_TCHAR(message)); + break; + } + +} + +static PFNGLGETERRORPROC glGetError; + +#if defined(DEBUG_OPENGL) && !defined(DEBUG_OPENGL_CALLBACK) +#define LogGLErrors(x) GLClearErrors();\ + x;\ + GLLogCall(#x, __FILE__, __LINE__) +#else +#define LogGLErrors(x) x +#endif + +static void GLClearErrors() +{ + /* loop while there are errors and until GL_NO_ERROR is returned */ + while (glGetError() != GL_NO_ERROR); +} + +static bool GLLogCall(const char* function, const char* file, int line) +{ + while (GLenum error = glGetError()) + { + UE_LOG(Libretro, Error, TEXT("OpenGL: %s:%s:%d: GLenum (%d)"), ANSI_TO_TCHAR(function), ANSI_TO_TCHAR(file), line, error); + return false; + } + + return true; +} + + void LibretroContext::create_window() { +#if PLATFORM_ANDROID + // Get an OpenGL context via EGL + const EGLint attribs[] = { + EGL_BLUE_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_RED_SIZE, 8, + EGL_NONE + }; + + EGLConfig config; + EGLint numConfigs; + + if ((core.gl.egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY)) == EGL_NO_DISPLAY) { + UE_LOG(Libretro, Fatal, TEXT("eglGetDisplay() returned error %d"), eglGetError()); + } + + if (!eglInitialize(core.gl.egl_display, 0, 0)) { + UE_LOG(Libretro, Fatal, TEXT("eglInitialize() returned error %d"), eglGetError()); + } + + if (!eglChooseConfig(core.gl.egl_display, attribs, &config, 1, &numConfigs)) { + UE_LOG(Libretro, Fatal, TEXT("eglChooseConfig() returned error %d"), eglGetError()); + } + + const EGLint attrib_list[] = { + EGL_CONTEXT_MAJOR_VERSION, (EGLint) core.hw.version_major, + EGL_CONTEXT_MINOR_VERSION, (EGLint) core.hw.version_minor, +#if defined(DEBUG_OPENGL_CALLBACK) + EGL_CONTEXT_OPENGL_DEBUG, EGL_TRUE, +#endif + EGL_NONE + }; + + UE_LOG(Libretro, Log, TEXT("EGL: Trying to load OpenGL ES version %d.%d"), core.hw.version_major, core.hw.version_minor); + if (!(core.gl.egl_context = eglCreateContext(core.gl.egl_display, config, 0, attrib_list))) { + UE_LOG(Libretro, Fatal, TEXT("eglCreateContext() returned error %d"), eglGetError()); + } + + //static_assert(EGL_KHR_surfaceless_context, TEXT("This check may break as a false positive. ndk r21 defines this as a macro might need to be queried at runtime on other platforms")); + if (!eglMakeCurrent(core.gl.egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, core.gl.egl_context)) { + UE_LOG(Libretro, Fatal, TEXT("eglMakeCurrent() returned error %d"), eglGetError()); + } +#elif PLATFORM_WINDOWS + // Use wgl to create a hidden window to get an OpenGL context + core.gl.window = CreateWindowEx( + 0, TEXT("my_opengl_class"), TEXT("OpenGL Window"), + WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_POPUP, + 0, 0, 1, 1, + NULL, NULL, GetModuleHandle(NULL), NULL + ); + + if (!core.gl.window) { + UE_LOG(Libretro, Fatal, TEXT("Failed to create window")); + } + + core.gl.hdc = GetDC(core.gl.window); + + PIXELFORMATDESCRIPTOR pfd = { + sizeof(PIXELFORMATDESCRIPTOR), 1, + PFD_SUPPORT_OPENGL, + PFD_TYPE_RGBA, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + PFD_MAIN_PLANE, 0, 0, 0, 0 + }; + + int pixel_format = ChoosePixelFormat(core.gl.hdc, &pfd); + if (!pixel_format) { + UE_LOG(Libretro, Fatal, TEXT("Failed to choose pixel format")); + } + + if (!SetPixelFormat(core.gl.hdc, pixel_format, &pfd)) { + UE_LOG(Libretro, Fatal, TEXT("Failed to set pixel format")); + } + + core.gl.context = wglCreateContext(core.gl.hdc); + if (!core.gl.context) { + UE_LOG(Libretro, Fatal, TEXT("Failed to create OpenGL context")); + } + + if (!wglMakeCurrent(core.gl.hdc, core.gl.context)) { + UE_LOG(Libretro, Fatal, TEXT("Failed to activate OpenGL context")); + } +#else + // @todo Other platforms don't have routines to get OpenGL contexts currently + // GLFW's interface has an oversight that windows and contexts are created together (You could expose some of it's internals to work around this) + // and SDL is kind of bloated and wasn't flexible enough for Android to work with it + // My current thoughts are use Google ANGLE since then the EGL code used above for PLATFORM_ANDROID should work without modification + // ANGLE potentially could allow for easier interop with Unreal's RHI since you can run ANGLE on top of it if its DX12 or Vulkan + // However the main issue with ANGLE is that it seems the Libretro Cores need to be built for it in mind and I don't know if that feature is being actively developed right now + checkNoEntry(); +#endif + + #pragma warning(push) + #pragma warning(disable:4191) + + // Initialize all entry points. + #define GET_GL_PROCEDURES(Type,Func) Func = (Type)GL_GET_PROC_ADDRESS(#Func); + ENUM_GL_PROCEDURES(GET_GL_PROCEDURES); + ENUM_GL_WIN32_INTEROP_PROCEDURES(GET_GL_PROCEDURES); + + // Check that all of the entry points have been initialized. + bool bFoundAllEntryPoints = true; + #define CHECK_GL_PROCEDURES(Type,Func) if (Func == NULL) { bFoundAllEntryPoints = false; UE_LOG(Libretro, Warning, TEXT("Failed to find entry point for %s"), TEXT(#Func)); } + ENUM_GL_PROCEDURES(CHECK_GL_PROCEDURES); + checkf(bFoundAllEntryPoints, TEXT("Failed to find all OpenGL entry points.")); + + ENUM_GL_WIN32_INTEROP_PROCEDURES(CHECK_GL_PROCEDURES); + this->gl_win32_interop_supported_by_driver = false; // bFoundAllEntryPoints; Not ready + + glGetError = (PFNGLGETERRORPROC)GL_GET_PROC_ADDRESS("glGetError"); + check(glGetError); + + // Restore warning C4191. + #pragma warning(pop) +#if defined(DEBUG_OPENGL_CALLBACK) + GLint opengl_flags; + GLint major_version; + GLint minor_version; + bool is_OpenGL_ES = strncmp("OpenGL ES", (char *)glGetString(GL_SHADING_LANGUAGE_VERSION), strlen("OpenGL ES")) == 0; + glGetIntegerv(GL_CONTEXT_FLAGS, &opengl_flags); + glGetIntegerv(GL_MAJOR_VERSION, &major_version); + glGetIntegerv(GL_MINOR_VERSION, &minor_version); + if ( major_version >= 4 && minor_version >= 3 && !is_OpenGL_ES + || major_version >= 3 && minor_version >= 2 && is_OpenGL_ES + && (opengl_flags & GL_CONTEXT_FLAG_DEBUG_BIT)) + { + glEnable(GL_DEBUG_OUTPUT); + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + glDebugMessageCallback(glDebugOutput, nullptr); + glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE); + } +#endif + + UE_LOG(Libretro, Log, TEXT("GL_SHADING_LANGUAGE_VERSION: %s\n"), ANSI_TO_TCHAR((char*)glGetString(GL_SHADING_LANGUAGE_VERSION))); + UE_LOG(Libretro, Log, TEXT("GL_VERSION: %s\n"), ANSI_TO_TCHAR((char*)glGetString(GL_VERSION))); +} + + void LibretroContext::video_configure(const struct retro_game_geometry *geom) { + if (!core.gl.pixel_format) { + auto data = RETRO_PIXEL_FORMAT_0RGB1555; + this->core_environment(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &data); + } + + // Unreal Resource init + void *SharedHandle = nullptr; + + uint64_t SizeInBytes, MipLevels; + GLenum handleType; + FTaskGraphInterface::Get().WaitUntilTaskCompletes( + FFunctionGraphTask::CreateAndDispatchWhenReady([&] + { + const unsigned CapacityMilliseconds = 50; + const unsigned CapacityFrames = CapacityMilliseconds * (core.av.timing.sample_rate / 1000.0); + Unreal.AudioQueue = MakeShared, ESPMode::ThreadSafe>(CapacityFrames); // @todo move to audio init when the hack below is removed + + // Make sure the game objects haven't been GCed + if (!UnrealSoundBuffer.IsValid() || !UnrealRenderTarget.IsValid()) + { // @hack until we acquire our own resources and don't rely on getting them by proxy through a UObject + ENQUEUE_RENDER_COMMAND(LibretroInitDummyRHIFramebuffer) + ([this](FRHICommandListImmediate& RHICmdList) + { + FRHIResourceCreateInfo Info{ TEXT("Dummy Texture for now") }; + this->Unreal.TextureRHI = RHICreateTexture2D(core.av.geometry.max_width, + core.av.geometry.max_height, + PF_R8G8B8A8, + 1, + 1, + TexCreate_CPUWritable | TexCreate_Dynamic, + Info); + }); + } + else + { + // Video Init + UnrealRenderTarget->bGPUSharedFlag = true; // Allows us to share this rendertarget with other applications and APIs in this case OpenGL + UnrealRenderTarget->InitCustomFormat(core.av.geometry.max_width, + core.av.geometry.max_height, + PF_R8G8B8A8, + false); + ENQUEUE_RENDER_COMMAND(LibretroInitRHIFramebuffer) + ([&](FRHICommandListImmediate& RHICmdList) + { + const auto Resource = static_cast(UnrealRenderTarget->GetRenderTargetResource()); + this->Unreal.TextureRHI = Resource->GetTextureRHI(); + + if (gl_win32_interop_supported_by_driver) + { +#if PLATFORM_WINDOWS + if ((FString)GDynamicRHI->GetName() == TEXT("D3D12")) + { + auto UE4D3DDevice = static_cast(GDynamicRHI->RHIGetNativeDevice()); + static FThreadSafeCounter NamingIdx; + ID3D12Resource* ResolvedTexture = (ID3D12Resource*)this->Unreal.TextureRHI->GetTexture2D()->GetNativeResource(); + D3D12_RESOURCE_DESC TextureAttributes = ResolvedTexture->GetDesc(); + D3D12_RESOURCE_ALLOCATION_INFO TextureMemoryUsage = UE4D3DDevice->GetResourceAllocationInfo(0b0, 1, &TextureAttributes); + verify(!FAILED(UE4D3DDevice->CreateSharedHandle(ResolvedTexture, NULL, GENERIC_ALL, *FString::Printf(TEXT("OpenGLSharedFramebuffer_UnrealLibretro_%u"), NamingIdx.Increment()), &SharedHandle))); + check(SharedHandle); + + MipLevels = TextureAttributes.MipLevels; + SizeInBytes = TextureMemoryUsage.SizeInBytes; + handleType = GL_HANDLE_TYPE_D3D12_RESOURCE_EXT; + } +#endif + } + }); + FlushRenderingCommands(); + + // Audio init + UnrealSoundBuffer->SetSampleRate(core.av.timing.sample_rate); + UnrealSoundBuffer->NumChannels = 2; + UnrealSoundBuffer->AudioQueue = Unreal.AudioQueue; + } + + }, TStatId(), nullptr, ENamedThreads::GameThread) + ); // mfence + + // Libretro Core resource init + if (core.using_opengl) { + if (gl_win32_interop_supported_by_driver && SharedHandle != nullptr) { +#if PLATFORM_WINDOWS + UE_LOG(Libretro, Log, TEXT("Sharing RHI RenderTarget memory with OpenGL")) + glCreateMemoryObjectsEXT(1, &core.gl.rhi_interop_memory); + glImportMemoryWin32HandleEXT(core.gl.rhi_interop_memory, SizeInBytes, handleType, SharedHandle); + glCreateTextures(GL_TEXTURE_2D, 1, &core.gl.texture); + glTextureStorageMem2DEXT(core.gl.texture, MipLevels, GL_RGBA8, core.av.geometry.max_width, + core.av.geometry.max_height, + core.gl.rhi_interop_memory, 0); + + // NOTE: ID3D12Device::CreateSharedHandle gives as an NT Handle, and so we need to call CloseHandle on it + if ((FString)GDynamicRHI->GetName() == TEXT("D3D12")) + { + verify(CloseHandle(SharedHandle)); + } +#endif + } else { // RHI Interop not supported fallback to creating OpenGL framebuffer + glGenTextures(1, &core.gl.texture); + + glBindTexture(GL_TEXTURE_2D, core.gl.texture); + LogGLErrors(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, geom->max_width, + geom->max_height, + 0, + core.gl.pixel_format, + core.gl.pixel_type, + NULL)); + glBindTexture(GL_TEXTURE_2D, 0); + } + + + glGenFramebuffers(1, &core.gl.framebuffer); + glBindFramebuffer(GL_FRAMEBUFFER, core.gl.framebuffer); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, core.gl.texture, 0); + + if ( core.hw.depth + && core.hw.stencil) { + glGenRenderbuffers(1, &core.gl.renderbuffer); + glBindRenderbuffer(GL_RENDERBUFFER, core.gl.renderbuffer); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, geom->max_width, + geom->max_height); + + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, core.gl.renderbuffer); + } + else if (core.hw.depth) { + glGenRenderbuffers(1, &core.gl.renderbuffer); + glBindRenderbuffer(GL_RENDERBUFFER, core.gl.renderbuffer); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, geom->max_width, + geom->max_height); + + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, core.gl.renderbuffer); + } + + glBindRenderbuffer(GL_RENDERBUFFER, 0); + + check(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); + + glClearColor(0, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + { + glGenBuffers(2, core.gl.pixel_buffer_objects); + for (int i = 0; i < sizeof(core.gl.pixel_buffer_objects) / sizeof(GLuint); i++) + { + glBindBuffer(GL_PIXEL_PACK_BUFFER, core.gl.pixel_buffer_objects[i]); + glBufferData(GL_PIXEL_PACK_BUFFER, 4 * geom->max_width * geom->max_height, 0, GL_DYNAMIC_READ); + } + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + } + } else { + for (auto i : { 0, 1 }) { + core.software.bgra_buffers[i] = FMemory::Malloc(4 * core.av.geometry.max_width + * core.av.geometry.max_height, PLATFORM_CACHE_LINE_SIZE); + } + } + + core.hw.context_reset(); +} + +#include "Async/TaskGraphInterfaces.h" +// Stripped down code for profiling purposes https://godbolt.org/z/c57esx + void LibretroContext::core_video_refresh(const void *data, unsigned width, unsigned height, unsigned pitch) { + DECLARE_SCOPE_CYCLE_COUNTER(TEXT("PrepareFrameBufferForRenderThread"), STAT_LibretroPrepareFrameBufferForRenderThread, STATGROUP_UnrealLibretro); + + unsigned SrcPitch = 4 * core.av.geometry.max_width; + + auto prepare_frame_for_upload_to_unreal_RHI = [&](void* const buffer) + { + void* old_buffer; + { + FScopeLock SwapPointer(&this->Unreal.FrameUpload.CriticalSection); + old_buffer = this->Unreal.FrameUpload.ClientBuffer; + this->Unreal.FrameUpload.ClientBuffer = buffer; + } + + if (!old_buffer) + { + ENQUEUE_RENDER_COMMAND(CopyToUnrealFramebufferTask)( // @todo this triggers an assert on MacOS you can get around it by enqueuing through the TaskGraph instead no idea why this is the case + [this, + MipIndex = 0, + SrcPitch, + Region = FUpdateTextureRegion2D(0, 0, 0, 0, width, height)] + (FRHICommandListImmediate& RHICmdList) + { + check(this->Unreal.TextureRHI.GetReference()); + + RHICmdList.EnqueueLambda([=](FRHICommandList&) + { + // Potentially this should be a TryLock() so you don't preempt the render thread although it's unlikely that would happen + FScopeLock UploadTextureToRenderHardware(&this->Unreal.FrameUpload.CriticalSection); + GDynamicRHI->RHIUpdateTexture2D( + this->Unreal.TextureRHI.GetReference(), + MipIndex, + Region, + SrcPitch, + (uint8*)this->Unreal.FrameUpload.ClientBuffer); + this->Unreal.FrameUpload.ClientBuffer = nullptr; + } + ); + } + ); + } + }; + + if (data && data != RETRO_HW_FRAME_BUFFER_VALID) { + DECLARE_SCOPE_CYCLE_COUNTER(TEXT("CPUConvertAndCopyFramebuffer"), STAT_LibretroCPUConvertAndCopyFramebuffer, STATGROUP_UnrealLibretro); + + auto bgra_buffer = core.software.bgra_buffers[core.free_framebuffer_index = !core.free_framebuffer_index]; + + switch (core.gl.pixel_type) { + case GL_UNSIGNED_SHORT_5_6_5: { + conv_rgb565_abgr8888(bgra_buffer, data, + width, height, + SrcPitch, pitch); + } + break; + case GL_UNSIGNED_SHORT_5_5_5_1: { + checkNoEntry(); + } + break; + case GL_UNSIGNED_BYTE: { + conv_argb8888_abgr8888(bgra_buffer, data, + width, height, + SrcPitch, pitch); + } + break; + default: + checkNoEntry(); + } + + prepare_frame_for_upload_to_unreal_RHI(bgra_buffer); + } + else if (data == RETRO_HW_FRAME_BUFFER_VALID) { + check(core.using_opengl && core.gl.pixel_type == GL_UNSIGNED_BYTE); + + if (core.gl.rhi_interop_memory) { + // @todo I make no attempt to synchronize the core's drawing operations with RHI reads, some cores will work but others have synchronization issues + // It seems like a good reference resource on how to do this is either ITextureShareItem Engine/Source/Programs/TextureShare/TextureShareSDK + } else { + // OpenGL is asynchronous and because of GPU driver reasons (work is executed FIFO for some drivers) + // if we try reading the framebuffer we'll block here and consequently the framerate will be capped by Unreal Engines framerate + // which will cause stuttering if its too low since most emulated games logic is tied to the framerate so we async copy the framebuffer and check a fence later + + switch (glClientWaitSync(core.gl.fence, GL_SYNC_FLUSH_COMMANDS_BIT, 0)) { + case GL_TIMEOUT_EXPIRED: + UE_LOG(Libretro, Verbose, TEXT("Frame didn't render in time will try copying next time...")) + break; + case GL_ALREADY_SIGNALED: + case GL_CONDITION_SATISFIED: + { + DECLARE_SCOPE_CYCLE_COUNTER(TEXT("GPUAsyncCopy"), STAT_LibretroGPUAsyncCopy, STATGROUP_UnrealLibretro); + { // Hand off previously copied frame to Unreal + glBindBuffer(GL_PIXEL_PACK_BUFFER, core.gl.pixel_buffer_objects[!core.free_framebuffer_index]); + + void* frame_buffer = glMapBufferRange(GL_PIXEL_PACK_BUFFER, + 0, // Offset + 4 * core.av.geometry.max_width * core.av.geometry.max_height, + GL_MAP_READ_BIT); + check(frame_buffer); + prepare_frame_for_upload_to_unreal_RHI(frame_buffer); + } + + { // Download Libretro Core frame from OpenGL asynchronously + LogGLErrors(glBindFramebuffer(GL_READ_FRAMEBUFFER, core.gl.framebuffer)); + LogGLErrors(glBindBuffer(GL_PIXEL_PACK_BUFFER, core.gl.pixel_buffer_objects[core.free_framebuffer_index])); + LogGLErrors(glReadBuffer(GL_COLOR_ATTACHMENT0)); + verify(glUnmapBuffer(GL_PIXEL_PACK_BUFFER) == GL_TRUE); + { // Async copy bound framebuffer color component into bound pbo + GLint mip_level = 0; + void* offset_into_pbo_where_data_is_written = 0x0; + // This call is async always and a DMA transfer on most platforms + LogGLErrors(glReadPixels(0, 0, + core.av.geometry.max_width, // @enhancement Only copy the portion of the buffer we need rather than the max possible potential size + core.av.geometry.max_height, + GL_RGBA, + GL_UNSIGNED_BYTE, + offset_into_pbo_where_data_is_written)); + } + glDeleteSync(core.gl.fence); + core.gl.fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + } + + core.free_framebuffer_index = !core.free_framebuffer_index; + + LogGLErrors(glBindFramebuffer(GL_FRAMEBUFFER, 0)); + LogGLErrors(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0)); + break; + } + case GL_WAIT_FAILED: + default: + checkNoEntry(); + } + } + } + else { + // *Duplicate frame* + return; + } +} + +size_t LibretroContext::core_audio_write(const int16_t *buf, size_t frames) { + unsigned FramesEnqueued = 0; + while (FramesEnqueued < frames && Unreal.AudioQueue->Enqueue(((int32*)buf)[FramesEnqueued])) { + FramesEnqueued++; + } + + if (FramesEnqueued != frames) { + UE_LOG(Libretro, Verbose, TEXT("Buffer underrun: %u"), frames - FramesEnqueued); + } + + return frames; + +} + + +static void core_log(enum retro_log_level level, const char *fmt, ...) { + char buffer[4096] = {0}; + static const char * levelstr[] = { "dbg", "inf", "wrn", "err" }; + va_list va; + + va_start(va, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, va); + va_end(va); + + switch (level) { + case RETRO_LOG_DEBUG: + UE_LOG(Libretro, Log, TEXT("%s"), ANSI_TO_TCHAR(buffer)); + break; + case RETRO_LOG_INFO: + UE_LOG(Libretro, Log, TEXT("%s"), ANSI_TO_TCHAR(buffer)); + break; + case RETRO_LOG_WARN: + UE_LOG(Libretro, Warning, TEXT("%s"), ANSI_TO_TCHAR(buffer)); + break; + case RETRO_LOG_ERROR: + UE_LOG(Libretro, Warning, TEXT("%s"), ANSI_TO_TCHAR(buffer)); + break; + } + +} + +bool LibretroContext::core_environment(unsigned cmd, void *data) { + bool delegate_status{false}; + if (CoreEnvironmentCallback) + { + delegate_status = CoreEnvironmentCallback(cmd, data); + } + + switch (cmd) { + case RETRO_ENVIRONMENT_GET_VARIABLE: { + retro_variable* var = (struct retro_variable*)data; + + std::string key(var->key); + + if (core.settings.find(key) != core.settings.end()) { + var->value = core.settings.at(key).c_str(); + return true; + } + + return false; + } + case RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE: { + const struct retro_variable* var = (const struct retro_variable*)data; + //checkNoEntry(); + return false; + } + case RETRO_ENVIRONMENT_SET_VARIABLES: { + const struct retro_variable* arr_var = (const struct retro_variable*)data; + + do { + // Initialize key + const std::string key(arr_var->key); + + // Don't override custom options already set + if (core.settings.count(key) == 0) { + // Parse and initialize default setting, First delimited setting is default by Libretro convention + auto advance_past_space = [](const char* x) { while (*x == ' ') { x++; } return x; }; + auto past_comment = advance_past_space(strchr(arr_var->value, ';') + 1); + const char *delimiter_ptr = strchr(arr_var->value, '|'); + if (delimiter_ptr == nullptr) delimiter_ptr = strchr(arr_var->value, '\0'); + std::string default_setting(past_comment, delimiter_ptr - past_comment); + + // Write setting to table + core.settings[key] = default_setting; + } + + } while ((++arr_var)->key); + + return true; + } + case RETRO_ENVIRONMENT_GET_LOG_INTERFACE: { + struct retro_log_callback *cb = (struct retro_log_callback *)data; + cb->log = core_log; + return true; + } + case RETRO_ENVIRONMENT_GET_CAN_DUPE: { + bool *bval = (bool *)data; + *bval = true; + return true; + } + + case RETRO_ENVIRONMENT_SET_PIXEL_FORMAT: { + const enum retro_pixel_format *format = (enum retro_pixel_format *)data; + + if (*format > RETRO_PIXEL_FORMAT_RGB565) + return false; + + if (core.gl.texture) + UE_LOG(Libretro, Fatal, TEXT("Tried to change pixel format after initialization.")); + + switch (*format) { + case RETRO_PIXEL_FORMAT_0RGB1555: + core.gl.pixel_type = GL_UNSIGNED_SHORT_5_5_5_1; + core.gl.pixel_format = GL_BGRA; + core.gl.bits_per_pixel = sizeof(uint16_t); + break; + case RETRO_PIXEL_FORMAT_XRGB8888: + core.gl.pixel_type = GL_UNSIGNED_BYTE; + core.gl.pixel_format = GL_RGBA; + core.gl.bits_per_pixel = sizeof(uint32_t); + break; + case RETRO_PIXEL_FORMAT_RGB565: + core.gl.pixel_type = GL_UNSIGNED_SHORT_5_6_5; + core.gl.pixel_format = GL_RGB; + core.gl.bits_per_pixel = sizeof(uint16_t); + break; + default: + UE_LOG(Libretro, Fatal, TEXT("Unknown pixel type %u"), *format); + } + + return true; + } + case RETRO_ENVIRONMENT_SET_HW_RENDER: { + struct retro_hw_render_callback *hw = (struct retro_hw_render_callback*)data; + check(hw->context_type < RETRO_HW_CONTEXT_VULKAN); hw->get_current_framebuffer = libretro_callbacks->c_get_current_framebuffer; -#pragma warning(push) -#pragma warning(disable:4191) - hw->get_proc_address = (retro_hw_get_proc_address_t)GL_GET_PROC_ADDRESS; -#pragma warning(pop) - core.hw = *hw; - core.using_opengl = true; - - if (core.hw.context_type == RETRO_HW_CONTEXT_OPENGLES2) { core.hw.version_major = 2; core.hw.version_minor = 0; } - if (core.hw.context_type == RETRO_HW_CONTEXT_OPENGLES3) { core.hw.version_major = 3; core.hw.version_minor = 0; } - - return true; - } - case RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO: { - auto system_av_info = *(const struct retro_system_av_info*)data; - - check( system_av_info.geometry.max_height <= core.av.geometry.max_height // @todo buffer reallocation. The libretro core can basically request to reallocate framebuffers atm this requires writing code to potentially reallocate 4 buffers - && system_av_info.geometry.max_width <= core.av.geometry.max_width); // in the case of software rendering the client buffer in opengl the pbo and texture buffer and in both cases the unreal engine rhi buffer on top of this there are - // annoying synchonization issues to deal with. We just assert here so we don't actually have to do that for now - - this->core.av.timing = system_av_info.timing; - - return true; - } - case RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY: { - const char** retro_path = (const char**)data; - auto RAII = StringCast(this->core.save_directory.GetData()); - verify(IFileManager::Get().MakeDirectory(RAII.Get(), true)); - *retro_path = core.save_directory.GetData(); - - return true; - } - case RETRO_ENVIRONMENT_GET_CORE_ASSETS_DIRECTORY: - case RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY: { - const char** retro_path = (const char**)data; - auto RAII = StringCast(this->core.system_directory.GetData()); - verify(IFileManager::Get().MakeDirectory(RAII.Get(), true)); - *retro_path = core.system_directory.GetData(); - - return true; - } - case RETRO_ENVIRONMENT_GET_LANGUAGE: { - unsigned* language = (unsigned*)data; - *language = RETRO_LANGUAGE_ENGLISH; - - return true; - } - case RETRO_ENVIRONMENT_GET_AUDIO_VIDEO_ENABLE: { - // This could potentially be useful if the object in unreal engine displaying the video and audio is either out of sight or earshot - - return false; - } - case RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS: { - auto input_descriptor = (const struct retro_input_descriptor*)data; - - do { - UE_LOG(Libretro, Verbose, TEXT("Button Found: %s"), ANSI_TO_TCHAR(input_descriptor->description)); - } while ((++input_descriptor)->description); - - return true; - } - case RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK: { - auto keyboard_callback = (const struct retro_keyboard_callback*)data; - - libretro_api.keyboard_event = keyboard_callback->callback; - - return true; - } - case RETRO_ENVIRONMENT_SET_CONTROLLER_INFO: { - auto controller_info = (const struct retro_controller_info*)data; - - FUnrealLibretroModule::EnvironmentParseControllerInfo(controller_info, ControllerDescriptions); - - return true; - } - case RETRO_ENVIRONMENT_GET_PREFERRED_HW_RENDER: { - unsigned* library = (unsigned*)data; -#if PLATFORM_ANDROID | PLATFORM_IOS - *library = RETRO_HW_CONTEXT_OPENGLES3; // Unreal Engine minimum spec requires OpenGL ES 3.1 -#else - *library = RETRO_HW_CONTEXT_OPENGL_CORE; -#endif - return true; - } - case RETRO_ENVIRONMENT_SET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE: { - const struct retro_hw_render_context_negotiation_interface* iface = - (const struct retro_hw_render_context_negotiation_interface*)data; - - core.hw_render_context_negotiation = iface; - return false; - } - case RETRO_ENVIRONMENT_GET_LED_INTERFACE: { - auto led_interface = (struct retro_led_interface *) data; - // UAE expects this to not be null even if you don't implement it - led_interface->set_led_state = [](int led, int state) { - // noop - }; - - return false; - } - case RETRO_ENVIRONMENT_GET_FASTFORWARDING: { - auto is_fast_forwarding = (bool*)data; - *is_fast_forwarding = false; - return true; - } - case RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY: { - return false; - } - default: - if (!delegate_status) { - core_log(RETRO_LOG_WARN, "Unhandled env #%u", cmd); - } - - return delegate_status; - } - - return delegate_status; -} - -// Unfinished experiment with less branchy version of this function https://godbolt.org/z/hYeYxr95r -int16_t LibretroContext::core_input_state(unsigned port, unsigned device, unsigned index, unsigned id) { - // Some of the setup to get the core to poll for certain types of input is either done through retro_set_controller_port_device - // or in the handled RETRO_ENVIRONMENT_GET_VARIABLE and RETRO_ENVIRONMENT_SET_CONTROLLER_INFO events in the retro_environment_t - // callback core_environment. Getting the core to handle specific input amounts to reading documentation and sifting through the core's code - - switch (device) { - case RETRO_DEVICE_JOYPAD: return InputState[port][to_integral(ERetroDeviceID::JoypadB) + id]; - case RETRO_DEVICE_LIGHTGUN: return InputState[port][to_integral(ERetroDeviceID::LightgunX) + id]; - case RETRO_DEVICE_ANALOG: return InputState[port][to_integral(ERetroDeviceID::AnalogLeftX) + 2 * index + (id % RETRO_DEVICE_ID_JOYPAD_L2)]; // The indexing logic is broken and might OOBs if we're queried for something that isn't an analog trigger or stick - case RETRO_DEVICE_POINTER: return InputState[port][to_integral(ERetroDeviceID::PointerX) + id]; - case RETRO_DEVICE_MOUSE: - case RETRO_DEVICE_KEYBOARD: - default: return 0; - } -} - - -void LibretroContext::core_audio_sample(int16_t left, int16_t right) { - int16_t buf[2] = {left, right}; - core_audio_write(buf, (size_t)1); -} - -void LibretroContext::set_controller_port_device(unsigned port, unsigned device) { - devices[port] = device; // The core doesn't keep track of this so we have to - _internal_set_controller_port_device(port, device); -} - -void LibretroContext::load(const char *sofile) { - void (*set_environment)(retro_environment_t) = NULL; - void (*set_video_refresh)(retro_video_refresh_t) = NULL; - void (*set_input_poll)(retro_input_poll_t) = NULL; - void (*set_input_state)(retro_input_state_t) = NULL; - void (*set_audio_sample)(retro_audio_sample_t) = NULL; - void (*set_audio_sample_batch)(retro_audio_sample_batch_t) = NULL; - - libretro_api.handle = FPlatformProcess::GetDllHandle(ANSI_TO_TCHAR(sofile)); - - if (!libretro_api.handle) - UE_LOG(LogTemp, Fatal ,TEXT("Failed to load core: %s"), ANSI_TO_TCHAR(sofile)); - - load_retro_sym(init); - load_retro_sym(deinit); - load_retro_sym(api_version); - load_retro_sym(get_system_info); - load_retro_sym(get_system_av_info); - load_retro_sym(reset); - load_retro_sym(run); - load_retro_sym(load_game); - load_retro_sym(unload_game); - load_retro_sym(get_memory_data); - load_retro_sym(get_memory_size); - load_retro_sym(serialize_size); - load_retro_sym(serialize); - load_retro_sym(unserialize); - - load_sym(_internal_set_controller_port_device, retro_set_controller_port_device); - - load_sym(set_environment, retro_set_environment); - load_sym(set_video_refresh, retro_set_video_refresh); - load_sym(set_input_poll, retro_set_input_poll); - load_sym(set_input_state, retro_set_input_state); - load_sym(set_audio_sample, retro_set_audio_sample); - load_sym(set_audio_sample_batch, retro_set_audio_sample_batch); - +#pragma warning(push) +#pragma warning(disable:4191) + hw->get_proc_address = (retro_hw_get_proc_address_t)GL_GET_PROC_ADDRESS; +#pragma warning(pop) + core.hw = *hw; + core.using_opengl = true; + + if (core.hw.context_type == RETRO_HW_CONTEXT_OPENGLES2) { core.hw.version_major = 2; core.hw.version_minor = 0; } + if (core.hw.context_type == RETRO_HW_CONTEXT_OPENGLES3) { core.hw.version_major = 3; core.hw.version_minor = 0; } + + return true; + } + case RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO: { + auto system_av_info = *(const struct retro_system_av_info*)data; + + check( system_av_info.geometry.max_height <= core.av.geometry.max_height // @todo buffer reallocation. The libretro core can basically request to reallocate framebuffers atm this requires writing code to potentially reallocate 4 buffers + && system_av_info.geometry.max_width <= core.av.geometry.max_width); // in the case of software rendering the client buffer in opengl the pbo and texture buffer and in both cases the unreal engine rhi buffer on top of this there are + // annoying synchonization issues to deal with. We just assert here so we don't actually have to do that for now + + this->core.av.timing = system_av_info.timing; + + return true; + } + case RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY: { + const char** retro_path = (const char**)data; + auto RAII = StringCast(this->core.save_directory.GetData()); + verify(IFileManager::Get().MakeDirectory(RAII.Get(), true)); + *retro_path = core.save_directory.GetData(); + + return true; + } + case RETRO_ENVIRONMENT_GET_CORE_ASSETS_DIRECTORY: + case RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY: { + const char** retro_path = (const char**)data; + auto RAII = StringCast(this->core.system_directory.GetData()); + verify(IFileManager::Get().MakeDirectory(RAII.Get(), true)); + *retro_path = core.system_directory.GetData(); + + return true; + } + case RETRO_ENVIRONMENT_GET_LANGUAGE: { + unsigned* language = (unsigned*)data; + *language = RETRO_LANGUAGE_ENGLISH; + + return true; + } + case RETRO_ENVIRONMENT_GET_AUDIO_VIDEO_ENABLE: { + // This could potentially be useful if the object in unreal engine displaying the video and audio is either out of sight or earshot + + return false; + } + case RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS: { + auto input_descriptor = (const struct retro_input_descriptor*)data; + + do { + UE_LOG(Libretro, Verbose, TEXT("Button Found: %s"), ANSI_TO_TCHAR(input_descriptor->description)); + } while ((++input_descriptor)->description); + + return true; + } + case RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK: { + auto keyboard_callback = (const struct retro_keyboard_callback*)data; + + libretro_api.keyboard_event = keyboard_callback->callback; + + return true; + } + case RETRO_ENVIRONMENT_SET_CONTROLLER_INFO: { + auto controller_info = (const struct retro_controller_info*)data; + + FUnrealLibretroModule::EnvironmentParseControllerInfo(controller_info, ControllerDescriptions); + + return true; + } + case RETRO_ENVIRONMENT_GET_PREFERRED_HW_RENDER: { + unsigned* library = (unsigned*)data; +#if PLATFORM_ANDROID | PLATFORM_IOS + *library = RETRO_HW_CONTEXT_OPENGLES3; // Unreal Engine minimum spec requires OpenGL ES 3.1 +#else + *library = RETRO_HW_CONTEXT_OPENGL_CORE; +#endif + return true; + } + case RETRO_ENVIRONMENT_SET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE: { + const struct retro_hw_render_context_negotiation_interface* iface = + (const struct retro_hw_render_context_negotiation_interface*)data; + + core.hw_render_context_negotiation = iface; + return false; + } + case RETRO_ENVIRONMENT_GET_LED_INTERFACE: { + auto led_interface = (struct retro_led_interface *) data; + // UAE expects this to not be null even if you don't implement it + led_interface->set_led_state = [](int led, int state) { + // noop + }; + + return false; + } + case RETRO_ENVIRONMENT_GET_FASTFORWARDING: { + auto is_fast_forwarding = (bool*)data; + *is_fast_forwarding = false; + return true; + } + case RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY: { + return false; + } + default: + if (!delegate_status) { + core_log(RETRO_LOG_WARN, "Unhandled env #%u", cmd); + } + + return delegate_status; + } + + return delegate_status; +} + +// Unfinished experiment with less branchy version of this function https://godbolt.org/z/hYeYxr95r +int16_t LibretroContext::core_input_state(unsigned port, unsigned device, unsigned index, unsigned id) { + // Some of the setup to get the core to poll for certain types of input is either done through retro_set_controller_port_device + // or in the handled RETRO_ENVIRONMENT_GET_VARIABLE and RETRO_ENVIRONMENT_SET_CONTROLLER_INFO events in the retro_environment_t + // callback core_environment. Getting the core to handle specific input amounts to reading documentation and sifting through the core's code + + switch (device) { + case RETRO_DEVICE_JOYPAD: return InputState[port][to_integral(ERetroDeviceID::JoypadB) + id]; + case RETRO_DEVICE_LIGHTGUN: return InputState[port][to_integral(ERetroDeviceID::LightgunX) + id]; + case RETRO_DEVICE_ANALOG: return InputState[port][to_integral(ERetroDeviceID::AnalogLeftX) + 2 * index + (id % RETRO_DEVICE_ID_JOYPAD_L2)]; // The indexing logic is broken and might OOBs if we're queried for something that isn't an analog trigger or stick + case RETRO_DEVICE_POINTER: return InputState[port][to_integral(ERetroDeviceID::PointerX) + id]; + case RETRO_DEVICE_MOUSE: + case RETRO_DEVICE_KEYBOARD: + default: return 0; + } +} + + +void LibretroContext::core_audio_sample(int16_t left, int16_t right) { + int16_t buf[2] = {left, right}; + core_audio_write(buf, (size_t)1); +} + +void LibretroContext::set_controller_port_device(unsigned port, unsigned device) { + devices[port] = device; // The core doesn't keep track of this so we have to + _internal_set_controller_port_device(port, device); +} + +void LibretroContext::load(const char *sofile) { + void (*set_environment)(retro_environment_t) = NULL; + void (*set_video_refresh)(retro_video_refresh_t) = NULL; + void (*set_input_poll)(retro_input_poll_t) = NULL; + void (*set_input_state)(retro_input_state_t) = NULL; + void (*set_audio_sample)(retro_audio_sample_t) = NULL; + void (*set_audio_sample_batch)(retro_audio_sample_batch_t) = NULL; + + libretro_api.handle = FPlatformProcess::GetDllHandle(ANSI_TO_TCHAR(sofile)); + + if (!libretro_api.handle) + UE_LOG(LogTemp, Fatal ,TEXT("Failed to load core: %s"), ANSI_TO_TCHAR(sofile)); + + load_retro_sym(init); + load_retro_sym(deinit); + load_retro_sym(api_version); + load_retro_sym(get_system_info); + load_retro_sym(get_system_av_info); + load_retro_sym(reset); + load_retro_sym(run); + load_retro_sym(load_game); + load_retro_sym(unload_game); + load_retro_sym(get_memory_data); + load_retro_sym(get_memory_size); + load_retro_sym(serialize_size); + load_retro_sym(serialize); + load_retro_sym(unserialize); + + load_sym(_internal_set_controller_port_device, retro_set_controller_port_device); + + load_sym(set_environment, retro_set_environment); + load_sym(set_video_refresh, retro_set_video_refresh); + load_sym(set_input_poll, retro_set_input_poll); + load_sym(set_input_state, retro_set_input_state); + load_sym(set_audio_sample, retro_set_audio_sample); + load_sym(set_audio_sample_batch, retro_set_audio_sample_batch); + libretro_callbacks->video_refresh = [=](const void* data, unsigned width, unsigned height, size_t pitch) { return core_video_refresh(data, width, height, pitch); }; libretro_callbacks->audio_write = [=](const int16_t *data, size_t frames) { return core_audio_write(data, frames); }; libretro_callbacks->audio_sample = [=](int16_t left, int16_t right) { return core_audio_sample(left, right); }; @@ -902,285 +902,285 @@ void LibretroContext::load(const char *sofile) { set_input_state(libretro_callbacks->c_input_state); set_audio_sample(libretro_callbacks->c_audio_sample); set_audio_sample_batch(libretro_callbacks->c_audio_write); - - libretro_api.init(); - libretro_api.initialized = true; -} - - -void LibretroContext::load_game(const char* filename) { - struct retro_game_info info = { filename , nullptr, (size_t)0, "" }; - TArray gameBinary; - - if (!system.need_fullpath) { - verify(FFileHelper::LoadFileToArray(gameBinary, UTF8_TO_TCHAR(filename))); - - info.data = gameBinary.GetData(); - info.size = gameBinary.Num(); - } - - if (!libretro_api.load_game(&info)) - UE_LOG(Libretro, Fatal, TEXT("The core failed to load the content.")); - - libretro_api.get_system_av_info(&core.av); - - if (core.using_opengl) { -// SDL State isn't threadlocal like OpenGL so we have to synchronize here when we create a window -#if PLATFORM_APPLE - // Apple OS's impose an additional requirement that 'all' rendering operations are done on the main thread - dispatch_sync(dispatch_get_main_queue(), - ^{ - create_window(); - }); -#else - static FCriticalSection WindowLock; // Threadsafe initializatation as of c++11 - FScopeLock scoped_lock(&WindowLock); - create_window(); -#endif - } - - video_configure(&core.av.geometry); -} - -LibretroContext* LibretroContext::Launch(ULibretroCoreInstance* LibretroCoreInstance, FString core, FString game, UTextureRenderTarget2D* RenderTarget, URawAudioSoundWave* SoundBuffer, TUniqueFunction LoadedCallback) -{ - - check(IsInGameThread()); // So static initialization is safe + UObject access - - static constexpr uint32 max_instances = sizeof(libretro_callbacks_table) / sizeof(libretro_callbacks_table[0]); + + libretro_api.init(); + libretro_api.initialized = true; +} + + +void LibretroContext::load_game(const char* filename) { + struct retro_game_info info = { filename , nullptr, (size_t)0, "" }; + TArray gameBinary; + + if (!system.need_fullpath) { + verify(FFileHelper::LoadFileToArray(gameBinary, UTF8_TO_TCHAR(filename))); + + info.data = gameBinary.GetData(); + info.size = gameBinary.Num(); + } + + if (!libretro_api.load_game(&info)) + UE_LOG(Libretro, Fatal, TEXT("The core failed to load the content.")); + + libretro_api.get_system_av_info(&core.av); + + if (core.using_opengl) { +// SDL State isn't threadlocal like OpenGL so we have to synchronize here when we create a window +#if PLATFORM_APPLE + // Apple OS's impose an additional requirement that 'all' rendering operations are done on the main thread + dispatch_sync(dispatch_get_main_queue(), + ^{ + create_window(); + }); +#else + static FCriticalSection WindowLock; // Threadsafe initializatation as of c++11 + FScopeLock scoped_lock(&WindowLock); + create_window(); +#endif + } + + video_configure(&core.av.geometry); +} + +LibretroContext* LibretroContext::Launch(ULibretroCoreInstance* LibretroCoreInstance, FString core, FString game, UTextureRenderTarget2D* RenderTarget, URawAudioSoundWave* SoundBuffer, TUniqueFunction LoadedCallback) +{ + + check(IsInGameThread()); // So static initialization is safe + UObject access + + static constexpr uint32 max_instances = sizeof(libretro_callbacks_table) / sizeof(libretro_callbacks_table[0]); static TBitArray> AllocatedInstances(false, max_instances); - LibretroContext *l = new LibretroContext(); - - for (auto& Option : LibretroCoreInstance->CoreOptions) - { - l->core.settings[TCHAR_TO_UTF8(*Option.Key)] = TCHAR_TO_UTF8(*Option.Value); - } - + LibretroContext *l = new LibretroContext(); + + for (auto& Option : LibretroCoreInstance->CoreOptions) + { + l->core.settings[TCHAR_TO_UTF8(*Option.Key)] = TCHAR_TO_UTF8(*Option.Value); + } + // Grab a statically generated callback structure int32 InstanceNumber; { InstanceNumber = AllocatedInstances.FindAndSetFirstZeroBit(); check(InstanceNumber != INDEX_NONE); l->libretro_callbacks = libretro_callbacks_table + InstanceNumber; - } - - auto ConvertPath = [](auto &core_directory, const FString& CoreDirectory) - { - FString AbsoluteCoreDirectory = IFileManager::Get().ConvertToAbsolutePathForExternalAppForWrite(*FUnrealLibretroModule::IfRelativeResolvePathRelativeToThisPluginWithPathExtensions(CoreDirectory)); - core_directory.SetNumZeroed(TStringConvert::ConvertedLength(*AbsoluteCoreDirectory, AbsoluteCoreDirectory.Len()) + 1); - TStringConvert::Convert(core_directory.GetData(), // has internal assertion if fails - core_directory.Num(), - *AbsoluteCoreDirectory, - AbsoluteCoreDirectory.Len()); - }; - - auto LibretroSettings = GetDefault(); - - ConvertPath(l->core.save_directory, LibretroSettings->CoreSaveDirectory); - ConvertPath(l->core.system_directory, LibretroSettings->CoreSystemDirectory); - - l->UnrealRenderTarget = MakeWeakObjectPtr(RenderTarget); - l->UnrealSoundBuffer = MakeWeakObjectPtr(SoundBuffer ); - - // Kick the initialization process off to another thread. It shouldn't be added to the Unreal task pool because those are too slow and my code relies on OpenGL state being thread local. - // The Runnable system is the standard way for spawning and managing threads in Unreal. FThread looks enticing, but they removed any way to detach threads since "it doesn't work as expected" - l->LambdaRunnable = FLambdaRunnable::RunLambdaOnBackGroundThread(FPaths::GetCleanFilename(core) + FPaths::GetCleanFilename(game), - [=, LoadedCallback = MoveTemp(LoadedCallback), ControllersSetOnLaunch = LibretroCoreInstance->ControllersSetOnLaunch]() { - - // Here I load a copy of the dll instead of the original. If you load the same dll multiple times you won't obtain a new instance of the dll loaded into memory, - // instead all variables and function pointers will point to the original loaded dll - // WARNING: Don't ever even try to load the original dll since the editor needs to load it to query core settings (This can happen when you pause in PIE!) - const FString InstancedCorePath = IFileManager::Get().ConvertToAbsolutePathForExternalAppForWrite( -#if PLATFORM_ANDROID - // On Android .so's have to be copied to your private application directory before they are loaded - // @enhancement Use dlmopen and package files in private application directory then you wouldn't have to copy the dll every time https://man.archlinux.org/man/dlmopen.3.en#:~:text=LM_ID_NEWLM,is%20initially%0A%20%20%20%20%20%20empty - // Although this solution would be limited to 16 'namespaces' which is probably enough for practical purposes but not great - *FString::Printf(TEXT("/data/data/%s/files/%s.%s"), *FAndroidPlatformProcess::GetGameBundleId(), *FGuid::NewGuid().ToString(), *FPaths::GetExtension(*core)) -#else - *FString::Printf(TEXT("%s.%s"), *FGuid::NewGuid().ToString(), *FPaths::GetExtension(*core)) -#endif - ); - - verify(IPlatformFile::GetPlatformPhysical().CopyFile(*InstancedCorePath, *core)); - - l->core.hw.version_major = 4; - l->core.hw.version_minor = 5; - l->core.hw.context_type = RETRO_HW_CONTEXT_OPENGL_CORE; - l->core.hw.context_reset = []() {}; - l->core.hw.context_destroy = []() {}; - - // Loads the dll and its function pointers into libretro_api - l->load(TCHAR_TO_UTF8(*InstancedCorePath)); - - l->libretro_api.get_system_info(&l->system); - for (int Port = 0; Port < PortCount; Port++) - { - auto* ControllerDescription = ControllersSetOnLaunch.Find(l->system.library_name); - if (ControllerDescription) - { - l->set_controller_port_device(Port, (*ControllerDescription)[Port].ID); - } - else - { - // This is needed for some cores nestopia specifically is one example - // otherwise nothing will be bound for some reason - l->set_controller_port_device(Port, RETRO_DEVICE_DEFAULT); - } - } - - // This does load the game but does many other things as well. If hardware rendering is needed it loads OpenGL resources from the OS and this also initializes the unreal engine resources for audio and video. - l->load_game(TCHAR_TO_UTF8(*game)); - - l->CoreState.store(ECoreState::Running, std::memory_order_release); - LoadedCallback(l, l->libretro_api); - - // This simplifies the logic in core_video_refresh, It stops us from erroring it doesn't error when we try to unmap this pixel buffer in core_video_refresh - // You could just move this to the beginning of core_video_refresh and surround it with an if statement that does this the first time through - if (l->core.using_opengl) { - l->glBindBuffer(GL_PIXEL_PACK_BUFFER, l->core.gl.pixel_buffer_objects[l->core.free_framebuffer_index]); - l->glMapBufferRange(GL_PIXEL_PACK_BUFFER, - 0, // Offset - 1, // Size... - GL_MAP_READ_BIT); - l->core.gl.fence = l->glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - l->glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); - } - - uint64 frames = 0; - auto start = FDateTime::Now(); - while (l->CoreState.load(std::memory_order_relaxed) != ECoreState::Shutdown) - { - DECLARE_SCOPE_CYCLE_COUNTER(TEXT("Frame"), STAT_LibretroFrame, STATGROUP_UnrealLibretro); - { - DECLARE_SCOPE_CYCLE_COUNTER(TEXT("Work"), STAT_LibretroWork, STATGROUP_UnrealLibretro); - - if (l->CoreState.load(std::memory_order_relaxed) == ECoreState::Running) - { - l->libretro_api.run(); - } - - // Execute tasks from command queue Note: It's semantically significant that this is here. Since I hook in save state - // operations here it must necessarily come after run is called on the core - TUniqueFunction Task; - while (l->LibretroAPITasks.Dequeue(Task)) - { - Task(l->libretro_api); - } - } - - { // @todo My timing solution is a bit adhoc. I'm sure theres probably a better way. - DECLARE_SCOPE_CYCLE_COUNTER(TEXT("Sleep"), STAT_LibretroSleep, STATGROUP_UnrealLibretro); - - frames++; - - double sleep = (frames / l->core.av.timing.fps) - (FDateTime::Now() - start).GetTotalSeconds(); - if (sleep > 0.0) { - FPlatformProcess::Sleep(sleep); // This always yields so only call it when we actually need to sleep - } else if (sleep < -(1 / l->core.av.timing.fps)) { // If over a frame behind don't try to catch up to the next frame - start = FDateTime::Now(); - frames = 0; - } - } - } - - // Explicit Cleanup - if (l->libretro_api.initialized) - { - l->libretro_api.deinit(); - } - - if (l->libretro_api.handle) - { - FPlatformProcess::FreeDllHandle(l->libretro_api.handle); - } - - IPlatformFile::GetPlatformPhysical().DeleteFile(*InstancedCorePath); - - l->Unreal.AudioQueue.Reset(); - - FFunctionGraphTask::CreateAndDispatchWhenReady([=] + } + + auto ConvertPath = [](auto &core_directory, const FString& CoreDirectory) + { + FString AbsoluteCoreDirectory = IFileManager::Get().ConvertToAbsolutePathForExternalAppForWrite(*FUnrealLibretroModule::IfRelativeResolvePathRelativeToThisPluginWithPathExtensions(CoreDirectory)); + core_directory.SetNumZeroed(TStringConvert::ConvertedLength(*AbsoluteCoreDirectory, AbsoluteCoreDirectory.Len()) + 1); + TStringConvert::Convert(core_directory.GetData(), // has internal assertion if fails + core_directory.Num(), + *AbsoluteCoreDirectory, + AbsoluteCoreDirectory.Len()); + }; + + auto LibretroSettings = GetDefault(); + + ConvertPath(l->core.save_directory, LibretroSettings->CoreSaveDirectory); + ConvertPath(l->core.system_directory, LibretroSettings->CoreSystemDirectory); + + l->UnrealRenderTarget = MakeWeakObjectPtr(RenderTarget); + l->UnrealSoundBuffer = MakeWeakObjectPtr(SoundBuffer ); + + // Kick the initialization process off to another thread. It shouldn't be added to the Unreal task pool because those are too slow and my code relies on OpenGL state being thread local. + // The Runnable system is the standard way for spawning and managing threads in Unreal. FThread looks enticing, but they removed any way to detach threads since "it doesn't work as expected" + l->LambdaRunnable = FLambdaRunnable::RunLambdaOnBackGroundThread(FPaths::GetCleanFilename(core) + FPaths::GetCleanFilename(game), + [=, LoadedCallback = MoveTemp(LoadedCallback), ControllersSetOnLaunch = LibretroCoreInstance->ControllersSetOnLaunch]() { + + // Here I load a copy of the dll instead of the original. If you load the same dll multiple times you won't obtain a new instance of the dll loaded into memory, + // instead all variables and function pointers will point to the original loaded dll + // WARNING: Don't ever even try to load the original dll since the editor needs to load it to query core settings (This can happen when you pause in PIE!) + const FString InstancedCorePath = IFileManager::Get().ConvertToAbsolutePathForExternalAppForWrite( +#if PLATFORM_ANDROID + // On Android .so's have to be copied to your private application directory before they are loaded + // @enhancement Use dlmopen and package files in private application directory then you wouldn't have to copy the dll every time https://man.archlinux.org/man/dlmopen.3.en#:~:text=LM_ID_NEWLM,is%20initially%0A%20%20%20%20%20%20empty + // Although this solution would be limited to 16 'namespaces' which is probably enough for practical purposes but not great + *FString::Printf(TEXT("/data/data/%s/files/%s.%s"), *FAndroidPlatformProcess::GetGameBundleId(), *FGuid::NewGuid().ToString(), *FPaths::GetExtension(*core)) +#else + *FString::Printf(TEXT("%s.%s"), *FGuid::NewGuid().ToString(), *FPaths::GetExtension(*core)) +#endif + ); + + verify(IPlatformFile::GetPlatformPhysical().CopyFile(*InstancedCorePath, *core)); + + l->core.hw.version_major = 4; + l->core.hw.version_minor = 5; + l->core.hw.context_type = RETRO_HW_CONTEXT_OPENGL_CORE; + l->core.hw.context_reset = []() {}; + l->core.hw.context_destroy = []() {}; + + // Loads the dll and its function pointers into libretro_api + l->load(TCHAR_TO_UTF8(*InstancedCorePath)); + + l->libretro_api.get_system_info(&l->system); + for (int Port = 0; Port < PortCount; Port++) + { + auto* ControllerDescription = ControllersSetOnLaunch.Find(l->system.library_name); + if (ControllerDescription) + { + l->set_controller_port_device(Port, (*ControllerDescription)[Port].ID); + } + else + { + // This is needed for some cores nestopia specifically is one example + // otherwise nothing will be bound for some reason + l->set_controller_port_device(Port, RETRO_DEVICE_DEFAULT); + } + } + + // This does load the game but does many other things as well. If hardware rendering is needed it loads OpenGL resources from the OS and this also initializes the unreal engine resources for audio and video. + l->load_game(TCHAR_TO_UTF8(*game)); + + l->CoreState.store(ECoreState::Running, std::memory_order_release); + LoadedCallback(l, l->libretro_api); + + // This simplifies the logic in core_video_refresh, It stops us from erroring it doesn't error when we try to unmap this pixel buffer in core_video_refresh + // You could just move this to the beginning of core_video_refresh and surround it with an if statement that does this the first time through + if (l->core.using_opengl) { + l->glBindBuffer(GL_PIXEL_PACK_BUFFER, l->core.gl.pixel_buffer_objects[l->core.free_framebuffer_index]); + l->glMapBufferRange(GL_PIXEL_PACK_BUFFER, + 0, // Offset + 1, // Size... + GL_MAP_READ_BIT); + l->core.gl.fence = l->glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + l->glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + } + + uint64 frames = 0; + auto start = FDateTime::Now(); + while (l->CoreState.load(std::memory_order_relaxed) != ECoreState::Shutdown) + { + DECLARE_SCOPE_CYCLE_COUNTER(TEXT("Frame"), STAT_LibretroFrame, STATGROUP_UnrealLibretro); + { + DECLARE_SCOPE_CYCLE_COUNTER(TEXT("Work"), STAT_LibretroWork, STATGROUP_UnrealLibretro); + + if (l->CoreState.load(std::memory_order_relaxed) == ECoreState::Running) + { + l->libretro_api.run(); + } + + // Execute tasks from command queue Note: It's semantically significant that this is here. Since I hook in save state + // operations here it must necessarily come after run is called on the core + TUniqueFunction Task; + while (l->LibretroAPITasks.Dequeue(Task)) + { + Task(l->libretro_api); + } + } + + { // @todo My timing solution is a bit adhoc. I'm sure theres probably a better way. + DECLARE_SCOPE_CYCLE_COUNTER(TEXT("Sleep"), STAT_LibretroSleep, STATGROUP_UnrealLibretro); + + frames++; + + double sleep = (frames / l->core.av.timing.fps) - (FDateTime::Now() - start).GetTotalSeconds(); + if (sleep > 0.0) { + FPlatformProcess::Sleep(sleep); // This always yields so only call it when we actually need to sleep + } else if (sleep < -(1 / l->core.av.timing.fps)) { // If over a frame behind don't try to catch up to the next frame + start = FDateTime::Now(); + frames = 0; + } + } + } + + // Explicit Cleanup + if (l->libretro_api.initialized) + { + l->libretro_api.deinit(); + } + + if (l->libretro_api.handle) + { + FPlatformProcess::FreeDllHandle(l->libretro_api.handle); + } + + IPlatformFile::GetPlatformPhysical().DeleteFile(*InstancedCorePath); + + l->Unreal.AudioQueue.Reset(); + + FFunctionGraphTask::CreateAndDispatchWhenReady([=] { AllocatedInstances[InstanceNumber] = false; }, TStatId(), nullptr, ENamedThreads::GameThread); - { -#if PLATFORM_WINDOWS - // On windows the thread that created a window MUST also destroy it https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-destroywindow#:~:text=A%20thread%20cannot%20use%20DestroyWindow%20to%20destroy%20a%20window%20created%20by%20a%20different%20thread - if (l->core.gl.window) - { - wglMakeCurrent(l->core.gl.hdc, NULL); - verify(ReleaseDC(l->core.gl.window, l->core.gl.hdc)); - verify(DestroyWindow(l->core.gl.window)); - } -#endif - // The double nested command enqueue is based on boilerplate I found elsewhere in the engine - // Since render commands are executed fifo we only delete shared resources after the render thread is done with them - // The actual render command execution is done on the RHI thread so we have to synchronize there as well - ENQUEUE_RENDER_COMMAND(LibretroCleanupResourcesSharedWithRenderThread)([l](FRHICommandListImmediate& RHICmdList) - { - RHICmdList.EnqueueLambda([l](FRHICommandList&) - { - for (int i : {0, 1}) - { - if (l->core.software.bgra_buffers[i]) - { - FMemory::Free(l->core.software.bgra_buffers[i]); - } - } -#if PLATFORM_WINDOWS - if (l->core.gl.context) - { - wglDeleteContext(l->core.gl.context); /** implicitly releases resources like fbos, pbos, and textures */ - } -#elif PLATFORM_ANDROID - if (l->core.gl.egl_context) - { - verify(eglDestroyContext(l->core.gl.egl_display, - l->core.gl.egl_context)); - } -#endif - - l->Unreal.TextureRHI.SafeRelease(); - - if (l->LambdaRunnable) - { - delete l->LambdaRunnable; /** This will block the render thread if for some reason the thread we were running on hasn't exited yet */ - } - - delete l; /** Task queue released */ - }); - } - ); - } - } - ); - - return l; -} - -void LibretroContext::Shutdown(LibretroContext* Instance) -{ - // We enqueue the shutdown procedure as the final task since we want outstanding tasks to be executed first - Instance->EnqueueTask([Instance](auto&&) - { - Instance->CoreState.store(ECoreState::Shutdown, std::memory_order_relaxed); - }); -} - -void LibretroContext::Pause(bool ShouldPause) -{ - // We enqueue the state change because otherwise we might prematurely unset the Starting state - // Alternatively you could add one more state to accomplish this, but this is fine for now - EnqueueTask([=](auto&&) - { - CoreState.store(ShouldPause ? ECoreState::Paused : ECoreState::Running, std::memory_order_relaxed); - }); - -} - -void LibretroContext::EnqueueTask(TUniqueFunction LibretroAPITask) -{ - check(IsInGameThread()); // LibretroAPITasks is a single producer single consumer queue - LibretroAPITasks.Enqueue(MoveTemp(LibretroAPITask)); -}; + { +#if PLATFORM_WINDOWS + // On windows the thread that created a window MUST also destroy it https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-destroywindow#:~:text=A%20thread%20cannot%20use%20DestroyWindow%20to%20destroy%20a%20window%20created%20by%20a%20different%20thread + if (l->core.gl.window) + { + wglMakeCurrent(l->core.gl.hdc, NULL); + verify(ReleaseDC(l->core.gl.window, l->core.gl.hdc)); + verify(DestroyWindow(l->core.gl.window)); + } +#endif + // The double nested command enqueue is based on boilerplate I found elsewhere in the engine + // Since render commands are executed fifo we only delete shared resources after the render thread is done with them + // The actual render command execution is done on the RHI thread so we have to synchronize there as well + ENQUEUE_RENDER_COMMAND(LibretroCleanupResourcesSharedWithRenderThread)([l](FRHICommandListImmediate& RHICmdList) + { + RHICmdList.EnqueueLambda([l](FRHICommandList&) + { + for (int i : {0, 1}) + { + if (l->core.software.bgra_buffers[i]) + { + FMemory::Free(l->core.software.bgra_buffers[i]); + } + } +#if PLATFORM_WINDOWS + if (l->core.gl.context) + { + wglDeleteContext(l->core.gl.context); /** implicitly releases resources like fbos, pbos, and textures */ + } +#elif PLATFORM_ANDROID + if (l->core.gl.egl_context) + { + verify(eglDestroyContext(l->core.gl.egl_display, + l->core.gl.egl_context)); + } +#endif + + l->Unreal.TextureRHI.SafeRelease(); + + if (l->LambdaRunnable) + { + delete l->LambdaRunnable; /** This will block the render thread if for some reason the thread we were running on hasn't exited yet */ + } + + delete l; /** Task queue released */ + }); + } + ); + } + } + ); + + return l; +} + +void LibretroContext::Shutdown(LibretroContext* Instance) +{ + // We enqueue the shutdown procedure as the final task since we want outstanding tasks to be executed first + Instance->EnqueueTask([Instance](auto&&) + { + Instance->CoreState.store(ECoreState::Shutdown, std::memory_order_relaxed); + }); +} + +void LibretroContext::Pause(bool ShouldPause) +{ + // We enqueue the state change because otherwise we might prematurely unset the Starting state + // Alternatively you could add one more state to accomplish this, but this is fine for now + EnqueueTask([=](auto&&) + { + CoreState.store(ShouldPause ? ECoreState::Paused : ECoreState::Running, std::memory_order_relaxed); + }); + +} + +void LibretroContext::EnqueueTask(TUniqueFunction LibretroAPITask) +{ + check(IsInGameThread()); // LibretroAPITasks is a single producer single consumer queue + LibretroAPITasks.Enqueue(MoveTemp(LibretroAPITask)); +}; diff --git a/Source/UnrealLibretro/Private/sdlarch.h b/Source/UnrealLibretro/Private/sdlarch.h index d13565a8..05edc01f 100644 --- a/Source/UnrealLibretro/Private/sdlarch.h +++ b/Source/UnrealLibretro/Private/sdlarch.h @@ -1,244 +1,244 @@ -#pragma once - -// Libretro API -#include "libretro/libretro.h" -static_assert(RETRO_API_VERSION == 1, "Retro API version changed"); - - -// Standard Template Library https://docs.unrealengine.com/en-US/Programming/Development/CodingStandard/#useofstandardlibraries -#include -#include -#include - -// Unreal imports -#include "CoreMinimal.h" -#include "Engine/TextureRenderTarget2D.h" -#include "UObject/WeakObjectPtrTemplates.h" -#include "Containers/CircularQueue.h" - -#include "LibretroInputDefinitions.h" -#include "RawAudioSoundWave.h" - -#if PLATFORM_WINDOWS -#include "Windows/PreWindowsApi.h" -#endif - -#include "ThirdParty/OpenGL/GL/glcorearb.h" -#include "GL/extension_definitions.h" - -#if PLATFORM_WINDOWS -#include "Windows/PostWindowsApi.h" -#endif - -DECLARE_STATS_GROUP(TEXT("UnrealLibretro"), STATGROUP_UnrealLibretro, STATCAT_Advanced); - -#define ENUM_GL_WIN32_INTEROP_PROCEDURES(EnumMacro) \ - EnumMacro(PFNGLIMPORTMEMORYWIN32HANDLEEXTPROC, glImportMemoryWin32HandleEXT) \ - EnumMacro(PFNGLCREATEMEMORYOBJECTSEXTPROC, glCreateMemoryObjectsEXT) \ - EnumMacro(PFNGLDELETEMEMORYOBJECTSEXTPROC, glDeleteMemoryObjectsEXT) \ - EnumMacro(PFNGLTEXTURESTORAGEMEM2DEXTPROC, glTextureStorageMem2DEXT) \ - EnumMacro(PFNGLCREATETEXTURESPROC, glCreateTextures) \ - -#define ENUM_GL_PROCEDURES(EnumMacro) \ - EnumMacro(PFNGLBINDFRAMEBUFFERPROC, glBindFramebuffer) \ - EnumMacro(PFNGLBINDRENDERBUFFERPROC, glBindRenderbuffer) \ - EnumMacro(PFNGLBINDTEXTUREPROC, glBindTexture) \ - EnumMacro(PFNGLCHECKFRAMEBUFFERSTATUSPROC, glCheckFramebufferStatus) \ - EnumMacro(PFNGLCLEARPROC, glClear) \ - EnumMacro(PFNGLCLEARCOLORPROC, glClearColor) \ - EnumMacro(PFNGLDEBUGMESSAGECALLBACKPROC, glDebugMessageCallback) \ - EnumMacro(PFNGLDEBUGMESSAGECONTROLPROC, glDebugMessageControl) \ - EnumMacro(PFNGLDELETEBUFFERSPROC, glDeleteBuffers) \ - EnumMacro(PFNGLDELETETEXTURESPROC, glDeleteTextures) \ - EnumMacro(PFNGLENABLEPROC, glEnable) \ - EnumMacro(PFNGLFRAMEBUFFERRENDERBUFFERPROC, glFramebufferRenderbuffer) \ - EnumMacro(PFNGLFRAMEBUFFERTEXTURE2DPROC, glFramebufferTexture2D) \ - EnumMacro(PFNGLGENFRAMEBUFFERSPROC, glGenFramebuffers) \ - EnumMacro(PFNGLGENRENDERBUFFERSPROC, glGenRenderbuffers) \ - EnumMacro(PFNGLGENTEXTURESPROC, glGenTextures) \ - EnumMacro(PFNGLGETINTEGERVPROC, glGetIntegerv) \ - EnumMacro(PFNGLGETSTRINGPROC, glGetString) \ - EnumMacro(PFNGLREADPIXELSPROC, glReadPixels) \ - EnumMacro(PFNGLPIXELSTOREIPROC, glPixelStorei) \ - EnumMacro(PFNGLRENDERBUFFERSTORAGEPROC, glRenderbufferStorage) \ - EnumMacro(PFNGLTEXIMAGE2DPROC, glTexImage2D) \ - EnumMacro(PFNGLFENCESYNCPROC, glFenceSync) \ - EnumMacro(PFNGLDELETESYNCPROC, glDeleteSync) \ - EnumMacro(PFNGLCLIENTWAITSYNCPROC, glClientWaitSync) \ - EnumMacro(PFNGLGENBUFFERSPROC, glGenBuffers) \ - EnumMacro(PFNGLBINDBUFFERPROC, glBindBuffer) \ - EnumMacro(PFNGLMAPBUFFERRANGEPROC, glMapBufferRange) \ - EnumMacro(PFNGLUNMAPBUFFERPROC, glUnmapBuffer) \ - EnumMacro(PFNGLBUFFERDATAPROC, glBufferData) \ - EnumMacro(PFNGLREADBUFFERPROC, glReadBuffer) \ - -struct libretro_api_t { - void* handle; - bool initialized; - - void (*init)(void); - void (*deinit)(void); - unsigned (*api_version)(void); - void (*get_system_info)(struct retro_system_info* info); - void (*get_system_av_info)(struct retro_system_av_info* info); - void (*reset)(void); - void (*run)(void); - size_t (*serialize_size)(void); - bool (*serialize)(void *data, size_t size); - bool (*unserialize)(const void *data, size_t size); - // void cheat_reset(void); - // void cheat_set(unsigned index, bool enabled, const char *code); - bool (*load_game)(const struct retro_game_info* game); - // bool load_game_special(unsigned game_type, const struct retro_game_info *info, size_t num_info); - void (*unload_game)(void); - //unsigned get_region(void); - void* (*get_memory_data)(unsigned id); - size_t (*get_memory_size)(unsigned id); - - retro_keyboard_event_t keyboard_event; -}; - -struct LibretroContext { -public: - /** - * @brief analogous to new except asynchronous - * @post The LoadedCallback is always called - */ - static LibretroContext* Launch(class ULibretroCoreInstance* LibretroCoreInstance, FString core, FString game, UTextureRenderTarget2D* RenderTarget, URawAudioSoundWave* SoundEmitter, TUniqueFunction LoadedCallback); - - /** - * @brief analogous to delete except asynchronous - */ - static void Shutdown(LibretroContext* Instance); - - /** - * Queued tasks will still execute even if paused - */ - void Pause(bool ShouldPause); - - /** - * @post Everything queued before calling shutdown will be executed - */ - void EnqueueTask(TUniqueFunction LibretroAPITask); - +#pragma once + +// Libretro API +#include "libretro/libretro.h" +static_assert(RETRO_API_VERSION == 1, "Retro API version changed"); + + +// Standard Template Library https://docs.unrealengine.com/en-US/Programming/Development/CodingStandard/#useofstandardlibraries +#include +#include +#include + +// Unreal imports +#include "CoreMinimal.h" +#include "Engine/TextureRenderTarget2D.h" +#include "UObject/WeakObjectPtrTemplates.h" +#include "Containers/CircularQueue.h" + +#include "LibretroInputDefinitions.h" +#include "RawAudioSoundWave.h" + +#if PLATFORM_WINDOWS +#include "Windows/PreWindowsApi.h" +#endif + +#include "ThirdParty/OpenGL/GL/glcorearb.h" +#include "GL/extension_definitions.h" + +#if PLATFORM_WINDOWS +#include "Windows/PostWindowsApi.h" +#endif + +DECLARE_STATS_GROUP(TEXT("UnrealLibretro"), STATGROUP_UnrealLibretro, STATCAT_Advanced); + +#define ENUM_GL_WIN32_INTEROP_PROCEDURES(EnumMacro) \ + EnumMacro(PFNGLIMPORTMEMORYWIN32HANDLEEXTPROC, glImportMemoryWin32HandleEXT) \ + EnumMacro(PFNGLCREATEMEMORYOBJECTSEXTPROC, glCreateMemoryObjectsEXT) \ + EnumMacro(PFNGLDELETEMEMORYOBJECTSEXTPROC, glDeleteMemoryObjectsEXT) \ + EnumMacro(PFNGLTEXTURESTORAGEMEM2DEXTPROC, glTextureStorageMem2DEXT) \ + EnumMacro(PFNGLCREATETEXTURESPROC, glCreateTextures) \ + +#define ENUM_GL_PROCEDURES(EnumMacro) \ + EnumMacro(PFNGLBINDFRAMEBUFFERPROC, glBindFramebuffer) \ + EnumMacro(PFNGLBINDRENDERBUFFERPROC, glBindRenderbuffer) \ + EnumMacro(PFNGLBINDTEXTUREPROC, glBindTexture) \ + EnumMacro(PFNGLCHECKFRAMEBUFFERSTATUSPROC, glCheckFramebufferStatus) \ + EnumMacro(PFNGLCLEARPROC, glClear) \ + EnumMacro(PFNGLCLEARCOLORPROC, glClearColor) \ + EnumMacro(PFNGLDEBUGMESSAGECALLBACKPROC, glDebugMessageCallback) \ + EnumMacro(PFNGLDEBUGMESSAGECONTROLPROC, glDebugMessageControl) \ + EnumMacro(PFNGLDELETEBUFFERSPROC, glDeleteBuffers) \ + EnumMacro(PFNGLDELETETEXTURESPROC, glDeleteTextures) \ + EnumMacro(PFNGLENABLEPROC, glEnable) \ + EnumMacro(PFNGLFRAMEBUFFERRENDERBUFFERPROC, glFramebufferRenderbuffer) \ + EnumMacro(PFNGLFRAMEBUFFERTEXTURE2DPROC, glFramebufferTexture2D) \ + EnumMacro(PFNGLGENFRAMEBUFFERSPROC, glGenFramebuffers) \ + EnumMacro(PFNGLGENRENDERBUFFERSPROC, glGenRenderbuffers) \ + EnumMacro(PFNGLGENTEXTURESPROC, glGenTextures) \ + EnumMacro(PFNGLGETINTEGERVPROC, glGetIntegerv) \ + EnumMacro(PFNGLGETSTRINGPROC, glGetString) \ + EnumMacro(PFNGLREADPIXELSPROC, glReadPixels) \ + EnumMacro(PFNGLPIXELSTOREIPROC, glPixelStorei) \ + EnumMacro(PFNGLRENDERBUFFERSTORAGEPROC, glRenderbufferStorage) \ + EnumMacro(PFNGLTEXIMAGE2DPROC, glTexImage2D) \ + EnumMacro(PFNGLFENCESYNCPROC, glFenceSync) \ + EnumMacro(PFNGLDELETESYNCPROC, glDeleteSync) \ + EnumMacro(PFNGLCLIENTWAITSYNCPROC, glClientWaitSync) \ + EnumMacro(PFNGLGENBUFFERSPROC, glGenBuffers) \ + EnumMacro(PFNGLBINDBUFFERPROC, glBindBuffer) \ + EnumMacro(PFNGLMAPBUFFERRANGEPROC, glMapBufferRange) \ + EnumMacro(PFNGLUNMAPBUFFERPROC, glUnmapBuffer) \ + EnumMacro(PFNGLBUFFERDATAPROC, glBufferData) \ + EnumMacro(PFNGLREADBUFFERPROC, glReadBuffer) \ + +struct libretro_api_t { + void* handle; + bool initialized; + + void (*init)(void); + void (*deinit)(void); + unsigned (*api_version)(void); + void (*get_system_info)(struct retro_system_info* info); + void (*get_system_av_info)(struct retro_system_av_info* info); + void (*reset)(void); + void (*run)(void); + size_t (*serialize_size)(void); + bool (*serialize)(void *data, size_t size); + bool (*unserialize)(const void *data, size_t size); + // void cheat_reset(void); + // void cheat_set(unsigned index, bool enabled, const char *code); + bool (*load_game)(const struct retro_game_info* game); + // bool load_game_special(unsigned game_type, const struct retro_game_info *info, size_t num_info); + void (*unload_game)(void); + //unsigned get_region(void); + void* (*get_memory_data)(unsigned id); + size_t (*get_memory_size)(unsigned id); + + retro_keyboard_event_t keyboard_event; +}; + +struct LibretroContext { +public: + /** + * @brief analogous to new except asynchronous + * @post The LoadedCallback is always called + */ + static LibretroContext* Launch(class ULibretroCoreInstance* LibretroCoreInstance, FString core, FString game, UTextureRenderTarget2D* RenderTarget, URawAudioSoundWave* SoundEmitter, TUniqueFunction LoadedCallback); + + /** + * @brief analogous to delete except asynchronous + */ + static void Shutdown(LibretroContext* Instance); + + /** + * Queued tasks will still execute even if paused + */ + void Pause(bool ShouldPause); + + /** + * @post Everything queued before calling shutdown will be executed + */ + void EnqueueTask(TUniqueFunction LibretroAPITask); + /** * This is what the libretro core reads from when determining input. If you want to use your own input method you can modify this directly. */ - FLibretroInputState InputState[PortCount]; - - std::atomic devices[PortCount]; - // Only safe to access from any thread once CoreState is not Starting - TStaticArray, PortCount> ControllerDescriptions; - - struct retro_system_info system = { 0 }; - - void set_controller_port_device(unsigned port, unsigned device); - - TUniqueFunction::Type> CoreEnvironmentCallback; - - enum class ECoreState : int8 - { - Starting, - Running, - Paused, - Shutdown - }; - - std::atomic CoreState{ ECoreState::Starting }; - -protected: - LibretroContext() {} - ~LibretroContext() {} - - void (*_internal_set_controller_port_device)(unsigned port, unsigned device); - libretro_api_t libretro_api = { 0 }; + FLibretroInputState InputState[PortCount]; + + std::atomic devices[PortCount]; + // Only safe to access from any thread once CoreState is not Starting + TStaticArray, PortCount> ControllerDescriptions; + + struct retro_system_info system = { 0 }; + + void set_controller_port_device(unsigned port, unsigned device); + + TUniqueFunction::Type> CoreEnvironmentCallback; + + enum class ECoreState : int8 + { + Starting, + Running, + Paused, + Shutdown + }; + + std::atomic CoreState{ ECoreState::Starting }; + +protected: + LibretroContext() {} + ~LibretroContext() {} + + void (*_internal_set_controller_port_device)(unsigned port, unsigned device); + libretro_api_t libretro_api = { 0 }; struct libretro_callbacks_t* libretro_callbacks = nullptr; - TQueue, EQueueMode::Spsc> LibretroAPITasks; // TQueue has acquire-release semantics on Enqueue and Dequeue so this should be thread-safe - - // @todo remove these and have the loaded callback handle these resources - TWeakObjectPtr UnrealRenderTarget{nullptr}; - TWeakObjectPtr UnrealSoundBuffer{nullptr}; - - struct - { - // These are all ThreadSafe shared pointers that are the main bridge between and unreal - FTexture2DRHIRef TextureRHI; - TSharedPtr, ESPMode::ThreadSafe> AudioQueue; - - struct - { - FCriticalSection CriticalSection; - void* ClientBuffer{ nullptr }; - } FrameUpload; - } Unreal = {0}; - - struct { - bool using_opengl; - - struct { -#if PLATFORM_ANDROID - void* egl_context; - void* egl_display; -#elif PLATFORM_WINDOWS - HWND window; - HGLRC context; - HDC hdc; -#endif - - GLuint texture; - GLuint framebuffer; - GLuint renderbuffer; - GLuint rhi_interop_memory; - - GLuint pixel_buffer_objects[2]; - GLsync fence; - - GLuint pitch; - GLuint pixel_type; - GLuint pixel_format; - GLuint bits_per_pixel; - } gl; - - struct { - void* bgra_buffers[2]; - } software; - - bool free_framebuffer_index; - - const struct retro_hw_render_context_negotiation_interface* hw_render_context_negotiation; // @todo - struct retro_hw_render_callback hw; - struct retro_system_av_info av; - - TArray> save_directory, - system_directory; - std::unordered_map settings; - } core = { 0 }; - -public: - bool &LibretroThread_bottom_left_origin = core.hw.bottom_left_origin; - struct retro_game_geometry &LibretroThread_geometry = core.av.geometry; - - class FLambdaRunnable* LambdaRunnable{nullptr}; -protected: + TQueue, EQueueMode::Spsc> LibretroAPITasks; // TQueue has acquire-release semantics on Enqueue and Dequeue so this should be thread-safe + + // @todo remove these and have the loaded callback handle these resources + TWeakObjectPtr UnrealRenderTarget{nullptr}; + TWeakObjectPtr UnrealSoundBuffer{nullptr}; + + struct + { + // These are all ThreadSafe shared pointers that are the main bridge between and unreal + FTexture2DRHIRef TextureRHI; + TSharedPtr, ESPMode::ThreadSafe> AudioQueue; + + struct + { + FCriticalSection CriticalSection; + void* ClientBuffer{ nullptr }; + } FrameUpload; + } Unreal = {0}; + + struct { + bool using_opengl; + + struct { +#if PLATFORM_ANDROID + void* egl_context; + void* egl_display; +#elif PLATFORM_WINDOWS + HWND window; + HGLRC context; + HDC hdc; +#endif + + GLuint texture; + GLuint framebuffer; + GLuint renderbuffer; + GLuint rhi_interop_memory; + + GLuint pixel_buffer_objects[2]; + GLsync fence; + + GLuint pitch; + GLuint pixel_type; + GLuint pixel_format; + GLuint bits_per_pixel; + } gl; + + struct { + void* bgra_buffers[2]; + } software; + + bool free_framebuffer_index; + + const struct retro_hw_render_context_negotiation_interface* hw_render_context_negotiation; // @todo + struct retro_hw_render_callback hw; + struct retro_system_av_info av; + + TArray> save_directory, + system_directory; + std::unordered_map settings; + } core = { 0 }; + +public: + bool &LibretroThread_bottom_left_origin = core.hw.bottom_left_origin; + struct retro_game_geometry &LibretroThread_geometry = core.av.geometry; + + class FLambdaRunnable* LambdaRunnable{nullptr}; +protected: // This is where the callback implementation logic is for the callbacks from the Libretro Core - void core_video_refresh(const void* data, unsigned width, unsigned height, unsigned pitch); - void core_audio_sample(int16_t left, int16_t right); - size_t core_audio_write(const int16_t* buf, size_t frames); - int16_t core_input_state(unsigned port, unsigned device, unsigned index, unsigned id); - // void core_input_poll(void); - bool core_environment(unsigned cmd, void* data); - - // I read somewhere online that you technically have to load OpenGL procedures per context you make on Windows - // I don't think it actually matters unless you're using multiple rendering devices, and even in that case it might - // not matter. I still load them per instance anyway to be on the safe side. - #define DEFINE_GL_PROCEDURES(Type,Func) Type Func = NULL; - ENUM_GL_PROCEDURES(DEFINE_GL_PROCEDURES); - ENUM_GL_WIN32_INTEROP_PROCEDURES(DEFINE_GL_PROCEDURES) - bool gl_win32_interop_supported_by_driver; - - void create_window(); - void video_configure(const struct retro_game_geometry* geom); - - void load(const char* sofile); - void load_game(const char* filename); + void core_video_refresh(const void* data, unsigned width, unsigned height, unsigned pitch); + void core_audio_sample(int16_t left, int16_t right); + size_t core_audio_write(const int16_t* buf, size_t frames); + int16_t core_input_state(unsigned port, unsigned device, unsigned index, unsigned id); + // void core_input_poll(void); + bool core_environment(unsigned cmd, void* data); + + // I read somewhere online that you technically have to load OpenGL procedures per context you make on Windows + // I don't think it actually matters unless you're using multiple rendering devices, and even in that case it might + // not matter. I still load them per instance anyway to be on the safe side. + #define DEFINE_GL_PROCEDURES(Type,Func) Type Func = NULL; + ENUM_GL_PROCEDURES(DEFINE_GL_PROCEDURES); + ENUM_GL_WIN32_INTEROP_PROCEDURES(DEFINE_GL_PROCEDURES) + bool gl_win32_interop_supported_by_driver; + + void create_window(); + void video_configure(const struct retro_game_geometry* geom); + + void load(const char* sofile); + void load_game(const char* filename); }; \ No newline at end of file diff --git a/Source/UnrealLibretro/Public/LibretroCoreInstance.h b/Source/UnrealLibretro/Public/LibretroCoreInstance.h index 2b6527cf..eedc8ad9 100644 --- a/Source/UnrealLibretro/Public/LibretroCoreInstance.h +++ b/Source/UnrealLibretro/Public/LibretroCoreInstance.h @@ -7,31 +7,31 @@ #include "Engine/TextureRenderTarget2D.h" #include "LibretroCoreInstance.generated.h" - -USTRUCT(BlueprintType) -struct FLibretroControllerDescription // retro_controller_description -{ - GENERATED_BODY() - - UPROPERTY(VisibleAnywhere) - FString Description{"Unspecified"}; - - UPROPERTY(VisibleAnywhere) - unsigned int ID{RETRO_DEVICE_DEFAULT}; -}; - -USTRUCT(BlueprintType) -struct FLibretroControllerDescriptions -{ - GENERATED_BODY() - - UPROPERTY(VisibleAnywhere) - FLibretroControllerDescription ControllerDescription[PortCount]; - - FLibretroControllerDescription& operator[](int Port) { return ControllerDescription[Port]; } - const FLibretroControllerDescription& operator[](int Port) const { return ControllerDescription[Port]; } -}; - + +USTRUCT(BlueprintType) +struct FLibretroControllerDescription // retro_controller_description +{ + GENERATED_BODY() + + UPROPERTY(VisibleAnywhere) + FString Description{"Unspecified"}; + + UPROPERTY(VisibleAnywhere) + unsigned int ID{RETRO_DEVICE_DEFAULT}; +}; + +USTRUCT(BlueprintType) +struct FLibretroControllerDescriptions +{ + GENERATED_BODY() + + UPROPERTY(VisibleAnywhere) + FLibretroControllerDescription ControllerDescription[PortCount]; + + FLibretroControllerDescription& operator[](int Port) { return ControllerDescription[Port]; } + const FLibretroControllerDescription& operator[](int Port) const { return ControllerDescription[Port]; } +}; + DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnCoreIsReady, const class UTextureRenderTarget2D*, LibretroFramebuffer, const class USoundWave*, AudioBuffer); DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnCoreFramebufferResize); @@ -48,7 +48,7 @@ class UNREALLIBRETRO_API ULibretroCoreInstance : public UActorComponent virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; virtual void BeginDestroy(); - friend class FLibretroCoreInstanceDetails; + friend class FLibretroCoreInstanceDetails; /** Delegate Functions */ /** @@ -110,64 +110,64 @@ class UNREALLIBRETRO_API ULibretroCoreInstance : public UActorComponent UFUNCTION(BlueprintCallable, Category = "Libretro|IneffectiveBeforeLaunch") void Pause(bool ShouldPause = true); - /** - * @brief Effectively a getter version of `retro_set_controller_port_device` - * - * @see `retro_set_controller_port_device` in libretro.h - */ - UFUNCTION(BlueprintCallable, Category = "Libretro|IneffectiveBeforeCoreIsReady") + /** + * @brief Effectively a getter version of `retro_set_controller_port_device` + * + * @see `retro_set_controller_port_device` in libretro.h + */ + UFUNCTION(BlueprintCallable, Category = "Libretro|IneffectiveBeforeCoreIsReady") void GetController(int Port, int64 &ID, FString &Description); - /** - * @brief Sets the state of a button for the Libretro core - * - * Bindings in ConnectController will override inputs set here - * You can set analog inputs through this interface as well however it won't work well - * - */ - UFUNCTION(BlueprintCallable, Category = "Libretro|IneffectiveBeforeLaunch") - void SetInputDigital(int Port, bool Activated, ERetroDeviceID Input); - - /** - * @brief Sets the analog state of an input for the Libretro core - * - * This should mainly only be used for analog stick inputs and analog triggers - * The internal datatype returned when input is queried by the Libretro Core is int16 - * for digital inputs its 1 or 0 in which case use SetInputDigital - */ - UFUNCTION(BlueprintCallable, Category = "Libretro|IneffectiveBeforeLaunch") - void SetInputAnalog(int Port, int _16BitSignedInteger, ERetroDeviceID Input); - - /** - * @brief Where the Libretro Core's frame is drawn - * - * Tip: While running with PIE you can visually examine this - * 1. Click Eject or Press F8 - * 2. Click on the actor with the rendertarget you want to look at - * 3. Click on the LibretroCoreInstance component - * 4. Click the rendertarget in the details - */ + /** + * @brief Sets the state of a button for the Libretro core + * + * Bindings in ConnectController will override inputs set here + * You can set analog inputs through this interface as well however it won't work well + * + */ + UFUNCTION(BlueprintCallable, Category = "Libretro|IneffectiveBeforeLaunch") + void SetInputDigital(int Port, bool Activated, ERetroDeviceID Input); + + /** + * @brief Sets the analog state of an input for the Libretro core + * + * This should mainly only be used for analog stick inputs and analog triggers + * The internal datatype returned when input is queried by the Libretro Core is int16 + * for digital inputs its 1 or 0 in which case use SetInputDigital + */ + UFUNCTION(BlueprintCallable, Category = "Libretro|IneffectiveBeforeLaunch") + void SetInputAnalog(int Port, int _16BitSignedInteger, ERetroDeviceID Input); + + /** + * @brief Where the Libretro Core's frame is drawn + * + * Tip: While running with PIE you can visually examine this + * 1. Click Eject or Press F8 + * 2. Click on the actor with the rendertarget you want to look at + * 3. Click on the LibretroCoreInstance component + * 4. Click the rendertarget in the details + */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Libretro) UTextureRenderTarget2D* RenderTarget; UPROPERTY(BlueprintReadWrite, Category = Libretro) UAudioComponent* AudioComponent; - /** - * @brief A key-value map for configuring Libretro Cores - * - * Options are appended to this list when you change them in the 'Libretro Core Options' section of the details panel - * They are only added here if they differ from their default - */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Libretro) - TMap CoreOptions; - - /** - * Making @todo probably don't make this part of the public interface - */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Libretro, meta = (ShowInnerProperties)) - TMap ControllersSetOnLaunch; - + /** + * @brief A key-value map for configuring Libretro Cores + * + * Options are appended to this list when you change them in the 'Libretro Core Options' section of the details panel + * They are only added here if they differ from their default + */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Libretro) + TMap CoreOptions; + + /** + * Making @todo probably don't make this part of the public interface + */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Libretro, meta = (ShowInnerProperties)) + TMap ControllersSetOnLaunch; + /** * You should provide a path to your ROM relative to the MyROMs directory in the UnrealLibretro directory in your project's Plugins directory. * So if your ROM is at [MyProjectName]/Plugins/UnrealLibretro/MyROMs/myrom.rom this should be set to myrom.rom @@ -190,24 +190,24 @@ class UNREALLIBRETRO_API ULibretroCoreInstance : public UActorComponent UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Libretro, AdvancedDisplay) FString SRAMPath = "Default.srm"; - - /** These properties are with respect to how the frame is drawn by the Libretro Core in the framebuffer it's provided */ - UPROPERTY(BlueprintReadOnly, Category = Libretro) - float FrameRotation; - - UPROPERTY(BlueprintReadOnly, Category = Libretro) - int FrameWidth; - - UPROPERTY(BlueprintReadOnly, Category = Libretro) - int FrameHeight; - - UPROPERTY(BlueprintReadOnly, Category = Libretro) - bool bFrameBottomLeftOrigin; - - /** Forward keyboard input from this APlayerController to the libretro core if the core can receive it */ - UPROPERTY(BlueprintReadWrite, Category = Libretro) - APlayerController* KeyboardInputSourcePlayerController{nullptr}; - + + /** These properties are with respect to how the frame is drawn by the Libretro Core in the framebuffer it's provided */ + UPROPERTY(BlueprintReadOnly, Category = Libretro) + float FrameRotation; + + UPROPERTY(BlueprintReadOnly, Category = Libretro) + int FrameWidth; + + UPROPERTY(BlueprintReadOnly, Category = Libretro) + int FrameHeight; + + UPROPERTY(BlueprintReadOnly, Category = Libretro) + bool bFrameBottomLeftOrigin; + + /** Forward keyboard input from this APlayerController to the libretro core if the core can receive it */ + UPROPERTY(BlueprintReadWrite, Category = Libretro) + APlayerController* KeyboardInputSourcePlayerController{nullptr}; + protected: // @todo: It'd be nice if I could use something like std::wrapped_reference however Unreal doesn't offer an equivalent for now diff --git a/Source/UnrealLibretro/Public/LibretroInputDefinitions.h b/Source/UnrealLibretro/Public/LibretroInputDefinitions.h index 6a077b13..e38b7b44 100644 --- a/Source/UnrealLibretro/Public/LibretroInputDefinitions.h +++ b/Source/UnrealLibretro/Public/LibretroInputDefinitions.h @@ -2,104 +2,104 @@ #include "libretro/libretro.h" - -// By convention RETRO_DEVICE_JOYPAD is the default controller although often it represents something different compared to -// the libretro.h documentation of RETRO_DEVICE_JOYPAD. Basically it will be the most reasonable controller for the core -#define RETRO_DEVICE_DEFAULT RETRO_DEVICE_JOYPAD - - + +// By convention RETRO_DEVICE_JOYPAD is the default controller although often it represents something different compared to +// the libretro.h documentation of RETRO_DEVICE_JOYPAD. Basically it will be the most reasonable controller for the core +#define RETRO_DEVICE_DEFAULT RETRO_DEVICE_JOYPAD + + #include "InputCoreTypes.h" #include "CoreMinimal.h" -#include // One of the few std headers where its usage is recommended over a native Unreal Engine implementation see https://docs.unrealengine.com/5.0/en-US/epic-cplusplus-coding-standard-for-unreal-engine/#useofstandardlibraries - -// Check libretro.h for further documentation. More or less this maps to RETRO_DEVICE_ID values in there -// DO NOT REORDER THESE... doing so will break indexing badly.... -// The order for each RETRO_DEVICE is in the same numerical increasing order as every RETRO_DEVICE_*_ID value -UENUM(BlueprintType) -enum class ERetroDeviceID : uint8 +#include // One of the few std headers where its usage is recommended over a native Unreal Engine implementation see https://docs.unrealengine.com/5.0/en-US/epic-cplusplus-coding-standard-for-unreal-engine/#useofstandardlibraries + +// Check libretro.h for further documentation. More or less this maps to RETRO_DEVICE_ID values in there +// DO NOT REORDER THESE... doing so will break indexing badly.... +// The order for each RETRO_DEVICE is in the same numerical increasing order as every RETRO_DEVICE_*_ID value +UENUM(BlueprintType) +enum class ERetroDeviceID : uint8 { - // RETRO_DEVICE_ID_JOYPAD - JoypadB, - JoypadY, - JoypadSelect, - JoypadStart, - JoypadUp, - JoypadDown, - JoypadLeft, - JoypadRight, - JoypadA, - JoypadX, - JoypadL, - JoypadR, - JoypadL2, - JoypadR2, - JoypadL3, - JoypadR3, - - // RETRO_DEVICE_ID_LIGHTGUN - LightgunX UMETA(Hidden), // The Lightgun entries marked UMETA(Hidden) here are deprecated according to libretro.h - LightgunY UMETA(Hidden), - LightgunTrigger, - LightgunAuxA, - LightgunAuxB, - LightgunPause UMETA(Hidden), - LightgunStart, - LightgunSelect, - LightgunAuxC, - LightgunDpadUp, - LightgunDpadDown, - LightgunDpadLeft, - LightgunDpadRight, - LightgunScreenX, - LightgunScreenY, - LightgunIsOffscreen, - LightgunReload, - - // RETRO_DEVICE_ID_ANALOG (For triggers) - // CartesianProduct(RETRO_DEVICE_ID_ANALOG, RETRO_DEVICE_INDEX) (For stick input) - AnalogLeftX, - AnalogLeftY, - AnalogRightX, - AnalogRightY, - AnalogL2, - AnalogR2, - - // RETRO_DEVICE_ID_POINTER - PointerX, - PointerY, - PointerPressed, - PointerX1 UMETA(Hidden), - PointerY1 UMETA(Hidden), - PointerPressed1 UMETA(Hidden), - PointerX2 UMETA(Hidden), - PointerY2 UMETA(Hidden), - PointerPressed2 UMETA(Hidden), - PointerX3 UMETA(Hidden), - PointerY3 UMETA(Hidden), - PointerPressed3 UMETA(Hidden), - PointerCount, - - Size UMETA(Hidden), + // RETRO_DEVICE_ID_JOYPAD + JoypadB, + JoypadY, + JoypadSelect, + JoypadStart, + JoypadUp, + JoypadDown, + JoypadLeft, + JoypadRight, + JoypadA, + JoypadX, + JoypadL, + JoypadR, + JoypadL2, + JoypadR2, + JoypadL3, + JoypadR3, + + // RETRO_DEVICE_ID_LIGHTGUN + LightgunX UMETA(Hidden), // The Lightgun entries marked UMETA(Hidden) here are deprecated according to libretro.h + LightgunY UMETA(Hidden), + LightgunTrigger, + LightgunAuxA, + LightgunAuxB, + LightgunPause UMETA(Hidden), + LightgunStart, + LightgunSelect, + LightgunAuxC, + LightgunDpadUp, + LightgunDpadDown, + LightgunDpadLeft, + LightgunDpadRight, + LightgunScreenX, + LightgunScreenY, + LightgunIsOffscreen, + LightgunReload, + + // RETRO_DEVICE_ID_ANALOG (For triggers) + // CartesianProduct(RETRO_DEVICE_ID_ANALOG, RETRO_DEVICE_INDEX) (For stick input) + AnalogLeftX, + AnalogLeftY, + AnalogRightX, + AnalogRightY, + AnalogL2, + AnalogR2, + + // RETRO_DEVICE_ID_POINTER + PointerX, + PointerY, + PointerPressed, + PointerX1 UMETA(Hidden), + PointerY1 UMETA(Hidden), + PointerPressed1 UMETA(Hidden), + PointerX2 UMETA(Hidden), + PointerY2 UMETA(Hidden), + PointerPressed2 UMETA(Hidden), + PointerX3 UMETA(Hidden), + PointerY3 UMETA(Hidden), + PointerPressed3 UMETA(Hidden), + PointerCount, + + Size UMETA(Hidden), }; -// std alchemy that converts enum class variables to their integer type -template -static constexpr auto to_integral(E e) -> typename std::underlying_type::type +// std alchemy that converts enum class variables to their integer type +template +static constexpr auto to_integral(E e) -> typename std::underlying_type::type +{ + return static_cast::type>(e); +} + +constexpr int PortCount = 4; + +struct FLibretroInputState { - return static_cast::type>(e); -} - -constexpr int PortCount = 4; - -struct FLibretroInputState -{ - int16_t Data[to_integral(ERetroDeviceID::Size)]{0}; - - FLibretroInputState() {} - - int16_t& operator[](unsigned Index) { return Data[Index]; } - int16_t& operator[](ERetroDeviceID RetroDeviceID) { return Data[to_integral(RetroDeviceID)]; } + int16_t Data[to_integral(ERetroDeviceID::Size)]{0}; + + FLibretroInputState() {} + + int16_t& operator[](unsigned Index) { return Data[Index]; } + int16_t& operator[](ERetroDeviceID RetroDeviceID) { return Data[to_integral(RetroDeviceID)]; } }; // const for a reason if you make this non-const make sure you don't cause a data race also make it non static too diff --git a/Source/UnrealLibretro/Public/UnrealLibretro.h b/Source/UnrealLibretro/Public/UnrealLibretro.h index 5964f418..f98e58ee 100644 --- a/Source/UnrealLibretro/Public/UnrealLibretro.h +++ b/Source/UnrealLibretro/Public/UnrealLibretro.h @@ -1,68 +1,68 @@ #pragma once -#include "LibretroCoreInstance.h" +#include "LibretroCoreInstance.h" #include "Interfaces/IPluginManager.h" -#include "UObject/NameTypes.h" +#include "UObject/NameTypes.h" UNREALLIBRETRO_API DECLARE_LOG_CATEGORY_EXTERN(Libretro, Log, All); -#if PLATFORM_WINDOWS && PLATFORM_64BITS -# define PLATFORM_INDEX 0 -#elif PLATFORM_ANDROID_ARM -# define PLATFORM_INDEX 1 -#elif PLATFORM_ANDROID_ARM64 -# define PLATFORM_INDEX 2 -#endif - -static const struct { FString DistributionPath; FString Extension; FString BuildbotPath; FName ImageName; } CoreLibMetadata[] = -{ - { TEXT("Win64/"), "_libretro.dll", "https://buildbot.libretro.com/nightly/windows/x86_64/latest/" , "Launcher.Platform_Windows.Large" }, - { TEXT("Android/armeabi-v7a/"), "_libretro_android.so", "https://buildbot.libretro.com/nightly/android/latest/armeabi-v7a/", "Launcher.Platform_Android.Large" }, - { TEXT("Android/arm64-v8a/"), "_libretro_android.so", "https://buildbot.libretro.com/nightly/android/latest/arm64-v8a/" , "Launcher.Platform_Android.Large" }, -}; - -#if PLATFORM_WINDOWS -#include "Windows/AllowWindowsPlatformTypes.h" -#include "Windows/PreWindowsApi.h" -#include - -typedef HGLRC(WINAPI* PFN_wglCreateContext)(HDC); -typedef BOOL(WINAPI* PFN_wglDeleteContext)(HGLRC); -typedef PROC(WINAPI* PFN_wglGetProcAddress)(LPCSTR); -typedef HDC(WINAPI* PFN_wglGetCurrentDC)(void); -typedef HGLRC(WINAPI* PFN_wglGetCurrentContext)(void); -typedef BOOL(WINAPI* PFN_wglMakeCurrent)(HDC, HGLRC); -typedef BOOL(WINAPI* PFN_wglShareLists)(HGLRC, HGLRC); - -#define wglCreateContext _wglCreateContext -#define wglDeleteContext _wglDeleteContext -#define wglMakeCurrent _wglMakeCurrent -#define wglGetProcAddress _wglGetProcAddress - -#define GL_GET_PROC_ADDRESS Win32GLGetProcAddress - -extern void* OpenGLDLL; -extern PFN_wglCreateContext _wglCreateContext; -extern PFN_wglDeleteContext _wglDeleteContext; -extern PFN_wglMakeCurrent _wglMakeCurrent; -extern PFN_wglGetProcAddress _wglGetProcAddress; - -static void* Win32GLGetProcAddress(const char* procname) -{ - void* proc = wglGetProcAddress(procname); - - if (!proc) - { - proc = FPlatformProcess::GetDllExport(OpenGLDLL, ANSI_TO_TCHAR(procname)); - } - - return proc; -} - -#include "Windows/PostWindowsApi.h" -#include "Windows/HideWindowsPlatformTypes.h" -#endif - +#if PLATFORM_WINDOWS && PLATFORM_64BITS +# define PLATFORM_INDEX 0 +#elif PLATFORM_ANDROID_ARM +# define PLATFORM_INDEX 1 +#elif PLATFORM_ANDROID_ARM64 +# define PLATFORM_INDEX 2 +#endif + +static const struct { FString DistributionPath; FString Extension; FString BuildbotPath; FName ImageName; } CoreLibMetadata[] = +{ + { TEXT("Win64/"), "_libretro.dll", "https://buildbot.libretro.com/nightly/windows/x86_64/latest/" , "Launcher.Platform_Windows.Large" }, + { TEXT("Android/armeabi-v7a/"), "_libretro_android.so", "https://buildbot.libretro.com/nightly/android/latest/armeabi-v7a/", "Launcher.Platform_Android.Large" }, + { TEXT("Android/arm64-v8a/"), "_libretro_android.so", "https://buildbot.libretro.com/nightly/android/latest/arm64-v8a/" , "Launcher.Platform_Android.Large" }, +}; + +#if PLATFORM_WINDOWS +#include "Windows/AllowWindowsPlatformTypes.h" +#include "Windows/PreWindowsApi.h" +#include + +typedef HGLRC(WINAPI* PFN_wglCreateContext)(HDC); +typedef BOOL(WINAPI* PFN_wglDeleteContext)(HGLRC); +typedef PROC(WINAPI* PFN_wglGetProcAddress)(LPCSTR); +typedef HDC(WINAPI* PFN_wglGetCurrentDC)(void); +typedef HGLRC(WINAPI* PFN_wglGetCurrentContext)(void); +typedef BOOL(WINAPI* PFN_wglMakeCurrent)(HDC, HGLRC); +typedef BOOL(WINAPI* PFN_wglShareLists)(HGLRC, HGLRC); + +#define wglCreateContext _wglCreateContext +#define wglDeleteContext _wglDeleteContext +#define wglMakeCurrent _wglMakeCurrent +#define wglGetProcAddress _wglGetProcAddress + +#define GL_GET_PROC_ADDRESS Win32GLGetProcAddress + +extern void* OpenGLDLL; +extern PFN_wglCreateContext _wglCreateContext; +extern PFN_wglDeleteContext _wglDeleteContext; +extern PFN_wglMakeCurrent _wglMakeCurrent; +extern PFN_wglGetProcAddress _wglGetProcAddress; + +static void* Win32GLGetProcAddress(const char* procname) +{ + void* proc = wglGetProcAddress(procname); + + if (!proc) + { + proc = FPlatformProcess::GetDllExport(OpenGLDLL, ANSI_TO_TCHAR(procname)); + } + + return proc; +} + +#include "Windows/PostWindowsApi.h" +#include "Windows/HideWindowsPlatformTypes.h" +#endif + class FUnrealLibretroModule : public IModuleInterface { public: @@ -73,11 +73,11 @@ class FUnrealLibretroModule : public IModuleInterface /** Path Resolution */ - static bool IsCoreName(const FString &CorePath) - { - return FPaths::GetExtension(CorePath).IsEmpty() && !CorePath.IsEmpty() && FPaths::GetPath(CorePath).IsEmpty(); - } - + static bool IsCoreName(const FString &CorePath) + { + return FPaths::GetExtension(CorePath).IsEmpty() && !CorePath.IsEmpty() && FPaths::GetPath(CorePath).IsEmpty(); + } + template static FString IfRelativeResolvePathRelativeToThisPluginWithPathExtensions(const FString& Path, PathTypes&&... InPaths) { @@ -91,9 +91,9 @@ class FUnrealLibretroModule : public IModuleInterface static FString ResolveCorePath(FString UnresolvedCorePath) { - if (IsCoreName(UnresolvedCorePath)) - { - UnresolvedCorePath = CoreLibMetadata[PLATFORM_INDEX].DistributionPath + *UnresolvedCorePath + CoreLibMetadata[PLATFORM_INDEX].Extension; + if (IsCoreName(UnresolvedCorePath)) + { + UnresolvedCorePath = CoreLibMetadata[PLATFORM_INDEX].DistributionPath + *UnresolvedCorePath + CoreLibMetadata[PLATFORM_INDEX].Extension; } return IfRelativeResolvePathRelativeToThisPluginWithPathExtensions(UnresolvedCorePath, TEXT("MyCores")); @@ -114,25 +114,25 @@ class FUnrealLibretroModule : public IModuleInterface return IfRelativeResolvePathRelativeToThisPluginWithPathExtensions(UnresolvedSavePath, TEXT("Saves"), TEXT("SRAM"), FPaths::GetCleanFilename(UnresolvedRomPath)); } - static void EnvironmentParseControllerInfo(const retro_controller_info* controller_info, - TStaticArray, PortCount>& ControllerDescriptions) - { - for (int port = 0; controller_info[port].types != NULL && port < PortCount; port++) { - for (unsigned t = 0; t < controller_info[port].num_types; t++) { - if (controller_info[port].types[t].desc == nullptr) break; // Not part of Libretro API but needed check for some cores - - retro_controller_description controller_description = controller_info[port].types[t]; - ControllerDescriptions[port].Add({ controller_description.desc, - controller_description.id }); - } - } - } - + static void EnvironmentParseControllerInfo(const retro_controller_info* controller_info, + TStaticArray, PortCount>& ControllerDescriptions) + { + for (int port = 0; controller_info[port].types != NULL && port < PortCount; port++) { + for (unsigned t = 0; t < controller_info[port].num_types; t++) { + if (controller_info[port].types[t].desc == nullptr) break; // Not part of Libretro API but needed check for some cores + + retro_controller_description controller_description = controller_info[port].types[t]; + ControllerDescriptions[port].Add({ controller_description.desc, + controller_description.id }); + } + } + } + private: #ifdef PLATFORM_WINDOWS FString RedistDirectory; #endif }; - -#undef PLATFORM_INDEX \ No newline at end of file + +#undef PLATFORM_INDEX diff --git a/Source/UnrealLibretro/UnrealLibretro.Build.cs b/Source/UnrealLibretro/UnrealLibretro.Build.cs index a1ba0716..a8b3334e 100644 --- a/Source/UnrealLibretro/UnrealLibretro.Build.cs +++ b/Source/UnrealLibretro/UnrealLibretro.Build.cs @@ -16,7 +16,7 @@ public UnrealLibretro(ReadOnlyTargetRules Target) : base(Target) RuntimeDependencies.Add("$(PluginDir)/Saves/*" ); RuntimeDependencies.Add("$(PluginDir)/System/*" ); - if (Target.Platform.Equals(UnrealTargetPlatform.Win64)) + if (Target.Platform.Equals(UnrealTargetPlatform.Win64)) { RuntimeDependencies.Add("$(PluginDir)/MyCores/Win64/*"); RuntimeDependencies.Add("$(PluginDir)/Binaries/Win64/ThirdParty/libretro/*"); @@ -37,8 +37,8 @@ public UnrealLibretro(ReadOnlyTargetRules Target) : base(Target) "Engine", "InputCore", "Projects", - "HeadMountedDisplay", - "NavigationSystem", + "HeadMountedDisplay", + "NavigationSystem", } ); diff --git a/UnrealLibretro.uplugin b/UnrealLibretro.uplugin index 60261e6c..9dc8d153 100644 --- a/UnrealLibretro.uplugin +++ b/UnrealLibretro.uplugin @@ -1,31 +1,31 @@ -{ - "FileVersion": 3, - "Version": 1, - "VersionName": "1.0", - "FriendlyName": "UnrealLibretro", - "Description": "Runs emulators and such within Unreal Engine", - "Category": "Other", - "CreatedBy": "John Rehbein", - "CreatedByURL": "", - "DocsURL": "", - "MarketplaceURL": "", - "SupportURL": "", - "CanContainContent": true, - "IsBetaVersion": true, - "IsExperimentalVersion": false, - "Installed": false, - "Modules": [ - { - "Name": "UnrealLibretro", - "Type": "Runtime", - "LoadingPhase": "Default", - "WhitelistPlatforms": [ "Win64", "Android" ] - }, - { - "Name": "UnrealLibretroEditor", - "Type": "Runtime", - "LoadingPhase": "Default", - "WhitelistPlatforms": [ "Win64" ] - } - ] +{ + "FileVersion": 3, + "Version": 1, + "VersionName": "1.0", + "FriendlyName": "UnrealLibretro", + "Description": "Runs emulators and such within Unreal Engine", + "Category": "Other", + "CreatedBy": "John Rehbein", + "CreatedByURL": "", + "DocsURL": "", + "MarketplaceURL": "", + "SupportURL": "", + "CanContainContent": true, + "IsBetaVersion": true, + "IsExperimentalVersion": false, + "Installed": false, + "Modules": [ + { + "Name": "UnrealLibretro", + "Type": "Runtime", + "LoadingPhase": "Default", + "WhitelistPlatforms": [ "Win64", "Android" ] + }, + { + "Name": "UnrealLibretroEditor", + "Type": "Runtime", + "LoadingPhase": "Default", + "WhitelistPlatforms": [ "Win64" ] + } + ] } \ No newline at end of file diff --git a/setup.sh b/setup.sh index 940d3853..192f4786 100755 --- a/setup.sh +++ b/setup.sh @@ -23,4 +23,4 @@ cp /tmp/RetroArch-Win64/*.dll Binaries/Win64/ThirdParty/libretro # Acquire and move unversioned data from unversioned branch (Note this will break if you don't have a really new version of git) git fetch -git restore --source origin/unversioned Content/ +git restore --source origin/unversioned Content/ From 72b3864b702e64f72f98757f4b60514a347db9c4 Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Sat, 13 May 2023 22:06:00 -0700 Subject: [PATCH 15/79] Make gamemode to select between VR & non-VR pawns --- .../UnrealLibretro/Public/LibretroGameMode.h | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 Source/UnrealLibretro/Public/LibretroGameMode.h diff --git a/Source/UnrealLibretro/Public/LibretroGameMode.h b/Source/UnrealLibretro/Public/LibretroGameMode.h new file mode 100644 index 00000000..599de9f0 --- /dev/null +++ b/Source/UnrealLibretro/Public/LibretroGameMode.h @@ -0,0 +1,65 @@ +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/GameModeBase.h" +#include "Engine/World.h" +#include "LibretroVRPawn.h" +#include "UObject/ConstructorHelpers.h" +#include "GameFramework/HUD.h" +#include "HeadMountedDisplayFunctionLibrary.h" +#include "LibretroGameMode.generated.h" + + + +UCLASS() +class UNREALLIBRETRO_API ALibretroGameMode : public AGameModeBase +{ + GENERATED_BODY() + +public: + // @todo There is probably some smarter way of determining this + // This returns true if you launch PIE in "VR Preview" mode + static CONSTEXPR auto ShouldStartPlayerInVRPawn = UHeadMountedDisplayFunctionLibrary::IsHeadMountedDisplayEnabled; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Classes) + TSubclassOf DefaultVRPawnClass; + + ALibretroGameMode() + { + static ConstructorHelpers::FClassFinder CharacterClassFinder(TEXT("/UnrealLibretro/Blueprints/LibretroCharacter")); + DefaultPawnClass = CharacterClassFinder.Class; + + static ConstructorHelpers::FClassFinder HUDClassFinder(TEXT("/UnrealLibretro/Blueprints/LibretroHUD")); + HUDClass = HUDClassFinder.Class; + + static ConstructorHelpers::FClassFinder VRPawnClassFinder(TEXT("/UnrealLibretro/Blueprints/LibretroVRPawnBlueprint")); + DefaultVRPawnClass = VRPawnClassFinder.Class; + } + +protected: + virtual void InitializeHUDForPlayer_Implementation(APlayerController* NewPlayer) override + { + if (ShouldStartPlayerInVRPawn()) + { + // The HUD currently displays information irrelevant to VR Players... so disable it + NewPlayer->ClientSetHUD(nullptr); + } + else + { + NewPlayer->ClientSetHUD(HUDClass); + } + } + + // I could not get this procedure to be called in blueprints that's the primary reason why I wrote this class in C++ + virtual APawn* SpawnDefaultPawnFor_Implementation(AController* NewPlayer, AActor* StartSpot) override + { + FActorSpawnParameters SpawnParams; + SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn; + SpawnParams.Owner = NewPlayer; + SpawnParams.Instigator = nullptr; + + auto Pawn = GetWorld()->SpawnActor(ShouldStartPlayerInVRPawn() ? DefaultVRPawnClass : DefaultPawnClass, StartSpot->GetActorTransform(), SpawnParams); + + return Pawn; + } +}; From 05153c02b6f06c83dc855d96d41dcf8845dd071a Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Sun, 14 May 2023 12:01:32 -0700 Subject: [PATCH 16/79] Fix bloated include UBT complained about --- Source/UnrealLibretro/Private/sdlarch.cpp | 1 - Source/UnrealLibretro/Private/sdlarch.h | 1 + Source/UnrealLibretro/Public/UnrealLibretro.h | 1 - 3 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Source/UnrealLibretro/Private/sdlarch.cpp b/Source/UnrealLibretro/Private/sdlarch.cpp index 7f0023ea..56ac5469 100644 --- a/Source/UnrealLibretro/Private/sdlarch.cpp +++ b/Source/UnrealLibretro/Private/sdlarch.cpp @@ -20,7 +20,6 @@ extern "C" #if PLATFORM_WINDOWS #include "Windows/AllowWindowsPlatformTypes.h" #include "Windows/PreWindowsApi.h" -#include #include #include "Windows/PostWindowsApi.h" #include "Windows/HideWindowsPlatformTypes.h" diff --git a/Source/UnrealLibretro/Private/sdlarch.h b/Source/UnrealLibretro/Private/sdlarch.h index 05edc01f..9930e50d 100644 --- a/Source/UnrealLibretro/Private/sdlarch.h +++ b/Source/UnrealLibretro/Private/sdlarch.h @@ -21,6 +21,7 @@ static_assert(RETRO_API_VERSION == 1, "Retro API version changed"); #if PLATFORM_WINDOWS #include "Windows/PreWindowsApi.h" +#include "Windows/WindowsHWrapper.h" #endif #include "ThirdParty/OpenGL/GL/glcorearb.h" diff --git a/Source/UnrealLibretro/Public/UnrealLibretro.h b/Source/UnrealLibretro/Public/UnrealLibretro.h index f98e58ee..b58ceb81 100644 --- a/Source/UnrealLibretro/Public/UnrealLibretro.h +++ b/Source/UnrealLibretro/Public/UnrealLibretro.h @@ -24,7 +24,6 @@ static const struct { FString DistributionPath; FString Extension; FString Build #if PLATFORM_WINDOWS #include "Windows/AllowWindowsPlatformTypes.h" #include "Windows/PreWindowsApi.h" -#include typedef HGLRC(WINAPI* PFN_wglCreateContext)(HDC); typedef BOOL(WINAPI* PFN_wglDeleteContext)(HGLRC); From 6b4c0d91f4229ba94bfb2a04f711104015e1606b Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Sun, 14 May 2023 12:28:14 -0700 Subject: [PATCH 17/79] Fix an invalid state transition and document --- Source/UnrealLibretro/Private/sdlarch.cpp | 5 ++++- Source/UnrealLibretro/Private/sdlarch.h | 16 +++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Source/UnrealLibretro/Private/sdlarch.cpp b/Source/UnrealLibretro/Private/sdlarch.cpp index 56ac5469..f0a7eb3e 100644 --- a/Source/UnrealLibretro/Private/sdlarch.cpp +++ b/Source/UnrealLibretro/Private/sdlarch.cpp @@ -1173,7 +1173,10 @@ void LibretroContext::Pause(bool ShouldPause) // Alternatively you could add one more state to accomplish this, but this is fine for now EnqueueTask([=](auto&&) { - CoreState.store(ShouldPause ? ECoreState::Paused : ECoreState::Running, std::memory_order_relaxed); + if (CoreState.load(std::memory_order_relaxed) != ECoreState::Shutdown) + { + CoreState.store(ShouldPause ? ECoreState::Paused : ECoreState::Running, std::memory_order_relaxed); + } }); } diff --git a/Source/UnrealLibretro/Private/sdlarch.h b/Source/UnrealLibretro/Private/sdlarch.h index 9930e50d..c90c720b 100644 --- a/Source/UnrealLibretro/Private/sdlarch.h +++ b/Source/UnrealLibretro/Private/sdlarch.h @@ -137,9 +137,23 @@ struct LibretroContext { TUniqueFunction::Type> CoreEnvironmentCallback; + /** + * @brief Describes the state of the core we're executing + * + * @note Certain fields like ControllerDescriptions are only safe to access from other threads once we're in the running state @todo Maybe add encapsulation to make this explicit + * The following State Machine indicates valid state transitions: + * +----------+ +-----------+ + * | Starting |--------->| Running |------------+ + * +----------+ +-----------+ | + * ^ | | + * | v v + * +-----------+ +-----------+ + * | Paused |------>| Shutdown | + * +-----------+ +-----------+ + */ enum class ECoreState : int8 { - Starting, + Starting, // Guranteed to stay in this state until we start running the core Running, Paused, Shutdown From ac352ab2df57cdc33f75553b76a710c2a708acf7 Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Mon, 15 May 2023 19:05:26 -0700 Subject: [PATCH 18/79] Improve VRPawn grab action --- Source/UnrealLibretro/Public/LibretroVRPawn.h | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/Source/UnrealLibretro/Public/LibretroVRPawn.h b/Source/UnrealLibretro/Public/LibretroVRPawn.h index 568c5f8b..32f5e869 100644 --- a/Source/UnrealLibretro/Public/LibretroVRPawn.h +++ b/Source/UnrealLibretro/Public/LibretroVRPawn.h @@ -159,31 +159,37 @@ class UNREALLIBRETRO_API ALibretroVRPawn : public APawn ULibretroGrabComponent* NearestGrabComponent = nullptr; FVector GripPosition = MotionController->GetComponentLocation(); - if (auto* World = GEngine->GetWorldFromContextObject(this, EGetWorldErrorMode::LogAndReturnNull)) + if (UWorld* World = GEngine->GetWorldFromContextObject(this, EGetWorldErrorMode::LogAndReturnNull)) { - FHitResult HitResult; - bool bHasHitResult = World->SweepSingleByChannel( - HitResult, + TArray HitResults; + bool bHasHitResults = World->SweepMultiByChannel( + HitResults, GripPosition, GripPosition, FQuat::Identity, - ECollisionChannel::ECC_PhysicsBody, + ECollisionChannel::ECC_PhysicsBody, FCollisionShape::MakeSphere(GrabRadiusFromGripPosition), FCollisionQueryParams() /* bTraceComplex = false */); - if (bHasHitResult && HitResult.GetActor()) + if (bHasHitResults) { - decltype(GripPosition.Size()) NearestComponentDistance = MAX_flt; + double NearestComponentDistance = MAX_dbl; - for (auto* Component : HitResult.GetActor()->GetComponents()) + for (const FHitResult& HitResult : HitResults) { - if (auto* GrabComponent = Cast(Component)) + if (AActor* HitActor = HitResult.GetActor()) { - auto GrabComponentDistance = (GrabComponent->GetComponentLocation() - GripPosition).Size(); - if (GrabComponentDistance <= NearestComponentDistance) + for (UActorComponent* Component : HitActor->GetComponents()) { - NearestGrabComponent = GrabComponent; - NearestComponentDistance = GrabComponentDistance; + if (ULibretroGrabComponent* GrabComponent = Cast(Component)) + { + double GrabComponentDistance = (GrabComponent->GetComponentLocation() - GripPosition).Size(); + if (GrabComponentDistance < NearestComponentDistance) + { + NearestGrabComponent = GrabComponent; + NearestComponentDistance = GrabComponentDistance; + } + } } } } From 26f7641806e790bea60fc535bec566a70b5543af Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Mon, 15 May 2023 19:11:09 -0700 Subject: [PATCH 19/79] Improve compatibility between engine versions This fixes MotionController behavior on 4.26 and beyond --- .../Public/LibretroGrabComponent.h | 4 +- Source/UnrealLibretro/Public/LibretroVRPawn.h | 49 +++++++++++-------- 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/Source/UnrealLibretro/Public/LibretroGrabComponent.h b/Source/UnrealLibretro/Public/LibretroGrabComponent.h index 767a7e20..98d45e6d 100644 --- a/Source/UnrealLibretro/Public/LibretroGrabComponent.h +++ b/Source/UnrealLibretro/Public/LibretroGrabComponent.h @@ -91,7 +91,7 @@ class UNREALLIBRETRO_API ULibretroGrabComponent : public USceneComponent, public } UFUNCTION(BlueprintCallable) - bool TryGrab(UMotionControllerComponent* MotionController) + bool TryGrab(UMotionControllerComponent* MotionController, FRotator OrientationBias = FRotator::ZeroRotator) { // Then 0: Try to grab switch (GrabType) @@ -127,7 +127,7 @@ class UNREALLIBRETRO_API ULibretroGrabComponent : public USceneComponent, public // Orient the held Actor to match GrabComponent's relative location GetAttachParent()->SetRelativeRotation( - GetRelativeRotation().GetInverse(), + FRotator(OrientationBias.Quaternion() * GetRelativeRotation().GetInverse().Quaternion()), /* Sweep = */ false, /* OutSweepHitResult = */ nullptr, ETeleportType::TeleportPhysics); diff --git a/Source/UnrealLibretro/Public/LibretroVRPawn.h b/Source/UnrealLibretro/Public/LibretroVRPawn.h index 32f5e869..54054c21 100644 --- a/Source/UnrealLibretro/Public/LibretroVRPawn.h +++ b/Source/UnrealLibretro/Public/LibretroVRPawn.h @@ -36,12 +36,18 @@ class UNREALLIBRETRO_API ALibretroVRPawn : public APawn MotionControllerLeft = CreateDefaultSubobject(TEXT("MotionControllerLeft")); MotionControllerRight = CreateDefaultSubobject(TEXT("MotionControllerRight")); - // John: I set defaults in a blueprint subclass instead since I think that's slightly easier to understand - // You can filter for settings that differ from the default in the editor which is a nice feature writing new defaults here would mask that ability - //MotionControllerLeft->bDisplayDeviceModel = true; - //MotionControllerRight->bDisplayDeviceModel = true; - //MotionControllerLeft->MotionSource = "Left"; - //MotionControllerRight->MotionSource = "Right"; +#if ENGINE_MAJOR_VERSION > 4 \ + || ENGINE_MINOR_VERSION >= 26 + MotionControllerLeftAim = CreateDefaultSubobject(TEXT("MotionControllerLeftAim")); + MotionControllerRightAim = CreateDefaultSubobject(TEXT("MotionControllerRightAim")); + MotionControllerLeftAim->MotionSource = "LeftAim"; + MotionControllerRightAim->MotionSource = "RightAim"; + MotionControllerLeftAim->SetupAttachment(DefaultRootComponent); + MotionControllerRightAim->SetupAttachment(DefaultRootComponent); +#else + MotionControllerLeftAim = MotionControllerLeft; + MotionControllerRightAim = MotionControllerRight; +#endif Camera->SetupAttachment(DefaultRootComponent); MotionControllerLeft->SetupAttachment(DefaultRootComponent); @@ -95,38 +101,38 @@ class UNREALLIBRETRO_API ALibretroVRPawn : public APawn UPROPERTY(EditAnywhere) UMotionControllerComponent* MotionControllerRight; - // These just aren't available in 4.24 - //UPROPERTY(EditAnywhere) - //UMotionControllerComponent* MotionControllerLeftAim; - // - //UPROPERTY(EditAnywhere) - //UMotionControllerComponent* MotionControllerRightAim; + UPROPERTY(EditAnywhere) + UMotionControllerComponent* MotionControllerLeftAim; + + UPROPERTY(EditAnywhere) + UMotionControllerComponent* MotionControllerRightAim; UPROPERTY(EditAnywhere) UStaticMesh* SplineMesh; UPROPERTY() - ULibretroGrabComponent *HeldComponentLeft = nullptr; + ULibretroGrabComponent* HeldComponentLeft = nullptr; UPROPERTY() - ULibretroGrabComponent *HeldComponentRight = nullptr; + ULibretroGrabComponent* HeldComponentRight = nullptr; protected: struct FHand { - decltype(HeldComponentLeft) &HeldComponent; - decltype(MotionControllerLeft) &MotionController; + decltype(HeldComponentLeft)& HeldComponent; + decltype(MotionControllerLeft)& MotionController; + decltype(MotionControllerLeftAim)& MotionControllerAim; }; CONSTEXPR FHand GetHand(EControllerHand ControllerHand) { if (ControllerHand == EControllerHand::Left) { - return { HeldComponentLeft, MotionControllerLeft }; + return { HeldComponentLeft, MotionControllerLeft, MotionControllerLeftAim }; } else { - return { HeldComponentRight, MotionControllerRight }; + return { HeldComponentRight, MotionControllerRight, MotionControllerRightAim }; } } @@ -236,7 +242,10 @@ class UNREALLIBRETRO_API ALibretroVRPawn : public APawn { if (auto* GrabComponent = GetGrabComponentNearMotionController(Hand.MotionController)) { - if (GrabComponent->TryGrab(Hand.MotionController)) + // John: This bias makes it so we don't aim up on Unreal Engine 4.26 or later since Epic + // changed how UMotionControllerComponent::MotionSource behaved in that version + FRotator OrientationAimBias = FRotator(Hand.MotionController->GetRelativeRotation().Quaternion().Inverse() * Hand.MotionControllerAim->GetRelativeRotation().Quaternion()); + if (GrabComponent->TryGrab(Hand.MotionController, OrientationAimBias)) { Hand.HeldComponent = GrabComponent; @@ -386,7 +395,7 @@ protected: bool bCanSnapTurn = true; } } - TeleportTrace(MotionControllerRight->GetComponentLocation(), MotionControllerRight->GetForwardVector()); + TeleportTrace(MotionControllerRightAim->GetComponentLocation(), MotionControllerRightAim->GetForwardVector()); } else { From 3dd94ae0d497198ee5c23178bc02588f3e2af5a2 Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Mon, 15 May 2023 19:14:16 -0700 Subject: [PATCH 20/79] Ditch the inherited blueprint idea --- Source/UnrealLibretro/Public/LibretroGameMode.h | 5 +---- Source/UnrealLibretro/Public/LibretroVRPawn.h | 11 +++++++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Source/UnrealLibretro/Public/LibretroGameMode.h b/Source/UnrealLibretro/Public/LibretroGameMode.h index 599de9f0..8b6ede21 100644 --- a/Source/UnrealLibretro/Public/LibretroGameMode.h +++ b/Source/UnrealLibretro/Public/LibretroGameMode.h @@ -22,7 +22,7 @@ class UNREALLIBRETRO_API ALibretroGameMode : public AGameModeBase static CONSTEXPR auto ShouldStartPlayerInVRPawn = UHeadMountedDisplayFunctionLibrary::IsHeadMountedDisplayEnabled; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Classes) - TSubclassOf DefaultVRPawnClass; + TSubclassOf DefaultVRPawnClass = ALibretroVRPawn::StaticClass(); ALibretroGameMode() { @@ -31,9 +31,6 @@ class UNREALLIBRETRO_API ALibretroGameMode : public AGameModeBase static ConstructorHelpers::FClassFinder HUDClassFinder(TEXT("/UnrealLibretro/Blueprints/LibretroHUD")); HUDClass = HUDClassFinder.Class; - - static ConstructorHelpers::FClassFinder VRPawnClassFinder(TEXT("/UnrealLibretro/Blueprints/LibretroVRPawnBlueprint")); - DefaultVRPawnClass = VRPawnClassFinder.Class; } protected: diff --git a/Source/UnrealLibretro/Public/LibretroVRPawn.h b/Source/UnrealLibretro/Public/LibretroVRPawn.h index 54054c21..9d06c0f5 100644 --- a/Source/UnrealLibretro/Public/LibretroVRPawn.h +++ b/Source/UnrealLibretro/Public/LibretroVRPawn.h @@ -29,6 +29,12 @@ class UNREALLIBRETRO_API ALibretroVRPawn : public APawn { PrimaryActorTick.bCanEverTick = false; + static ConstructorHelpers::FClassFinder VRTeleportVisualizerClassFinder(TEXT("/UnrealLibretro/Blueprints/LibretroVRTeleportVisualizer")); + TeleportVisualizerClass = VRTeleportVisualizerClassFinder.Class; + + static ConstructorHelpers::FObjectFinder BeamMeshFinder(TEXT("/UnrealLibretro/Mesh/BeamMesh")); + SplineMesh = BeamMeshFinder.Object; + auto* DefaultRootComponent = CreateDefaultSubobject(TEXT("DefaultSceneRoot")); SetRootComponent(DefaultRootComponent); @@ -36,6 +42,11 @@ class UNREALLIBRETRO_API ALibretroVRPawn : public APawn MotionControllerLeft = CreateDefaultSubobject(TEXT("MotionControllerLeft")); MotionControllerRight = CreateDefaultSubobject(TEXT("MotionControllerRight")); + MotionControllerLeft->MotionSource = "Left"; + MotionControllerRight->MotionSource = "Right"; + MotionControllerLeft->bDisplayDeviceModel = true; + MotionControllerRight->bDisplayDeviceModel = true; + #if ENGINE_MAJOR_VERSION > 4 \ || ENGINE_MINOR_VERSION >= 26 MotionControllerLeftAim = CreateDefaultSubobject(TEXT("MotionControllerLeftAim")); From f5fa95e547e08c29d5ff6b94fb42898888966b75 Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Mon, 15 May 2023 21:09:26 -0700 Subject: [PATCH 21/79] Remove useless pragma --- Source/UnrealLibretro/Private/LambdaRunnable.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/Source/UnrealLibretro/Private/LambdaRunnable.cpp b/Source/UnrealLibretro/Private/LambdaRunnable.cpp index 062c6fe5..c04b97e7 100644 --- a/Source/UnrealLibretro/Private/LambdaRunnable.cpp +++ b/Source/UnrealLibretro/Private/LambdaRunnable.cpp @@ -1,5 +1,3 @@ -#pragma once - #include "LambdaRunnable.h" #include "HAL/Runnable.h" #include "HAL/RunnableThread.h" From 057a806ea41ce74b10143ce2024e2e59c1a7a834 Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Mon, 15 May 2023 21:11:29 -0700 Subject: [PATCH 22/79] Add procedure for engine version compatibility --- .../LibretroBlueprintFunctionLibrary.cpp | 24 +++++++++++++++++++ .../Public/LibretroBlueprintFunctionLibrary.h | 3 +++ Source/UnrealLibretro/UnrealLibretro.Build.cs | 6 +++++ 3 files changed, 33 insertions(+) diff --git a/Source/UnrealLibretro/Private/LibretroBlueprintFunctionLibrary.cpp b/Source/UnrealLibretro/Private/LibretroBlueprintFunctionLibrary.cpp index 4c17de30..35d0fa87 100644 --- a/Source/UnrealLibretro/Private/LibretroBlueprintFunctionLibrary.cpp +++ b/Source/UnrealLibretro/Private/LibretroBlueprintFunctionLibrary.cpp @@ -6,6 +6,13 @@ #include "Camera/CameraComponent.h" #include "PhysicsEngine/PhysicsSettings.h" +#if ENGINE_MAJOR_VERSION != 5 \ + || ENGINE_MINOR_VERSION < 1 +#include "OculusFunctionLibrary.h" +#else +#include "HeadMountedDisplayFunctionLibrary.h" +#endif + AActor* ULibretroBlueprintFunctionLibrary::LookingAtActor(UCameraComponent* CameraComponent, EBranchNames& Branch) { UWorld* const World = GEngine->GetWorldFromContextObjectChecked(CameraComponent); @@ -31,4 +38,21 @@ UActorComponent* ULibretroBlueprintFunctionLibrary::HasComponent(AActor* Actor, bool ULibretroBlueprintFunctionLibrary::IsSupportUVFromHitResultsEnabledInConfig() { return UPhysicsSettings::Get()->bSupportUVFromHitResults; +} + +FTransform ULibretroBlueprintFunctionLibrary::GetPlayAreaTransform() +{ + FTransform Transform; + +#if ENGINE_MAJOR_VERSION == 5 \ + && ENGINE_MINOR_VERSION >= 1 + FVector2D Rect; + UHeadMountedDisplayFunctionLibrary::GetPlayAreaRect(Transform, Rect); + Transform.SetScale3D({Rect.Y / 100.0, Rect.X / 100.0, 1.0}); // The cube we're scaling is 100 cm^3 which is why we divide by 100 since I'm assuming that's the units we're given + // I'm not really sure why the coordinates are swapped maybe OpenXR uses a different axes convention? +#else + Transform = UOculusFunctionLibrary::GetPlayAreaTransform(); +#endif + + return Transform; } \ No newline at end of file diff --git a/Source/UnrealLibretro/Public/LibretroBlueprintFunctionLibrary.h b/Source/UnrealLibretro/Public/LibretroBlueprintFunctionLibrary.h index 3e139ee1..202e6515 100644 --- a/Source/UnrealLibretro/Public/LibretroBlueprintFunctionLibrary.h +++ b/Source/UnrealLibretro/Public/LibretroBlueprintFunctionLibrary.h @@ -33,4 +33,7 @@ class UNREALLIBRETRO_API ULibretroBlueprintFunctionLibrary : public UBlueprintFu UFUNCTION(BlueprintPure, Category = "Libretro|Util") static bool IsSupportUVFromHitResultsEnabledInConfig(); + + UFUNCTION(BlueprintCallable, Category = "Libretro|Util") + static FTransform GetPlayAreaTransform(); }; diff --git a/Source/UnrealLibretro/UnrealLibretro.Build.cs b/Source/UnrealLibretro/UnrealLibretro.Build.cs index a8b3334e..fd63499b 100644 --- a/Source/UnrealLibretro/UnrealLibretro.Build.cs +++ b/Source/UnrealLibretro/UnrealLibretro.Build.cs @@ -42,6 +42,12 @@ public UnrealLibretro(ReadOnlyTargetRules Target) : base(Target) } ); + if ( Target.Version.MajorVersion == 4 + || Target.Version.MinorVersion == 0) + { + PrivateDependencyModuleNames.Add("OculusHMD"); + } + if ( Target.Version.MajorVersion > 4 || Target.Version.MinorVersion >= 26) { From 29701003437ad78adfc6d3c054439a8409e0d035 Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Mon, 15 May 2023 21:14:12 -0700 Subject: [PATCH 23/79] Fix #21 UE5_2 Build --- Source/UnrealLibretro/Private/sdlarch.cpp | 6 +++++- Source/UnrealLibretro/UnrealLibretro.Build.cs | 6 ++++++ .../Private/LibretroCoreInstanceDetails.cpp | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Source/UnrealLibretro/Private/sdlarch.cpp b/Source/UnrealLibretro/Private/sdlarch.cpp index f0a7eb3e..9f4ac4c5 100644 --- a/Source/UnrealLibretro/Private/sdlarch.cpp +++ b/Source/UnrealLibretro/Private/sdlarch.cpp @@ -454,11 +454,15 @@ static bool GLLogCall(const char* function, const char* file, int line) { check(this->Unreal.TextureRHI.GetReference()); - RHICmdList.EnqueueLambda([=](FRHICommandList&) + RHICmdList.EnqueueLambda([=](FRHICommandList& RHICmdList) { // Potentially this should be a TryLock() so you don't preempt the render thread although it's unlikely that would happen FScopeLock UploadTextureToRenderHardware(&this->Unreal.FrameUpload.CriticalSection); GDynamicRHI->RHIUpdateTexture2D( +#if ENGINE_MAJOR_VERSION == 5 \ + && ENGINE_MINOR_VERSION >= 2 + RHICmdList, +#endif this->Unreal.TextureRHI.GetReference(), MipIndex, Region, diff --git a/Source/UnrealLibretro/UnrealLibretro.Build.cs b/Source/UnrealLibretro/UnrealLibretro.Build.cs index fd63499b..64c7e992 100644 --- a/Source/UnrealLibretro/UnrealLibretro.Build.cs +++ b/Source/UnrealLibretro/UnrealLibretro.Build.cs @@ -53,5 +53,11 @@ public UnrealLibretro(ReadOnlyTargetRules Target) : base(Target) { PublicDependencyModuleNames.Add("DeveloperSettings"); // Was moved into its own module in 4.26 } + + if ( Target.Version.MajorVersion == 5 + && Target.Version.MinorVersion >= 2) + { + PrivateDependencyModuleNames.Add("AudioExtensions"); + } } } diff --git a/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.cpp b/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.cpp index 5ae9c996..c27ad624 100644 --- a/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.cpp +++ b/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.cpp @@ -25,6 +25,7 @@ #include "Interfaces/IHttpResponse.h" #include "HAL/FileManager.h" #include "Misc/FileHelper.h" +#include "EditorStyleSet.h" TSharedRef FLibretroCoreInstanceDetails::MakeInstance() { From 2300105de3731c6dcc657dad86734910255c43f9 Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Sun, 21 May 2023 17:54:53 -0700 Subject: [PATCH 24/79] Add SIMD support for ARM NEON --- .../Public/libretro/gfx/scaler/pixconv.c | 66 +++++++++++++++++-- 1 file changed, 62 insertions(+), 4 deletions(-) diff --git a/Source/UnrealLibretro/Public/libretro/gfx/scaler/pixconv.c b/Source/UnrealLibretro/Public/libretro/gfx/scaler/pixconv.c index d38d8d47..6571fec1 100644 --- a/Source/UnrealLibretro/Public/libretro/gfx/scaler/pixconv.c +++ b/Source/UnrealLibretro/Public/libretro/gfx/scaler/pixconv.c @@ -41,6 +41,8 @@ #include #elif defined(__MMX__) #include +#elif (defined(__ARM_NEON__) || defined(__ARM_NEON)) +#include #endif void conv_rgb565_0rgb1555(void *output_, const void *input_, @@ -220,6 +222,8 @@ void conv_rgb565_argb8888(void *output_, const void *input_, const __m64 a = _mm_set1_pi16(0x00ff); int max_width = width - 3; +#elif (defined(__ARM_NEON__) || defined(__ARM_NEON)) + int max_width = width - 7; #endif for (h = 0; h < height; @@ -282,6 +286,23 @@ void conv_rgb565_argb8888(void *output_, const void *input_, } _mm_empty(); +#elif (defined(__ARM_NEON__) || defined(__ARM_NEON)) + for (; w < max_width; w += 8) + { + uint16x8_t in = vld1q_u16(input + w); + + uint16x8_t r = vsriq_n_u16(in, in, 5); + uint16x8_t b = vsliq_n_u16(in, in, 5); + uint16x8_t g = vsriq_n_u16(b, b, 6); + + uint8x8x4_t res; + res.val[3] = vdup_n_u8(0xffu); + res.val[2] = vshrn_n_u16(r, 8); + res.val[1] = vshrn_n_u16(g, 8); + res.val[0] = vshrn_n_u16(b, 2); + + vst4_u8((uint8_t*)(output + w), res); + } #endif for (; w < width; w++) @@ -314,7 +335,10 @@ void conv_rgb565_abgr8888(void *output_, const void *input_, const __m128i mul16_g = _mm_set1_epi16(0x2080); const __m128i mul16_b = _mm_set1_epi16(0x4200); const __m128i a = _mm_set1_epi16(0x00ff); - int max_width = width - 7; + + int max_width = width - 7; +#elif (defined(__ARM_NEON__) || defined(__ARM_NEON)) + int max_width = width - 7; #endif for (h = 0; h < height; h++, output += out_stride >> 2, input += in_stride >> 1) @@ -343,6 +367,23 @@ void conv_rgb565_abgr8888(void *output_, const void *input_, _mm_storeu_si128((__m128i*)(output + w + 0), res_lo); _mm_storeu_si128((__m128i*)(output + w + 4), res_hi); } +#elif (defined(__ARM_NEON__) || defined(__ARM_NEON)) + for (; w < max_width; w += 8) + { + uint16x8_t in = vld1q_u16(input + w); + + uint16x8_t r = vsriq_n_u16(in, in, 5); + uint16x8_t b = vsliq_n_u16(in, in, 5); + uint16x8_t g = vsriq_n_u16(b, b, 6); + + uint8x8x4_t res; + res.val[3] = vdup_n_u8(0xffu); + res.val[2] = vshrn_n_u16(b, 2); + res.val[1] = vshrn_n_u16(g, 8); + res.val[0] = vshrn_n_u16(r, 8); + + vst4_u8((uint8_t*)(output + w), res); + } #endif for (; w < width; w++) { @@ -868,9 +909,26 @@ void conv_argb8888_abgr8888(void *output_, const void *input_, const uint32_t *input = (const uint32_t*)input_; uint32_t *output = (uint32_t*)output_; - for (h = 0; h < height; - h++, output += out_stride >> 2, input += in_stride >> 2) - { +#if (defined(__ARM_NEON__) || defined(__ARM_NEON)) + int max_width = width - 15; +#endif + + for (h = 0; h < height; + h++, output += out_stride >> 2, input += in_stride >> 2) + { + w = 0; +#if (defined(__ARM_NEON__) || defined(__ARM_NEON)) + for (w = 0; w < max_width; w += 16) + { + uint8x16x4_t in = vld4q_u8((unsigned char*)(input + w)); + uint8x16x4_t res; + res.val[3] = in.val[3]; + res.val[2] = in.val[0]; + res.val[1] = in.val[1]; + res.val[0] = in.val[2]; + vst4q_u8((uint8_t*)(output + w), res); + } +#endif for (w = 0; w < width; w++) { uint32_t col = input[w]; From 9f76692c408e7d6c5fc5f82eed880dac94a15889 Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Mon, 22 May 2023 22:26:44 -0700 Subject: [PATCH 25/79] Refactor to make struct reusable --- .../Public/LibretroCoreInstance.h | 16 +++ .../Private/LibretroCoreInstanceDetails.cpp | 109 +++++++++--------- .../Private/LibretroCoreInstanceDetails.h | 15 --- 3 files changed, 68 insertions(+), 72 deletions(-) diff --git a/Source/UnrealLibretro/Public/LibretroCoreInstance.h b/Source/UnrealLibretro/Public/LibretroCoreInstance.h index eedc8ad9..9cad8eeb 100644 --- a/Source/UnrealLibretro/Public/LibretroCoreInstance.h +++ b/Source/UnrealLibretro/Public/LibretroCoreInstance.h @@ -7,6 +7,22 @@ #include "Engine/TextureRenderTarget2D.h" #include "LibretroCoreInstance.generated.h" +USTRUCT(BlueprintType) +struct FLibretroOptions +{ + GENERATED_BODY() + + static CONSTEXPR int DefaultOptionIndex = 0; // By libretro convention + + UPROPERTY(BlueprintReadOnly) + FString Key; + + UPROPERTY(BlueprintReadOnly) + FString Description; + + UPROPERTY(BlueprintReadOnly) + TArray Values; +}; USTRUCT(BlueprintType) struct FLibretroControllerDescription // retro_controller_description diff --git a/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.cpp b/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.cpp index c27ad624..ea480843 100644 --- a/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.cpp +++ b/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.cpp @@ -32,59 +32,6 @@ TSharedRef FLibretroCoreInstanceDetails::MakeInstance() return MakeShareable(new FLibretroCoreInstanceDetails); } -void FLibretroOptions::OnOptionSelected(int32 Index) -{ - if (Parent->LibretroCoreInstance.IsValid()) - { - // If default is selected remove the key from core options since no key is implicit default - if (Index == 0) - { - if (Parent->LibretroCoreInstance->CoreOptions.Contains(Key)) - { - Parent->LibretroCoreInstance->CoreOptions.Remove(Key); - Parent->MarkEditorNeedsSave(); - } - } - // Otherwise add the selected option - else - { - Parent->LibretroCoreInstance->CoreOptions.Add(Key) = Values[Index]; - Parent->MarkEditorNeedsSave(); - } - } -} - -FText FLibretroOptions::GetSelectedOptionName() const -{ - if (Parent->LibretroCoreInstance.IsValid()) - { - FString *SelectedOptionString = Parent->LibretroCoreInstance->CoreOptions.Find(Key); - if (SelectedOptionString == nullptr) - { - return FText::FromString(Values[0]); - } - else - { - return FText::FromString(*SelectedOptionString); - } - } - - return FText::FromString(TEXT("Error")); -} - -TSharedRef FLibretroOptions::OnGenerateDropdownMenu() -{ - FMenuBuilder MenuBuilder(true, nullptr); - - for (int32 i = 0; i < Values.Num(); i++) - { - FUIAction ItemAction(FExecuteAction::CreateRaw(this, &FLibretroOptions::OnOptionSelected, i)); - MenuBuilder.AddMenuEntry(FText::FromString(Values[i]), TAttribute(), FSlateIcon(), ItemAction); - } - - return MenuBuilder.MakeWidget(); -} - void FLibretroCoreInstanceDetails::RefreshCoreListViews() { const FText& InSearchText = SearchBox->GetText(); @@ -453,7 +400,6 @@ void FLibretroCoreInstanceDetails::CustomizeDetails(IDetailLayoutBuilder& Detail IDetailCategoryBuilder& OptionsCategory = DetailBuilder.EditCategory("Libretro Core Options"); for (int i = 0; i < n; i++) { - static_LibretroOptions[i].Parent = this; LibretroOptions.Add(static_LibretroOptions[i]); OptionsCategory.AddCustomRow(FText::GetEmpty()) @@ -479,18 +425,67 @@ void FLibretroCoreInstanceDetails::CustomizeDetails(IDetailLayoutBuilder& Detail .MinDesiredWidth(150.0f) [ SNew(STextBlock) - .Text_Raw(&LibretroOptions[i], &FLibretroOptions::GetOptionDescription) + .Text_Lambda([&Option = LibretroOptions[i]](){ return FText::FromString(Option.Description); }) .Font(IDetailLayoutBuilder::GetDetailFont()) ] .ValueContent() [ SNew(SComboButton) - .OnGetMenuContent_Raw(&LibretroOptions[i], &FLibretroOptions::OnGenerateDropdownMenu) + // The following is fired when the user clicks an option to modify it this callback fills the drop down with options + .OnGetMenuContent_Lambda([&Option = LibretroOptions[i], this]() + { + FMenuBuilder MenuBuilder(true, nullptr); + + for (int32 i = 0; i < Option.Values.Num(); i++) + { + // This is fired when the user clicks one of the possible options from the drop down + FUIAction ItemAction(FExecuteAction::CreateLambda([&Option, i, this] + { + if (this->LibretroCoreInstance.IsValid()) + { + // If default is selected remove the key from core options since no key is implicit default + if (i == FLibretroOptions::DefaultOptionIndex) + { + if (this->LibretroCoreInstance->CoreOptions.Contains(Option.Key)) + { + this->LibretroCoreInstance->CoreOptions.Remove(Option.Key); + this->MarkEditorNeedsSave(); + } + } + // Otherwise add the selected option + else + { + this->LibretroCoreInstance->CoreOptions.Add(Option.Key) = Option.Values[i]; + this->MarkEditorNeedsSave(); + } + } + })); + MenuBuilder.AddMenuEntry(FText::FromString(Option.Values[i]), TAttribute(), FSlateIcon(), ItemAction); + } + + return MenuBuilder.MakeWidget(); + }) .ContentPadding(FMargin(2.0f, 2.0f)) .ButtonContent() [ SNew(STextBlock) - .Text_Raw(&LibretroOptions[i], &FLibretroOptions::GetSelectedOptionName) + .Text_Lambda([&Option = LibretroOptions[i], this]() + { + if (this->LibretroCoreInstance.IsValid()) + { + FString* SelectedOptionString = this->LibretroCoreInstance->CoreOptions.Find(Option.Key); + if (SelectedOptionString == nullptr) + { + return FText::FromString(Option.Values[FLibretroOptions::DefaultOptionIndex]); + } + else + { + return FText::FromString(*SelectedOptionString); + } + } + + return FText::FromString(TEXT("Error")); + }) .Font(IDetailLayoutBuilder::GetDetailFont()) ] ]; diff --git a/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.h b/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.h index fd414ab1..c2ea9d18 100644 --- a/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.h +++ b/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.h @@ -14,21 +14,6 @@ class IPropertyHandle; class SComboButton; -struct FLibretroOptions -{ - class FLibretroCoreInstanceDetails* Parent; - - FString Key; - FString Description; - TArray Values; - - FText GetSelectedOptionName() const; - FText GetOptionDescription() const { return FText::FromString(Description); } - void OnOptionSelected(int32 Index); - TSharedRef OnGenerateDropdownMenu(); -}; - - /** * @brief Editor custom GUI code for the LibretroCoreInstance type * From eed30c5c26bf8ccb3971aa218bf395e87157c635 Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Wed, 24 May 2023 19:26:09 -0700 Subject: [PATCH 26/79] Refactor and cleanup --- Source/UnrealLibretro/Private/sdlarch.cpp | 4 +- .../Public/LibretroCoreInstance.h | 2 +- Source/UnrealLibretro/Public/UnrealLibretro.h | 43 +++++++++++++++- .../Private/LibretroCoreInstanceDetails.cpp | 51 ++++--------------- .../Private/LibretroCoreInstanceDetails.h | 2 +- 5 files changed, 54 insertions(+), 48 deletions(-) diff --git a/Source/UnrealLibretro/Private/sdlarch.cpp b/Source/UnrealLibretro/Private/sdlarch.cpp index 9f4ac4c5..32d3c8c0 100644 --- a/Source/UnrealLibretro/Private/sdlarch.cpp +++ b/Source/UnrealLibretro/Private/sdlarch.cpp @@ -776,9 +776,7 @@ bool LibretroContext::core_environment(unsigned cmd, void *data) { return true; } case RETRO_ENVIRONMENT_SET_CONTROLLER_INFO: { - auto controller_info = (const struct retro_controller_info*)data; - - FUnrealLibretroModule::EnvironmentParseControllerInfo(controller_info, ControllerDescriptions); + ControllerDescriptions = FUnrealLibretroModule::EnvironmentParseControllerInfo((const struct retro_controller_info*)data); return true; } diff --git a/Source/UnrealLibretro/Public/LibretroCoreInstance.h b/Source/UnrealLibretro/Public/LibretroCoreInstance.h index 9cad8eeb..8625c7b9 100644 --- a/Source/UnrealLibretro/Public/LibretroCoreInstance.h +++ b/Source/UnrealLibretro/Public/LibretroCoreInstance.h @@ -8,7 +8,7 @@ #include "LibretroCoreInstance.generated.h" USTRUCT(BlueprintType) -struct FLibretroOptions +struct FLibretroOption { GENERATED_BODY() diff --git a/Source/UnrealLibretro/Public/UnrealLibretro.h b/Source/UnrealLibretro/Public/UnrealLibretro.h index b58ceb81..b317948b 100644 --- a/Source/UnrealLibretro/Public/UnrealLibretro.h +++ b/Source/UnrealLibretro/Public/UnrealLibretro.h @@ -113,9 +113,10 @@ class FUnrealLibretroModule : public IModuleInterface return IfRelativeResolvePathRelativeToThisPluginWithPathExtensions(UnresolvedSavePath, TEXT("Saves"), TEXT("SRAM"), FPaths::GetCleanFilename(UnresolvedRomPath)); } - static void EnvironmentParseControllerInfo(const retro_controller_info* controller_info, - TStaticArray, PortCount>& ControllerDescriptions) + static TStaticArray, PortCount> EnvironmentParseControllerInfo(const retro_controller_info* controller_info) { + TStaticArray, PortCount> ControllerDescriptions; + for (int port = 0; controller_info[port].types != NULL && port < PortCount; port++) { for (unsigned t = 0; t < controller_info[port].num_types; t++) { if (controller_info[port].types[t].desc == nullptr) break; // Not part of Libretro API but needed check for some cores @@ -125,6 +126,44 @@ class FUnrealLibretroModule : public IModuleInterface controller_description.id }); } } + + return ControllerDescriptions; + } + + static TArray EnvironmentParseOptions(const struct retro_variable* variable_array) + { + TArray ParsedOptions; + + while (variable_array->key != nullptr) + { + // Example entry: + // { .key = "foo_option", .value = "Speed hack coprocessor X; false|true" } + const FString Value = variable_array->value; + + // Find the position of the semicolon that separates the description and values + int32 SemicolonIndex; + if (!Value.FindChar(';', SemicolonIndex)) + { + UE_LOG(Libretro, Warning, TEXT("Failed to parse libretro core option '%s'. No semicolon"), *Value); + continue; // Skip this Option + } + + FLibretroOption Option; + + // Extract the description substring + Option.Description = Value.Left(SemicolonIndex); + + // Extract the values substring and split it into individual values + FString PipeDelimitedValueString = Value.Mid(SemicolonIndex + 1).TrimStart(); + + PipeDelimitedValueString.ParseIntoArray(Option.Values, TEXT("|"), false); + Option.Key = variable_array->key; + + ParsedOptions.Add(Option); + variable_array++; + } + + return ParsedOptions; } private: diff --git a/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.cpp b/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.cpp index ea480843..db97bfa5 100644 --- a/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.cpp +++ b/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.cpp @@ -129,8 +129,7 @@ void FLibretroCoreInstanceDetails::StartBuildbotBatchDownload(TSharedPtr RefreshCoreListViews(); } -static int32 n = 0; -static FLibretroOptions static_LibretroOptions[100]; +static TArray static_LibretroOptions; static TStaticArray, PortCount> static_ControllerDescriptions; @@ -138,39 +137,12 @@ static bool core_environment(unsigned cmd, void* data) { switch (cmd) { case RETRO_ENVIRONMENT_SET_VARIABLES: { - const struct retro_variable* arr_var = (const struct retro_variable*)data; - - for (n = 0; n < sizeof(static_LibretroOptions) / sizeof(static_LibretroOptions[0]); arr_var++) { - if (arr_var->key == nullptr) break; - - // Example entry: - // { .key = "foo_option", .value = "Speed hack coprocessor X; false|true" } - const FString Value = arr_var->value; - - // Find the position of the semicolon that separates the description and values - int32 SemicolonIndex; - if (!Value.FindChar(';', SemicolonIndex)) { - UE_LOG(Libretro, Warning, TEXT("Failed to parse libretro core option '%s'. No semicolon"), *Value); - continue; // Skip this Option - } - - // Extract the description substring - static_LibretroOptions[n].Description = Value.Left(SemicolonIndex); - - // Extract the values substring and split it into individual values - FString PipeDelimitedValueString = Value.Mid(SemicolonIndex + 1).TrimStart(); - - PipeDelimitedValueString.ParseIntoArray(static_LibretroOptions[n].Values, TEXT("|"), false); - static_LibretroOptions[n].Key = arr_var->key; - n++; - } + static_LibretroOptions = FUnrealLibretroModule::EnvironmentParseOptions((const struct retro_variable*)data); return true; } case RETRO_ENVIRONMENT_SET_CONTROLLER_INFO: { - auto controller_info = (const struct retro_controller_info*)data; - - FUnrealLibretroModule::EnvironmentParseControllerInfo(controller_info, static_ControllerDescriptions); + static_ControllerDescriptions = FUnrealLibretroModule::EnvironmentParseControllerInfo((const struct retro_controller_info*)data); return true; } @@ -192,8 +164,6 @@ void FLibretroCoreInstanceDetails::CustomizeDetails(IDetailLayoutBuilder& Detail LibretroCoreInstance = CastChecked(ObjectsCustomized[0].Get()); - static_ControllerDescriptions = TStaticArray, PortCount>{}; - n = 0; // To make sure we don't get stale options from the last selected core FString RomPathValidExtensionsDelimited; // Contains a string like "bin|rom|iso" if (LibretroCoreInstance.IsValid()) { @@ -398,10 +368,9 @@ void FLibretroCoreInstanceDetails::CustomizeDetails(IDetailLayoutBuilder& Detail // Construct Libreto Core Options Panel... It's directly under the Libretro section IDetailCategoryBuilder& OptionsCategory = DetailBuilder.EditCategory("Libretro Core Options"); - for (int i = 0; i < n; i++) + LibretroOptions = MoveTemp(static_LibretroOptions); + for (const FLibretroOption& Option : LibretroOptions) { - LibretroOptions.Add(static_LibretroOptions[i]); - OptionsCategory.AddCustomRow(FText::GetEmpty()) #if ENGINE_MAJOR_VERSION >= 5 .OverrideResetToDefault(FResetToDefaultOverride::Create @@ -425,14 +394,14 @@ void FLibretroCoreInstanceDetails::CustomizeDetails(IDetailLayoutBuilder& Detail .MinDesiredWidth(150.0f) [ SNew(STextBlock) - .Text_Lambda([&Option = LibretroOptions[i]](){ return FText::FromString(Option.Description); }) + .Text_Lambda([&Option](){ return FText::FromString(Option.Description); }) .Font(IDetailLayoutBuilder::GetDetailFont()) ] .ValueContent() [ SNew(SComboButton) // The following is fired when the user clicks an option to modify it this callback fills the drop down with options - .OnGetMenuContent_Lambda([&Option = LibretroOptions[i], this]() + .OnGetMenuContent_Lambda([&Option, this]() { FMenuBuilder MenuBuilder(true, nullptr); @@ -444,7 +413,7 @@ void FLibretroCoreInstanceDetails::CustomizeDetails(IDetailLayoutBuilder& Detail if (this->LibretroCoreInstance.IsValid()) { // If default is selected remove the key from core options since no key is implicit default - if (i == FLibretroOptions::DefaultOptionIndex) + if (i == FLibretroOption::DefaultOptionIndex) { if (this->LibretroCoreInstance->CoreOptions.Contains(Option.Key)) { @@ -469,14 +438,14 @@ void FLibretroCoreInstanceDetails::CustomizeDetails(IDetailLayoutBuilder& Detail .ButtonContent() [ SNew(STextBlock) - .Text_Lambda([&Option = LibretroOptions[i], this]() + .Text_Lambda([&Option, this]() { if (this->LibretroCoreInstance.IsValid()) { FString* SelectedOptionString = this->LibretroCoreInstance->CoreOptions.Find(Option.Key); if (SelectedOptionString == nullptr) { - return FText::FromString(Option.Values[FLibretroOptions::DefaultOptionIndex]); + return FText::FromString(Option.Values[FLibretroOption::DefaultOptionIndex]); } else { diff --git a/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.h b/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.h index c2ea9d18..720c295e 100644 --- a/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.h +++ b/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.h @@ -51,7 +51,7 @@ class FLibretroCoreInstanceDetails : public IDetailCustomization /** IDetailCustomization interface */ virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override; - TArray> LibretroOptions; + TArray LibretroOptions; TStaticArray, PortCount> ControllerDescriptions; TWeakObjectPtr LibretroCoreInstance; From 63ca3398d09a5a455c201b9664eac16b96a90625 Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Mon, 29 May 2023 14:11:15 -0700 Subject: [PATCH 27/79] Reimplement options code to be more threadsafe Modifying options is lockfree (important for gamethread) Also I made certain data races impossible --- Config/DefaultUnrealLibretro.ini | 3 + .../Private/LibretroCoreInstance.cpp | 62 +++++++++- .../UnrealLibretro/Private/LibretroSettings.h | 8 +- Source/UnrealLibretro/Private/sdlarch.cpp | 113 ++++++++++-------- Source/UnrealLibretro/Private/sdlarch.h | 20 ++-- .../Public/LibretroCoreInstance.h | 81 +++++++------ .../Public/LibretroInputDefinitions.h | 31 +++++ .../Private/LibretroCoreInstanceDetails.cpp | 60 +++++----- .../Private/LibretroCoreInstanceDetails.h | 6 +- 9 files changed, 248 insertions(+), 136 deletions(-) diff --git a/Config/DefaultUnrealLibretro.ini b/Config/DefaultUnrealLibretro.ini index e1335168..14a434c0 100644 --- a/Config/DefaultUnrealLibretro.ini +++ b/Config/DefaultUnrealLibretro.ini @@ -2,6 +2,9 @@ CoreSaveDirectory=Saves/Core/ CoreSystemDirectory=System/ +;Global options for all cores can be set here or in the editor +;GlobalCoreOptions=(("mame_lightgun_mode", "touchscreen"),("nestopia_zapper_device", "pointer")) + ;[/Script/Engine.PhysicsSettings] ;; Arcade guns won't work without this I have a text warning over the arcade gun actor for this, but I'm leaving this here as a reminder ;bSupportUVFromHitResults=True \ No newline at end of file diff --git a/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp b/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp index 1216c862..dff1f083 100644 --- a/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp +++ b/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp @@ -19,15 +19,32 @@ ULibretroCoreInstance::ULibretroCoreInstance() PrimaryComponentTick.bCanEverTick = true; } +//bool ULibretroCoreInstance::IsReadyForFinishDestroy() { return true; }; + +void ULibretroCoreInstance::SetController(int Port, int64 ID) +{ + NOT_LAUNCHED_GUARD + + // This if statement guards against a datarace on LibretroContext::DeviceIDs + if (CoreInstance.GetValue()->CoreState.load(std::memory_order_acquire) != LibretroContext::ECoreState::Starting) + { + CoreInstance.GetValue()->DeviceIDs[Port] = ID; + CoreInstance.GetValue()->EnqueueTask([Port, ID](libretro_api_t &libretro_api) + { + libretro_api.set_controller_port_device(Port, ID); + }); + } +} + void ULibretroCoreInstance::GetController(int Port, int64& ID, FString& Description) { NOT_LAUNCHED_GUARD - // This if statement guards against a datarace on LibretroContext::ControllerDescriptions + // This if statement guards against a datarace on LibretroContext::DeviceIDs if (CoreInstance.GetValue()->CoreState.load(std::memory_order_acquire) != LibretroContext::ECoreState::Starting) { - ID = CoreInstance.GetValue()->devices[Port].load(std::memory_order_relaxed); - for (auto & ControllerDescription : CoreInstance.GetValue()->ControllerDescriptions[Port]) + ID = CoreInstance.GetValue()->DeviceIDs[Port]; + for (FLibretroControllerDescription& ControllerDescription : CoreInstance.GetValue()->ControllerDescriptions[Port]) { if (ControllerDescription.ID == ID) { @@ -38,6 +55,45 @@ void ULibretroCoreInstance::GetController(int Port, int64& ID, FString& Descript } } +TArray ULibretroCoreInstance::GetOptionDescriptions() +{ + return CoreInstance.IsSet() ? CoreInstance.GetValue()->OptionDescriptions : TArray{}; +} + +TArray ULibretroCoreInstance::GetControllerDescriptions(int Port) +{ + return CoreInstance.IsSet() ? CoreInstance.GetValue()->ControllerDescriptions[Port] : TArray{}; +} + +void ULibretroCoreInstance::GetOption(const FString& Key, FString& Value, int& Index) +{ + NOT_LAUNCHED_GUARD + + for (int i = 0; i < CoreInstance.GetValue()->OptionDescriptions.Num(); i++) + { + if (CoreInstance.GetValue()->OptionDescriptions[i].Key == Key) + { + Index = CoreInstance.GetValue()->OptionSelectedIndex[i].load(std::memory_order_relaxed); + Value = CoreInstance.GetValue()->OptionDescriptions[i].Values[Index]; + } + } +} + +void ULibretroCoreInstance::SetOption(const FString& Key, const FString& Value) +{ + NOT_LAUNCHED_GUARD + + for (int i = 0; i < CoreInstance.GetValue()->OptionDescriptions.Num(); i++) + { + if (CoreInstance.GetValue()->OptionDescriptions[i].Key == Key) + { + int32 Index = CoreInstance.GetValue()->OptionDescriptions[i].Values.IndexOfByKey(Value); + CoreInstance.GetValue()->OptionSelectedIndex[i].store(Index, std::memory_order_relaxed); + CoreInstance.GetValue()->OptionsHaveBeenModified.store(true, std::memory_order_release); + } + } +} + void ULibretroCoreInstance::Launch() { Shutdown(); diff --git a/Source/UnrealLibretro/Private/LibretroSettings.h b/Source/UnrealLibretro/Private/LibretroSettings.h index f33ca039..df5d3bc4 100644 --- a/Source/UnrealLibretro/Private/LibretroSettings.h +++ b/Source/UnrealLibretro/Private/LibretroSettings.h @@ -9,14 +9,18 @@ class UNREALLIBRETRO_API ULibretroSettings : public UDeveloperSettings GENERATED_BODY() public: - /** Path to the 'system' directory passed to Libretro Cores */ + /** Path to the 'save' directory passed to Libretro Cores */ UPROPERTY(Config, EditAnywhere, Category = Libretro) FString CoreSaveDirectory; - /** Path to the 'save' directory passed to Libretro Cores */ + /** Path to the 'system' directory passed to Libretro Cores */ UPROPERTY(Config, EditAnywhere, Category = Libretro) FString CoreSystemDirectory; + /** Core options that are loaded for every core. They will be overwritten by options set for a specific ULibretroCoreInstance */ + UPROPERTY(config, EditAnywhere, Category = Libretro) + TMap GlobalCoreOptions; + FName GetCategoryName() const override { return TEXT("Plugins"); diff --git a/Source/UnrealLibretro/Private/sdlarch.cpp b/Source/UnrealLibretro/Private/sdlarch.cpp index 32d3c8c0..252341ca 100644 --- a/Source/UnrealLibretro/Private/sdlarch.cpp +++ b/Source/UnrealLibretro/Private/sdlarch.cpp @@ -624,41 +624,65 @@ bool LibretroContext::core_environment(unsigned cmd, void *data) { case RETRO_ENVIRONMENT_GET_VARIABLE: { retro_variable* var = (struct retro_variable*)data; - std::string key(var->key); + int i = 0; + for (FString TargetKey = FString(var->key); i < OptionDescriptions.Num(); i++) { + if (OptionDescriptions[i].Key == TargetKey) break; + } - if (core.settings.find(key) != core.settings.end()) { - var->value = core.settings.at(key).c_str(); - return true; + if (i == OptionDescriptions.Num()) { + UE_LOG(Libretro, Warning, TEXT ("Core '%s' violated libretro spec asked for unkown option '%s'"), UTF8_TO_TCHAR(system.library_name), UTF8_TO_TCHAR(var->key)); + return false; } - return false; + FString TargetValue = OptionDescriptions[i].Values[OptionSelectedIndex[i].load(std::memory_order_relaxed)]; + + int32 Utf8Length = FTCHARToUTF8_Convert::ConvertedLength(*TargetValue, TargetValue.Len()); + TArray& TargetValueCString = OptionsCache.FindOrAdd(var->key); + TargetValueCString.SetNumZeroed(Utf8Length + 1, false); + + FTCHARToUTF8_Convert::Convert(TargetValueCString.GetData(), Utf8Length, *TargetValue, TargetValue.Len()); + var->value = TargetValueCString.GetData(); + + return true; } case RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE: { - const struct retro_variable* var = (const struct retro_variable*)data; - //checkNoEntry(); + bool* core_should_query_for_options = (bool*)data; + + // atomic<>::exchange is necessary otherwise we could miss newly set options because of a race condition + *core_should_query_for_options = OptionsHaveBeenModified.exchange(false, std::memory_order_acquire); + return false; } case RETRO_ENVIRONMENT_SET_VARIABLES: { - const struct retro_variable* arr_var = (const struct retro_variable*)data; - - do { - // Initialize key - const std::string key(arr_var->key); - - // Don't override custom options already set - if (core.settings.count(key) == 0) { - // Parse and initialize default setting, First delimited setting is default by Libretro convention - auto advance_past_space = [](const char* x) { while (*x == ' ') { x++; } return x; }; - auto past_comment = advance_past_space(strchr(arr_var->value, ';') + 1); - const char *delimiter_ptr = strchr(arr_var->value, '|'); - if (delimiter_ptr == nullptr) delimiter_ptr = strchr(arr_var->value, '\0'); - std::string default_setting(past_comment, delimiter_ptr - past_comment); - - // Write setting to table - core.settings[key] = default_setting; - } + // Queuing this and synchronizing on the game thread prevents a data race + FTaskGraphInterface::Get().WaitUntilTaskCompletes(FFunctionGraphTask::CreateAndDispatchWhenReady([this, data]() + { + OptionDescriptions = FUnrealLibretroModule::EnvironmentParseOptions((const struct retro_variable*)data); + + if (OptionSelectedIndex.Num() > 0 && OptionSelectedIndex.Num() != OptionDescriptions.Num()) { + UE_LOG(Libretro, Warning, TEXT("Core violated libretro spec size of Options changed")); + } + + // By libretro spec the 0 index setting is the default one + OptionSelectedIndex.SetNumZeroed(OptionDescriptions.Num(), false); + }, + TStatId(), nullptr, ENamedThreads::GameThread)); + + for (int i = 0; i < OptionDescriptions.Num(); i++) + { + if (FString* TargetValue = StartingOptions.Find(OptionDescriptions[i].Key)) + { + auto TargetIndex = OptionDescriptions[i].Values.IndexOfByKey(*TargetValue); - } while ((++arr_var)->key); + if (TargetIndex == INDEX_NONE) + { + UE_LOG(Libretro, Warning, TEXT("Value '%s' does not exist for option '%s'"), **TargetValue, *OptionDescriptions[i].Key); + continue; + } + + OptionSelectedIndex[i].store(TargetIndex, std::memory_order_relaxed); + } + } return true; } @@ -776,7 +800,9 @@ bool LibretroContext::core_environment(unsigned cmd, void *data) { return true; } case RETRO_ENVIRONMENT_SET_CONTROLLER_INFO: { - ControllerDescriptions = FUnrealLibretroModule::EnvironmentParseControllerInfo((const struct retro_controller_info*)data); + FTaskGraphInterface::Get().WaitUntilTaskCompletes(FFunctionGraphTask::CreateAndDispatchWhenReady([this, data]() + { ControllerDescriptions = FUnrealLibretroModule::EnvironmentParseControllerInfo((const struct retro_controller_info*)data); }, + TStatId(), nullptr, ENamedThreads::GameThread)); return true; } @@ -847,11 +873,6 @@ void LibretroContext::core_audio_sample(int16_t left, int16_t right) { core_audio_write(buf, (size_t)1); } -void LibretroContext::set_controller_port_device(unsigned port, unsigned device) { - devices[port] = device; // The core doesn't keep track of this so we have to - _internal_set_controller_port_device(port, device); -} - void LibretroContext::load(const char *sofile) { void (*set_environment)(retro_environment_t) = NULL; void (*set_video_refresh)(retro_video_refresh_t) = NULL; @@ -870,6 +891,7 @@ void LibretroContext::load(const char *sofile) { load_retro_sym(api_version); load_retro_sym(get_system_info); load_retro_sym(get_system_av_info); + load_retro_sym(set_controller_port_device); load_retro_sym(reset); load_retro_sym(run); load_retro_sym(load_game); @@ -880,8 +902,6 @@ void LibretroContext::load(const char *sofile) { load_retro_sym(serialize); load_retro_sym(unserialize); - load_sym(_internal_set_controller_port_device, retro_set_controller_port_device); - load_sym(set_environment, retro_set_environment); load_sym(set_video_refresh, retro_set_video_refresh); load_sym(set_input_poll, retro_set_input_poll); @@ -953,11 +973,6 @@ LibretroContext* LibretroContext::Launch(ULibretroCoreInstance* LibretroCoreInst LibretroContext *l = new LibretroContext(); - for (auto& Option : LibretroCoreInstance->CoreOptions) - { - l->core.settings[TCHAR_TO_UTF8(*Option.Key)] = TCHAR_TO_UTF8(*Option.Value); - } - // Grab a statically generated callback structure int32 InstanceNumber; { @@ -980,6 +995,9 @@ LibretroContext* LibretroContext::Launch(ULibretroCoreInstance* LibretroCoreInst ConvertPath(l->core.save_directory, LibretroSettings->CoreSaveDirectory); ConvertPath(l->core.system_directory, LibretroSettings->CoreSystemDirectory); + + l->StartingOptions = LibretroSettings->GlobalCoreOptions; + l->StartingOptions.Append(LibretroCoreInstance->EditorPresetOptions); // Potentially overrides global options l->UnrealRenderTarget = MakeWeakObjectPtr(RenderTarget); l->UnrealSoundBuffer = MakeWeakObjectPtr(SoundBuffer ); @@ -987,7 +1005,7 @@ LibretroContext* LibretroContext::Launch(ULibretroCoreInstance* LibretroCoreInst // Kick the initialization process off to another thread. It shouldn't be added to the Unreal task pool because those are too slow and my code relies on OpenGL state being thread local. // The Runnable system is the standard way for spawning and managing threads in Unreal. FThread looks enticing, but they removed any way to detach threads since "it doesn't work as expected" l->LambdaRunnable = FLambdaRunnable::RunLambdaOnBackGroundThread(FPaths::GetCleanFilename(core) + FPaths::GetCleanFilename(game), - [=, LoadedCallback = MoveTemp(LoadedCallback), ControllersSetOnLaunch = LibretroCoreInstance->ControllersSetOnLaunch]() { + [=, LoadedCallback = MoveTemp(LoadedCallback), EditorPresetControllers = LibretroCoreInstance->EditorPresetControllers]() { // Here I load a copy of the dll instead of the original. If you load the same dll multiple times you won't obtain a new instance of the dll loaded into memory, // instead all variables and function pointers will point to the original loaded dll @@ -1017,17 +1035,14 @@ LibretroContext* LibretroContext::Launch(ULibretroCoreInstance* LibretroCoreInst l->libretro_api.get_system_info(&l->system); for (int Port = 0; Port < PortCount; Port++) { - auto* ControllerDescription = ControllersSetOnLaunch.Find(l->system.library_name); - if (ControllerDescription) + unsigned DeviceID = RETRO_DEVICE_DEFAULT; + if (const FLibretroControllerDescriptions* CorePresetControllers = EditorPresetControllers.Find(l->system.library_name)) { - l->set_controller_port_device(Port, (*ControllerDescription)[Port].ID); - } - else - { - // This is needed for some cores nestopia specifically is one example - // otherwise nothing will be bound for some reason - l->set_controller_port_device(Port, RETRO_DEVICE_DEFAULT); + DeviceID = (*CorePresetControllers)[Port].ID; } + + l->DeviceIDs[Port] = DeviceID; + l->libretro_api.set_controller_port_device(Port, DeviceID); } // This does load the game but does many other things as well. If hardware rendering is needed it loads OpenGL resources from the OS and this also initializes the unreal engine resources for audio and video. diff --git a/Source/UnrealLibretro/Private/sdlarch.h b/Source/UnrealLibretro/Private/sdlarch.h index c90c720b..b57760ad 100644 --- a/Source/UnrealLibretro/Private/sdlarch.h +++ b/Source/UnrealLibretro/Private/sdlarch.h @@ -7,8 +7,6 @@ static_assert(RETRO_API_VERSION == 1, "Retro API version changed"); // Standard Template Library https://docs.unrealengine.com/en-US/Programming/Development/CodingStandard/#useofstandardlibraries #include -#include -#include // Unreal imports #include "CoreMinimal.h" @@ -82,6 +80,7 @@ struct libretro_api_t { unsigned (*api_version)(void); void (*get_system_info)(struct retro_system_info* info); void (*get_system_av_info)(struct retro_system_av_info* info); + void (*set_controller_port_device)(unsigned port, unsigned device); void (*reset)(void); void (*run)(void); size_t (*serialize_size)(void); @@ -127,14 +126,17 @@ struct LibretroContext { */ FLibretroInputState InputState[PortCount]; - std::atomic devices[PortCount]; - // Only safe to access from any thread once CoreState is not Starting + std::atomic OptionsHaveBeenModified; + TArray> OptionSelectedIndex; + TMap StartingOptions; + + // The following are always safe to access from the game thread and are guranteed to be initialized after exiting the starting state + TArray OptionDescriptions; TStaticArray, PortCount> ControllerDescriptions; + decltype(retro_controller_description::id) DeviceIDs[PortCount]; // The core doesn't keep track of this so we have to struct retro_system_info system = { 0 }; - void set_controller_port_device(unsigned port, unsigned device); - TUniqueFunction::Type> CoreEnvironmentCallback; /** @@ -165,7 +167,6 @@ struct LibretroContext { LibretroContext() {} ~LibretroContext() {} - void (*_internal_set_controller_port_device)(unsigned port, unsigned device); libretro_api_t libretro_api = { 0 }; struct libretro_callbacks_t* libretro_callbacks = nullptr; TQueue, EQueueMode::Spsc> LibretroAPITasks; // TQueue has acquire-release semantics on Enqueue and Dequeue so this should be thread-safe @@ -174,6 +175,10 @@ struct LibretroContext { TWeakObjectPtr UnrealRenderTarget{nullptr}; TWeakObjectPtr UnrealSoundBuffer{nullptr}; + TMap> OptionsCache; // This isn't technically needed in theory a core should immediately convert an option into it's internal representation and cease referencing + // the string before control flow is passed back to you, but the lifetime of a string passed to a core when it uses RETRO_ENVIRONMENT_GET_VARIABLE + // isn't well defined in the libretro spec, so some cores like mame will misbehave if you free a string before it's done with it + struct { // These are all ThreadSafe shared pointers that are the main bridge between and unreal @@ -226,7 +231,6 @@ struct LibretroContext { TArray> save_directory, system_directory; - std::unordered_map settings; } core = { 0 }; public: diff --git a/Source/UnrealLibretro/Public/LibretroCoreInstance.h b/Source/UnrealLibretro/Public/LibretroCoreInstance.h index 8625c7b9..3f32cc2a 100644 --- a/Source/UnrealLibretro/Public/LibretroCoreInstance.h +++ b/Source/UnrealLibretro/Public/LibretroCoreInstance.h @@ -7,35 +7,6 @@ #include "Engine/TextureRenderTarget2D.h" #include "LibretroCoreInstance.generated.h" -USTRUCT(BlueprintType) -struct FLibretroOption -{ - GENERATED_BODY() - - static CONSTEXPR int DefaultOptionIndex = 0; // By libretro convention - - UPROPERTY(BlueprintReadOnly) - FString Key; - - UPROPERTY(BlueprintReadOnly) - FString Description; - - UPROPERTY(BlueprintReadOnly) - TArray Values; -}; - -USTRUCT(BlueprintType) -struct FLibretroControllerDescription // retro_controller_description -{ - GENERATED_BODY() - - UPROPERTY(VisibleAnywhere) - FString Description{"Unspecified"}; - - UPROPERTY(VisibleAnywhere) - unsigned int ID{RETRO_DEVICE_DEFAULT}; -}; - USTRUCT(BlueprintType) struct FLibretroControllerDescriptions { @@ -127,19 +98,43 @@ class UNREALLIBRETRO_API ULibretroCoreInstance : public UActorComponent void Pause(bool ShouldPause = true); /** - * @brief Effectively a getter version of `retro_set_controller_port_device` + * The following methods help with setting bound controllers for the core at runtime + * These are not preserved when the core is restarted * - * @see `retro_set_controller_port_device` in libretro.h + * @see `RETRO_ENVIRONMENT_SET_CONTROLLER_INFO` + * @see retro_set_controller_port_device + */ + UFUNCTION(BlueprintPure, Category = "Libretro|IneffectiveBeforeLaunchComplete") + TArray GetControllerDescriptions(int Port); + + UFUNCTION(BlueprintCallable, Category = "Libretro|IneffectiveBeforeLaunchComplete") + void GetController(int Port, int64& ID, FString& Description); + + UFUNCTION(BlueprintCallable, Category = "Libretro|IneffectiveBeforeLaunchComplete") + void SetController(int Port, int64 ID); + + /** + * The following methods help with setting options for the core at runtime + * These are not preserved when the core is restarted + * + * @see `RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE` + * @see `RETRO_ENVIRONMENT_GET_VARIABLE` + * @see `RETRO_ENVIRONMENT_SET_VARIABLES` */ - UFUNCTION(BlueprintCallable, Category = "Libretro|IneffectiveBeforeCoreIsReady") - void GetController(int Port, int64 &ID, FString &Description); + UFUNCTION(BlueprintPure, Category = "Libretro|IneffectiveBeforeLaunchComplete") + TArray GetOptionDescriptions(); + + UFUNCTION(BlueprintCallable, Category = "Libretro|IneffectiveBeforeLaunchComplete") + void GetOption(const FString& Key, FString& Value, int &Index); + + UFUNCTION(BlueprintCallable, Category = "Libretro|IneffectiveBeforeLaunchComplete") + void SetOption(const FString& Key, const FString& Value); /** * @brief Sets the state of a button for the Libretro core * * Bindings in ConnectController will override inputs set here * You can set analog inputs through this interface as well however it won't work well - * */ UFUNCTION(BlueprintCallable, Category = "Libretro|IneffectiveBeforeLaunch") void SetInputDigital(int Port, bool Activated, ERetroDeviceID Input); @@ -170,19 +165,23 @@ class UNREALLIBRETRO_API ULibretroCoreInstance : public UActorComponent UAudioComponent* AudioComponent; /** - * @brief A key-value map for configuring Libretro Cores + * @brief A key-value map that stores the options set for Libretro Cores from the editor * * Options are appended to this list when you change them in the 'Libretro Core Options' section of the details panel - * They are only added here if they differ from their default + * They are only added here if they differ from their default. These Options override global options set from an ini */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Libretro) - TMap CoreOptions; + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Libretro) + TMap EditorPresetOptions; /** - * Making @todo probably don't make this part of the public interface + * @brief A key-value map for storing controllers that will be bound on launch per libretro core + * + * @todo The keys are from a C struct retro_system_info::library_name I use this to associate the core with the controls... + * This might not be the most reliable and if you update a core there's a chance the association might break. + * Looking at the Retroarch source might provide some insights here */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Libretro, meta = (ShowInnerProperties)) - TMap ControllersSetOnLaunch; + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Libretro, meta = (ShowInnerProperties)) + TMap EditorPresetControllers; /** * You should provide a path to your ROM relative to the MyROMs directory in the UnrealLibretro directory in your project's Plugins directory. diff --git a/Source/UnrealLibretro/Public/LibretroInputDefinitions.h b/Source/UnrealLibretro/Public/LibretroInputDefinitions.h index e38b7b44..092de314 100644 --- a/Source/UnrealLibretro/Public/LibretroInputDefinitions.h +++ b/Source/UnrealLibretro/Public/LibretroInputDefinitions.h @@ -13,6 +13,37 @@ #include // One of the few std headers where its usage is recommended over a native Unreal Engine implementation see https://docs.unrealengine.com/5.0/en-US/epic-cplusplus-coding-standard-for-unreal-engine/#useofstandardlibraries +#include "LibretroInputDefinitions.generated.h" + +USTRUCT(BlueprintType) +struct FLibretroOption +{ + GENERATED_BODY() + + static CONSTEXPR int DefaultOptionIndex = 0; // By libretro convention + + UPROPERTY(BlueprintReadOnly) + FString Key; + + UPROPERTY(BlueprintReadOnly) + FString Description; + + UPROPERTY(BlueprintReadOnly) + TArray Values; +}; + +USTRUCT(BlueprintType) +struct FLibretroControllerDescription // retro_controller_description +{ + GENERATED_BODY() + + UPROPERTY(VisibleAnywhere) + FString Description{"Unspecified"}; + + UPROPERTY(VisibleAnywhere) + unsigned int ID{ RETRO_DEVICE_DEFAULT }; +}; + // Check libretro.h for further documentation. More or less this maps to RETRO_DEVICE_ID values in there // DO NOT REORDER THESE... doing so will break indexing badly.... // The order for each RETRO_DEVICE is in the same numerical increasing order as every RETRO_DEVICE_*_ID value diff --git a/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.cpp b/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.cpp index db97bfa5..27359af8 100644 --- a/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.cpp +++ b/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.cpp @@ -219,10 +219,10 @@ void FLibretroCoreInstanceDetails::CustomizeDetails(IDetailLayoutBuilder& Detail if (LibretroCoreInstance.IsValid()) { TArray Keys; - LibretroCoreInstance->ControllersSetOnLaunch.GetKeys(Keys); + LibretroCoreInstance->EditorPresetControllers.GetKeys(Keys); // Make the CorePath core always selectable - if (!LibretroCoreInstance->ControllersSetOnLaunch.Contains(CoreLibraryName)) + if (!LibretroCoreInstance->EditorPresetControllers.Contains(CoreLibraryName)) { Keys.Add(CoreLibraryName); } @@ -248,32 +248,32 @@ void FLibretroCoreInstanceDetails::CustomizeDetails(IDetailLayoutBuilder& Detail { return LibretroCoreInstance.IsValid() && SelectedLibraryName == CoreLibraryName - && LibretroCoreInstance->ControllersSetOnLaunch.Contains(SelectedLibraryName) - && LibretroCoreInstance->ControllersSetOnLaunch[SelectedLibraryName][Port].ID != RETRO_DEVICE_DEFAULT; + && LibretroCoreInstance->EditorPresetControllers.Contains(SelectedLibraryName) + && LibretroCoreInstance->EditorPresetControllers[SelectedLibraryName][Port].ID != RETRO_DEVICE_DEFAULT; }), FResetToDefaultHandler::CreateLambda([Port, this](TSharedPtr CoreControllerProperty) { if (LibretroCoreInstance.IsValid()) { - auto &ControllersSetOnLaunchForSelectedCore = LibretroCoreInstance->ControllersSetOnLaunch.FindChecked(SelectedLibraryName); + auto &EditorPresetControllersForSelectedCore = LibretroCoreInstance->EditorPresetControllers.FindChecked(SelectedLibraryName); // Set to unspecified default then try to set to specified one if there is one - ControllersSetOnLaunchForSelectedCore[Port] = FLibretroControllerDescription(); + EditorPresetControllersForSelectedCore[Port] = FLibretroControllerDescription(); for (auto& ControllerDescription : ControllerDescriptions[Port]) { if (ControllerDescription.ID == RETRO_DEVICE_DEFAULT) { - ControllersSetOnLaunchForSelectedCore[Port] = ControllerDescription; + EditorPresetControllersForSelectedCore[Port] = ControllerDescription; } } - RemoveControllersSetOnLaunchForSelectedCoreIfAllAreDefault(); + RemoveEditorPresetControllersForSelectedCoreIfAllAreDefault(); } }) )) #endif // Setting this makes it so our reset to default handlers are called I believe - .PropertyHandleList({ DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(ULibretroCoreInstance, ControllersSetOnLaunch)) }) + .PropertyHandleList({ DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(ULibretroCoreInstance, EditorPresetControllers)) }) .NameContent() .MinDesiredWidth(150.0f) [ @@ -294,32 +294,32 @@ void FLibretroCoreInstanceDetails::CustomizeDetails(IDetailLayoutBuilder& Detail { // Early return if we're not actually changing selected controllers for a port if ( !LibretroCoreInstance.IsValid() - || !LibretroCoreInstance->ControllersSetOnLaunch.Contains(SelectedLibraryName) + || !LibretroCoreInstance->EditorPresetControllers.Contains(SelectedLibraryName) && ControllerDescriptions[RowPort][j].ID == RETRO_DEVICE_DEFAULT - || LibretroCoreInstance->ControllersSetOnLaunch.Contains(SelectedLibraryName) - && ControllerDescriptions[RowPort][j].ID == LibretroCoreInstance->ControllersSetOnLaunch[SelectedLibraryName][RowPort].ID) + || LibretroCoreInstance->EditorPresetControllers.Contains(SelectedLibraryName) + && ControllerDescriptions[RowPort][j].ID == LibretroCoreInstance->EditorPresetControllers[SelectedLibraryName][RowPort].ID) { return; } - FLibretroControllerDescriptions &ControllersSetOnLaunchForCore = LibretroCoreInstance->ControllersSetOnLaunch.FindOrAdd(SelectedLibraryName); + FLibretroControllerDescriptions &EditorPresetControllersForCore = LibretroCoreInstance->EditorPresetControllers.FindOrAdd(SelectedLibraryName); - ControllersSetOnLaunchForCore[RowPort] = ControllerDescriptions[RowPort][j]; + EditorPresetControllersForCore[RowPort] = ControllerDescriptions[RowPort][j]; // Copy over whatever special names the Core uses for the default for (int Port = 0; Port < PortCount; Port++) { - if (ControllersSetOnLaunchForCore[Port].ID != RETRO_DEVICE_DEFAULT) continue; + if (EditorPresetControllersForCore[Port].ID != RETRO_DEVICE_DEFAULT) continue; for (auto& ControllerDescription : ControllerDescriptions[Port]) { if (ControllerDescription.ID == RETRO_DEVICE_DEFAULT) { - ControllersSetOnLaunchForCore[Port] = ControllerDescription; + EditorPresetControllersForCore[Port] = ControllerDescription; } } } - RemoveControllersSetOnLaunchForSelectedCoreIfAllAreDefault(); + RemoveEditorPresetControllersForSelectedCoreIfAllAreDefault(); MarkEditorNeedsSave(); })); @@ -338,12 +338,12 @@ void FLibretroCoreInstanceDetails::CustomizeDetails(IDetailLayoutBuilder& Detail if (LibretroCoreInstance.IsValid()) { - auto* ControllersSetOnLaunchForCore = LibretroCoreInstance->ControllersSetOnLaunch.Find(SelectedLibraryName); + auto* EditorPresetControllersForCore = LibretroCoreInstance->EditorPresetControllers.Find(SelectedLibraryName); // If we have non-default controllers bound to a port - if (ControllersSetOnLaunchForCore) + if (EditorPresetControllersForCore) { - DisplayControllerDescription = (*ControllersSetOnLaunchForCore)[Port]; + DisplayControllerDescription = (*EditorPresetControllersForCore)[Port]; } // Else relay the default description the core told us else @@ -375,21 +375,21 @@ void FLibretroCoreInstanceDetails::CustomizeDetails(IDetailLayoutBuilder& Detail #if ENGINE_MAJOR_VERSION >= 5 .OverrideResetToDefault(FResetToDefaultOverride::Create ( - FIsResetToDefaultVisible::CreateLambda([i, this](TSharedPtr CoreOptionsOverrideProperty) + FIsResetToDefaultVisible::CreateLambda([&Option, this](TSharedPtr EditorPresetOptionsOverrideProperty) { - return LibretroCoreInstance.IsValid() ? LibretroCoreInstance->CoreOptions.Contains(LibretroOptions[i].Key) : false; + return LibretroCoreInstance.IsValid() ? LibretroCoreInstance->EditorPresetOptions.Contains(Option.Key) : false; }), - FResetToDefaultHandler::CreateLambda([i, this](TSharedPtr CoreOptionsOverrideProperty) + FResetToDefaultHandler::CreateLambda([&Option, this](TSharedPtr EditorPresetOptionsOverrideProperty) { - if (LibretroCoreInstance.IsValid() && LibretroCoreInstance->CoreOptions.Contains(LibretroOptions[i].Key)) + if (LibretroCoreInstance.IsValid() && LibretroCoreInstance->EditorPresetOptions.Contains(Option.Key)) { - LibretroCoreInstance->CoreOptions.FindAndRemoveChecked(LibretroOptions[i].Key); + LibretroCoreInstance->EditorPresetOptions.FindAndRemoveChecked(Option.Key); } }) )) #endif // Setting this makes it so our reset to default handlers are called I believe - .PropertyHandleList({ DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(ULibretroCoreInstance, CoreOptions)) }) + .PropertyHandleList({ DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(ULibretroCoreInstance, EditorPresetOptions)) }) .NameContent() .MinDesiredWidth(150.0f) [ @@ -415,16 +415,16 @@ void FLibretroCoreInstanceDetails::CustomizeDetails(IDetailLayoutBuilder& Detail // If default is selected remove the key from core options since no key is implicit default if (i == FLibretroOption::DefaultOptionIndex) { - if (this->LibretroCoreInstance->CoreOptions.Contains(Option.Key)) + if (this->LibretroCoreInstance->EditorPresetOptions.Contains(Option.Key)) { - this->LibretroCoreInstance->CoreOptions.Remove(Option.Key); + this->LibretroCoreInstance->EditorPresetOptions.Remove(Option.Key); this->MarkEditorNeedsSave(); } } // Otherwise add the selected option else { - this->LibretroCoreInstance->CoreOptions.Add(Option.Key) = Option.Values[i]; + this->LibretroCoreInstance->EditorPresetOptions.Add(Option.Key) = Option.Values[i]; this->MarkEditorNeedsSave(); } } @@ -442,7 +442,7 @@ void FLibretroCoreInstanceDetails::CustomizeDetails(IDetailLayoutBuilder& Detail { if (this->LibretroCoreInstance.IsValid()) { - FString* SelectedOptionString = this->LibretroCoreInstance->CoreOptions.Find(Option.Key); + FString* SelectedOptionString = this->LibretroCoreInstance->EditorPresetOptions.Find(Option.Key); if (SelectedOptionString == nullptr) { return FText::FromString(Option.Values[FLibretroOption::DefaultOptionIndex]); diff --git a/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.h b/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.h index 720c295e..d711908b 100644 --- a/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.h +++ b/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.h @@ -58,17 +58,17 @@ class FLibretroCoreInstanceDetails : public IDetailCustomization FString CoreLibraryName; FString SelectedLibraryName; - FORCEINLINE_DEBUGGABLE void RemoveControllersSetOnLaunchForSelectedCoreIfAllAreDefault() + FORCEINLINE_DEBUGGABLE void RemoveEditorPresetControllersForSelectedCoreIfAllAreDefault() { check(LibretroCoreInstance.IsValid()); // If all controllers were set to default no longer bookkeep bound controllers since its not necessary // The important side effect here is that it is reflected to the user in the gui since they'll only care // to view which cores are configured with non-default controllers - if (Algo::AllOf(LibretroCoreInstance->ControllersSetOnLaunch[SelectedLibraryName].ControllerDescription, + if (Algo::AllOf(LibretroCoreInstance->EditorPresetControllers[SelectedLibraryName].ControllerDescription, [](auto& D) { return D.ID == RETRO_DEVICE_DEFAULT; })) { - LibretroCoreInstance->ControllersSetOnLaunch.Remove(SelectedLibraryName); + LibretroCoreInstance->EditorPresetControllers.Remove(SelectedLibraryName); } } From ab6d9aeecd8bbc570a85d03ff2f2f0d4c9ed814b Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Mon, 29 May 2023 14:16:48 -0700 Subject: [PATCH 28/79] Decouple core discovery; Add http error check --- .../Private/UnrealLibretroEditor.cpp | 51 ++++++++++++------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/Source/UnrealLibretroEditor/Private/UnrealLibretroEditor.cpp b/Source/UnrealLibretroEditor/Private/UnrealLibretroEditor.cpp index 938df7ce..e46c247d 100644 --- a/Source/UnrealLibretroEditor/Private/UnrealLibretroEditor.cpp +++ b/Source/UnrealLibretroEditor/Private/UnrealLibretroEditor.cpp @@ -6,6 +6,7 @@ #include "DetailCustomizations.h" #include "HttpModule.h" #include "PlatformHttp.h" +#include "HAL/FileManager.h" #include "Interfaces/IHttpResponse.h" #include "miniz.h" @@ -63,34 +64,48 @@ void FUnrealLibretroEditorModule::StartupModule() { if (bSucceeded) { - check(IsInGameThread()); // Make sure we're handling the result on the GameThread because we're modifying datastructures that Slate uses too - - // Each row in this array looks something like this "2022-10-21 bb0926bb boom3_libretro.dll.zip" - // CoreIdentifer = "boom3" for this example - TArray CoreIndexRows; - HttpResponse->GetContentAsString().ParseIntoArrayLines(CoreIndexRows); - - for (auto CoreIndexRow : CoreIndexRows) + if (EHttpResponseCodes::IsOk(HttpResponse->GetResponseCode())) { - TArray CoreIndexColumnEntries; - CoreIndexRow.ParseIntoArrayWS(CoreIndexColumnEntries); - - auto CoreIdentiferEnd = CoreIndexColumnEntries.Last().Find(CoreLibMetadata[PlatformIndex].Extension, ESearchCase::CaseSensitive); - auto CoreIdentifer = FString(CoreIdentiferEnd, *CoreIndexColumnEntries.Last()); - CoreListViewDataSource.FindOrAdd(CoreIdentifer).PlatformAvailableBitField |= (1UL << PlatformIndex); + check(IsInGameThread()); // Make sure we're handling the result on the GameThread because we're modifying datastructures that Slate uses too + + // Each row in this array looks something like this "2022-10-21 bb0926bb boom3_libretro.dll.zip" + // CoreIdentifer = "boom3" for this example + TArray CoreIndexRows; + HttpResponse->GetContentAsString().ParseIntoArrayLines(CoreIndexRows); - // The logic here is naive I make no attempt to periodically poll the directory for external file creation/deletion - auto RelativeCorePath = CoreLibMetadata[PlatformIndex].DistributionPath + CoreIdentifer + CoreLibMetadata[PlatformIndex].Extension; - if (FPaths::FileExists(FUnrealLibretroModule::ResolveCorePath(RelativeCorePath))) + for (auto CoreIndexRow : CoreIndexRows) { - CoreListViewDataSource[CoreIdentifer].PlatformDownloadedBitField |= (1UL << PlatformIndex); + TArray CoreIndexColumnEntries; + CoreIndexRow.ParseIntoArrayWS(CoreIndexColumnEntries); + + int32 CoreIdentiferEnd = CoreIndexColumnEntries.Last().Find(CoreLibMetadata[PlatformIndex].Extension, ESearchCase::CaseSensitive); + FString CoreIdentifer = FString(CoreIdentiferEnd, *CoreIndexColumnEntries.Last()); + CoreListViewDataSource.FindOrAdd(CoreIdentifer).PlatformAvailableBitField |= (1UL << PlatformIndex); } } + else + { + UE_LOG(Libretro, Warning, TEXT("Failed to get core list for platform. Invalid response. code=%d error=%s"), HttpResponse->GetResponseCode(), *HttpResponse->GetContentAsString()); + } } else { UE_LOG(Libretro, Warning, TEXT("Failed to get core list for platform")); } + + // The logic here is naive I make no attempt to periodically poll the directory for external file creation/deletion + TArray CorePaths; + auto PlatformCoresPath = IPluginManager::Get().FindPlugin("UnrealLibretro")->GetBaseDir() + "/MyCores/" + CoreLibMetadata[PlatformIndex].DistributionPath; + IFileManager::Get().FindFiles(CorePaths, *PlatformCoresPath, nullptr); + + for (FString& CorePath : CorePaths) + { + FString CoreIdentifer; + if (CorePath.Split(CoreLibMetadata[PlatformIndex].Extension, &CoreIdentifer, nullptr, ESearchCase::CaseSensitive, ESearchDir::FromEnd)) + { + CoreListViewDataSource.FindOrAdd(CoreIdentifer).PlatformDownloadedBitField |= (1UL << PlatformIndex); + } + } }); BuildbotRequest->ProcessRequest(); From 8b51fe9374cd4a8a3cd1e2d83db8f397d50ab609 Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Sat, 3 Jun 2023 19:53:15 -0700 Subject: [PATCH 29/79] Add OculusVR as a dependency --- UnrealLibretro.uplugin | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/UnrealLibretro.uplugin b/UnrealLibretro.uplugin index 9dc8d153..b6f827fd 100644 --- a/UnrealLibretro.uplugin +++ b/UnrealLibretro.uplugin @@ -27,5 +27,11 @@ "LoadingPhase": "Default", "WhitelistPlatforms": [ "Win64" ] } - ] + ], + "Plugins": [ + { + "Name": "OculusVR", + "Enabled": true + } + ] } \ No newline at end of file From f77073210e5ef182e51266a30fe487caebfda36b Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Sat, 3 Jun 2023 19:55:58 -0700 Subject: [PATCH 30/79] Add obvious disclaimer --- Source/UnrealLibretro/UnrealLibretro.Build.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/UnrealLibretro/UnrealLibretro.Build.cs b/Source/UnrealLibretro/UnrealLibretro.Build.cs index 64c7e992..899f9fbf 100644 --- a/Source/UnrealLibretro/UnrealLibretro.Build.cs +++ b/Source/UnrealLibretro/UnrealLibretro.Build.cs @@ -12,7 +12,7 @@ public UnrealLibretro(ReadOnlyTargetRules Target) : base(Target) PrivateIncludePaths.Add("UnrealLibretro/Public/libretro/include"); - RuntimeDependencies.Add("$(PluginDir)/MyRoms/*" ); + RuntimeDependencies.Add("$(PluginDir)/MyRoms/*" ); // Don't actually distribute roms this is just for the convinience of testing RuntimeDependencies.Add("$(PluginDir)/Saves/*" ); RuntimeDependencies.Add("$(PluginDir)/System/*" ); From 1105b31a22f95096bb0123a72b40b538c2ea4c7c Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Sat, 3 Jun 2023 20:07:31 -0700 Subject: [PATCH 31/79] Add planned supported platforms --- Source/UnrealLibretro/Public/UnrealLibretro.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Source/UnrealLibretro/Public/UnrealLibretro.h b/Source/UnrealLibretro/Public/UnrealLibretro.h index b317948b..609ef8c2 100644 --- a/Source/UnrealLibretro/Public/UnrealLibretro.h +++ b/Source/UnrealLibretro/Public/UnrealLibretro.h @@ -16,9 +16,12 @@ UNREALLIBRETRO_API DECLARE_LOG_CATEGORY_EXTERN(Libretro, Log, All); static const struct { FString DistributionPath; FString Extension; FString BuildbotPath; FName ImageName; } CoreLibMetadata[] = { - { TEXT("Win64/"), "_libretro.dll", "https://buildbot.libretro.com/nightly/windows/x86_64/latest/" , "Launcher.Platform_Windows.Large" }, - { TEXT("Android/armeabi-v7a/"), "_libretro_android.so", "https://buildbot.libretro.com/nightly/android/latest/armeabi-v7a/", "Launcher.Platform_Android.Large" }, - { TEXT("Android/arm64-v8a/"), "_libretro_android.so", "https://buildbot.libretro.com/nightly/android/latest/arm64-v8a/" , "Launcher.Platform_Android.Large" }, + { TEXT("Win64/"), "_libretro.dll", "https://buildbot.libretro.com/nightly/windows/x86_64/latest/", "Launcher.Platform_Windows.Large" }, + { TEXT("Android/armeabi-v7a/"), "_libretro_android.so", "https://buildbot.libretro.com/nightly/android/latest/armeabi-v7a/", "Launcher.Platform_Android.Large" }, + { TEXT("Android/arm64-v8a/"), "_libretro_android.so", "https://buildbot.libretro.com/nightly/android/latest/arm64-v8a/", "Launcher.Platform_Android.Large" }, +// { TEXT("Linux/x86_64/"), "_libretro.so", "https://buildbot.libretro.com/nightly/linux/x86_64/latest/", "Launcher.Platform_Linux.Large" }, +// { TEXT("Mac/arm64-v8a/"), "_libretro.dylib", "https://buildbot.libretro.com/nightly/apple/osx/arm64/latest/", "Launcher.Platform_Mac.Large" }, +// { TEXT("iOS/universal/"), "_libretro_ios.dylib", "https://buildbot.libretro.com/nightly/apple/ios-arm64/latest/", "Launcher.Platform_iOS.Large" }, }; #if PLATFORM_WINDOWS From 329a9c5477f8ee0b2ef31ee0c938ac49857c6483 Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Sat, 3 Jun 2023 20:26:59 -0700 Subject: [PATCH 32/79] Fix indexing logic --- Source/UnrealLibretro/Private/sdlarch.cpp | 14 ++++++++----- .../Public/LibretroInputDefinitions.h | 20 ++++++++++--------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/Source/UnrealLibretro/Private/sdlarch.cpp b/Source/UnrealLibretro/Private/sdlarch.cpp index 252341ca..0ae1a918 100644 --- a/Source/UnrealLibretro/Private/sdlarch.cpp +++ b/Source/UnrealLibretro/Private/sdlarch.cpp @@ -852,18 +852,22 @@ bool LibretroContext::core_environment(unsigned cmd, void *data) { // Unfinished experiment with less branchy version of this function https://godbolt.org/z/hYeYxr95r int16_t LibretroContext::core_input_state(unsigned port, unsigned device, unsigned index, unsigned id) { - // Some of the setup to get the core to poll for certain types of input is either done through retro_set_controller_port_device - // or in the handled RETRO_ENVIRONMENT_GET_VARIABLE and RETRO_ENVIRONMENT_SET_CONTROLLER_INFO events in the retro_environment_t - // callback core_environment. Getting the core to handle specific input amounts to reading documentation and sifting through the core's code + // To get the core to poll for certain types of input sometimes requires setting particular controllers for compatible ports + // or changing specific options related to the input you're trying to poll for. If it's not obvious your main resources are + // forums, the libretro documentation, or looking through the core's code itself. + // Also here are some pitfalls I've encountered: + // - The core might need to poll for at least two frames to register an input + // - Some cores will not poll for any input by default (I fix this by always binding the RETRO_DEVICE_JOYPAD) + // - The RETRO_DEVICE_POINTER interface is generally preferred over the lightgun and mouse even for things like lightguns and mice although you still use some parts of the lightgun interface for handling lightgun input probably same goes for mouse switch (device) { case RETRO_DEVICE_JOYPAD: return InputState[port][to_integral(ERetroDeviceID::JoypadB) + id]; case RETRO_DEVICE_LIGHTGUN: return InputState[port][to_integral(ERetroDeviceID::LightgunX) + id]; case RETRO_DEVICE_ANALOG: return InputState[port][to_integral(ERetroDeviceID::AnalogLeftX) + 2 * index + (id % RETRO_DEVICE_ID_JOYPAD_L2)]; // The indexing logic is broken and might OOBs if we're queried for something that isn't an analog trigger or stick - case RETRO_DEVICE_POINTER: return InputState[port][to_integral(ERetroDeviceID::PointerX) + id]; + case RETRO_DEVICE_POINTER: return InputState[port][to_integral(ERetroDeviceID::PointerX) + 4 * index + id]; case RETRO_DEVICE_MOUSE: case RETRO_DEVICE_KEYBOARD: - default: return 0; + default: return 0; } } diff --git a/Source/UnrealLibretro/Public/LibretroInputDefinitions.h b/Source/UnrealLibretro/Public/LibretroInputDefinitions.h index 092de314..2f550f68 100644 --- a/Source/UnrealLibretro/Public/LibretroInputDefinitions.h +++ b/Source/UnrealLibretro/Public/LibretroInputDefinitions.h @@ -100,16 +100,18 @@ enum class ERetroDeviceID : uint8 PointerX, PointerY, PointerPressed, - PointerX1 UMETA(Hidden), - PointerY1 UMETA(Hidden), - PointerPressed1 UMETA(Hidden), - PointerX2 UMETA(Hidden), - PointerY2 UMETA(Hidden), - PointerPressed2 UMETA(Hidden), - PointerX3 UMETA(Hidden), - PointerY3 UMETA(Hidden), - PointerPressed3 UMETA(Hidden), PointerCount, + PointerX1 UMETA(Hidden), + PointerY1 UMETA(Hidden), + PointerPressed1 UMETA(Hidden), + PointerCountVoid1 UMETA(Hidden), + PointerX2 UMETA(Hidden), + PointerY2 UMETA(Hidden), + PointerPressed2 UMETA(Hidden), + PointerCountVoid2 UMETA(Hidden), + PointerX3 UMETA(Hidden), + PointerY3 UMETA(Hidden), + PointerPressed3 UMETA(Hidden), Size UMETA(Hidden), }; From 3269058ffb4a08392748af02d2d11bc092c59d64 Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Sat, 3 Jun 2023 22:33:25 -0700 Subject: [PATCH 33/79] Add a temp fix for pixel formats on Android --- Source/UnrealLibretro/Private/sdlarch.cpp | 47 +++++++++++++++++++---- Source/UnrealLibretro/Private/sdlarch.h | 2 + 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/Source/UnrealLibretro/Private/sdlarch.cpp b/Source/UnrealLibretro/Private/sdlarch.cpp index 0ae1a918..3cdab0c4 100644 --- a/Source/UnrealLibretro/Private/sdlarch.cpp +++ b/Source/UnrealLibretro/Private/sdlarch.cpp @@ -292,7 +292,7 @@ static bool GLLogCall(const char* function, const char* file, int line) FRHIResourceCreateInfo Info{ TEXT("Dummy Texture for now") }; this->Unreal.TextureRHI = RHICreateTexture2D(core.av.geometry.max_width, core.av.geometry.max_height, - PF_R8G8B8A8, + UnrealPixelFormat, 1, 1, TexCreate_CPUWritable | TexCreate_Dynamic, @@ -305,7 +305,7 @@ static bool GLLogCall(const char* function, const char* file, int line) UnrealRenderTarget->bGPUSharedFlag = true; // Allows us to share this rendertarget with other applications and APIs in this case OpenGL UnrealRenderTarget->InitCustomFormat(core.av.geometry.max_width, core.av.geometry.max_height, - PF_R8G8B8A8, + UnrealPixelFormat, false); ENQUEUE_RENDER_COMMAND(LibretroInitRHIFramebuffer) ([&](FRHICommandListImmediate& RHICmdList) @@ -481,9 +481,10 @@ static bool GLLogCall(const char* function, const char* file, int line) auto bgra_buffer = core.software.bgra_buffers[core.free_framebuffer_index = !core.free_framebuffer_index]; + if (core.gl.pixel_format == GL_BGRA) { switch (core.gl.pixel_type) { case GL_UNSIGNED_SHORT_5_6_5: { - conv_rgb565_abgr8888(bgra_buffer, data, + conv_rgb565_argb8888(bgra_buffer, data, width, height, SrcPitch, pitch); } @@ -492,6 +493,23 @@ static bool GLLogCall(const char* function, const char* file, int line) checkNoEntry(); } break; + case GL_UNSIGNED_BYTE: { + conv_copy(bgra_buffer, data, + width, height, + SrcPitch, pitch); + } + break; + default: + checkNoEntry(); + } + } else { + switch (core.gl.pixel_type) { + case GL_UNSIGNED_SHORT_5_6_5: { + conv_rgb565_abgr8888(bgra_buffer, data, + width, height, + SrcPitch, pitch); + } + break; case GL_UNSIGNED_BYTE: { conv_argb8888_abgr8888(bgra_buffer, data, width, height, @@ -500,6 +518,7 @@ static bool GLLogCall(const char* function, const char* file, int line) break; default: checkNoEntry(); + } } prepare_frame_for_upload_to_unreal_RHI(bgra_buffer); @@ -546,8 +565,8 @@ static bool GLLogCall(const char* function, const char* file, int line) LogGLErrors(glReadPixels(0, 0, core.av.geometry.max_width, // @enhancement Only copy the portion of the buffer we need rather than the max possible potential size core.av.geometry.max_height, - GL_RGBA, - GL_UNSIGNED_BYTE, + core.gl.pixel_format, + core.gl.pixel_type, offset_into_pbo_where_data_is_written)); } glDeleteSync(core.gl.fence); @@ -713,9 +732,21 @@ bool LibretroContext::core_environment(unsigned cmd, void *data) { core.gl.bits_per_pixel = sizeof(uint16_t); break; case RETRO_PIXEL_FORMAT_XRGB8888: - core.gl.pixel_type = GL_UNSIGNED_BYTE; - core.gl.pixel_format = GL_RGBA; - core.gl.bits_per_pixel = sizeof(uint32_t); + + core.gl.pixel_type = GL_UNSIGNED_BYTE; + core.gl.pixel_format = GL_BGRA; + core.gl.bits_per_pixel = sizeof(uint32_t); + + // @todo The OpenGL RHI backend is supposed to swizzle the red and blue components of textures created with render targets + // so in effect it should be a BGRA however that doesn't seem to be the case and it always behaves like rgba + // I'll probably clean all this stuff up when I do a big optimization of the framebuffer copying stuff + if ((FString)GDynamicRHI->GetName() == TEXT("OpenGL")) { + UnrealPixelFormat = PF_R8G8B8A8; + core.gl.pixel_type = GL_UNSIGNED_BYTE; + core.gl.pixel_format = GL_RGBA; + core.gl.bits_per_pixel = sizeof(uint32_t); + } + break; case RETRO_PIXEL_FORMAT_RGB565: core.gl.pixel_type = GL_UNSIGNED_SHORT_5_6_5; diff --git a/Source/UnrealLibretro/Private/sdlarch.h b/Source/UnrealLibretro/Private/sdlarch.h index b57760ad..4b6eb6d0 100644 --- a/Source/UnrealLibretro/Private/sdlarch.h +++ b/Source/UnrealLibretro/Private/sdlarch.h @@ -163,6 +163,8 @@ struct LibretroContext { std::atomic CoreState{ ECoreState::Starting }; + EPixelFormat UnrealPixelFormat{PF_B8G8R8A8}; + protected: LibretroContext() {} ~LibretroContext() {} From 6692eb7f4ebefa44e5cfbbc42094e44e2420fbf1 Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Sun, 4 Jun 2023 17:43:43 -0700 Subject: [PATCH 34/79] Mark the editor module as an editor module --- UnrealLibretro.uplugin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UnrealLibretro.uplugin b/UnrealLibretro.uplugin index b6f827fd..b7a82bf4 100644 --- a/UnrealLibretro.uplugin +++ b/UnrealLibretro.uplugin @@ -23,7 +23,7 @@ }, { "Name": "UnrealLibretroEditor", - "Type": "Runtime", + "Type": "Editor", "LoadingPhase": "Default", "WhitelistPlatforms": [ "Win64" ] } From cee965259616d009f62a3aad0270674a81d38a04 Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Sun, 4 Jun 2023 18:58:51 -0700 Subject: [PATCH 35/79] BUGFIX default initialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gl_win32_interop_supported_by_driver not being initialized caused immediate crashing in packaged builds 😔 --- Source/UnrealLibretro/Private/LambdaRunnable.h | 4 ++-- Source/UnrealLibretro/Private/sdlarch.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/UnrealLibretro/Private/LambdaRunnable.h b/Source/UnrealLibretro/Private/LambdaRunnable.h index 5c1f18fc..43a31c8d 100644 --- a/Source/UnrealLibretro/Private/LambdaRunnable.h +++ b/Source/UnrealLibretro/Private/LambdaRunnable.h @@ -13,7 +13,7 @@ class UNREALLIBRETRO_API FLambdaRunnable : public FRunnable private: /** Thread to run the worker FRunnable on */ - uint64 Number; + uint64 Number{0}; //Lambda function pointer TUniqueFunction< void()> FunctionPointer; @@ -27,7 +27,7 @@ class UNREALLIBRETRO_API FLambdaRunnable : public FRunnable // Begin FRunnable interface. virtual uint32 Run(); - FRunnableThread* Thread; + FRunnableThread* Thread{nullptr}; // End FRunnable interface /* diff --git a/Source/UnrealLibretro/Private/sdlarch.h b/Source/UnrealLibretro/Private/sdlarch.h index 4b6eb6d0..470996c4 100644 --- a/Source/UnrealLibretro/Private/sdlarch.h +++ b/Source/UnrealLibretro/Private/sdlarch.h @@ -255,7 +255,7 @@ struct LibretroContext { #define DEFINE_GL_PROCEDURES(Type,Func) Type Func = NULL; ENUM_GL_PROCEDURES(DEFINE_GL_PROCEDURES); ENUM_GL_WIN32_INTEROP_PROCEDURES(DEFINE_GL_PROCEDURES) - bool gl_win32_interop_supported_by_driver; + bool gl_win32_interop_supported_by_driver{false}; void create_window(); void video_configure(const struct retro_game_geometry* geom); From 4fbf32335c4a3849989ca360f4fa3dece979b4a5 Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Sun, 4 Jun 2023 19:14:31 -0700 Subject: [PATCH 36/79] Document and fix small things --- .../UnrealLibretro/Private/LibretroCoreInstance.cpp | 9 +++++++-- Source/UnrealLibretro/Private/sdlarch.h | 12 ++++++------ Source/UnrealLibretro/Public/LibretroGameMode.h | 1 + Source/UnrealLibretro/Public/UnrealLibretro.h | 4 ++-- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp b/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp index dff1f083..7b332868 100644 --- a/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp +++ b/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp @@ -98,6 +98,12 @@ void ULibretroCoreInstance::Launch() { Shutdown(); + if (this->RomPath.IsEmpty()) + { + UE_LOG(Libretro, Warning, TEXT("Failed to launch Libretro core '%s'. Path given for ROM was empty"), *this->CorePath); + return; + } + auto _CorePath = IFileManager::Get().ConvertToAbsolutePathForExternalAppForWrite(*FUnrealLibretroModule::ResolveCorePath(this->CorePath)); auto _RomPath = IFileManager::Get().ConvertToAbsolutePathForExternalAppForWrite(*FUnrealLibretroModule::ResolveROMPath (this->RomPath)); @@ -134,8 +140,7 @@ void ULibretroCoreInstance::Launch() //RenderTarget->AddressY = TA_Clamp; this->CoreInstance = LibretroContext::Launch(this, _CorePath, _RomPath, RenderTarget, static_cast(AudioBuffer), - [weakThis = MakeWeakObjectPtr(this), SRAMPath = FUnrealLibretroModule::ResolveSRAMPath(_RomPath, SRAMPath), - ControllersSetOnLaunch = this->ControllersSetOnLaunch] + [weakThis = MakeWeakObjectPtr(this), SRAMPath = FUnrealLibretroModule::ResolveSRAMPath(_RomPath, SRAMPath)] (LibretroContext *_CoreInstance, libretro_api_t &libretro_api) { // Core has loaded diff --git a/Source/UnrealLibretro/Private/sdlarch.h b/Source/UnrealLibretro/Private/sdlarch.h index 470996c4..3fc9a358 100644 --- a/Source/UnrealLibretro/Private/sdlarch.h +++ b/Source/UnrealLibretro/Private/sdlarch.h @@ -154,12 +154,12 @@ struct LibretroContext { * +-----------+ +-----------+ */ enum class ECoreState : int8 - { - Starting, // Guranteed to stay in this state until we start running the core - Running, - Paused, - Shutdown - }; + { + Starting, + Running, + Paused, + Shutdown + }; std::atomic CoreState{ ECoreState::Starting }; diff --git a/Source/UnrealLibretro/Public/LibretroGameMode.h b/Source/UnrealLibretro/Public/LibretroGameMode.h index 8b6ede21..f29049ba 100644 --- a/Source/UnrealLibretro/Public/LibretroGameMode.h +++ b/Source/UnrealLibretro/Public/LibretroGameMode.h @@ -19,6 +19,7 @@ class UNREALLIBRETRO_API ALibretroGameMode : public AGameModeBase public: // @todo There is probably some smarter way of determining this // This returns true if you launch PIE in "VR Preview" mode + // If you start your packaged game with the flag -vr then this will also be true static CONSTEXPR auto ShouldStartPlayerInVRPawn = UHeadMountedDisplayFunctionLibrary::IsHeadMountedDisplayEnabled; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Classes) diff --git a/Source/UnrealLibretro/Public/UnrealLibretro.h b/Source/UnrealLibretro/Public/UnrealLibretro.h index 609ef8c2..00acdb21 100644 --- a/Source/UnrealLibretro/Public/UnrealLibretro.h +++ b/Source/UnrealLibretro/Public/UnrealLibretro.h @@ -101,9 +101,9 @@ class FUnrealLibretroModule : public IModuleInterface return IfRelativeResolvePathRelativeToThisPluginWithPathExtensions(UnresolvedCorePath, TEXT("MyCores")); } - static FString ResolveROMPath(const FString& UnresolvedRomRom) + static FString ResolveROMPath(const FString& UnresolvedRomPath) { - return IfRelativeResolvePathRelativeToThisPluginWithPathExtensions(UnresolvedRomRom, TEXT("MyROMs")); + return IfRelativeResolvePathRelativeToThisPluginWithPathExtensions(UnresolvedRomPath, TEXT("MyROMs")); } static FString ResolveSaveStatePath(const FString& UnresolvedRomPath, const FString& UnresolvedSavePath) From 6986cbb039e845d12b4ea8867ad8a397e71fe7da Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Sun, 4 Jun 2023 19:16:23 -0700 Subject: [PATCH 37/79] Add bSuccess parameter to core launch event --- Source/UnrealLibretro/Private/LibretroCoreInstance.cpp | 4 ++-- Source/UnrealLibretro/Public/LibretroCoreInstance.h | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp b/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp index 7b332868..ab9aa830 100644 --- a/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp +++ b/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp @@ -161,8 +161,8 @@ void ULibretroCoreInstance::Launch() { if (weakThis.IsValid()) { - weakThis->OnCoreIsReady.Broadcast(weakThis->RenderTarget, - weakThis->AudioBuffer); + weakThis->OnLaunchComplete.Broadcast(weakThis->RenderTarget, + weakThis->AudioBuffer, true); weakThis->bFrameBottomLeftOrigin = bottom_left_origin; weakThis->FrameWidth = geometry.base_width; diff --git a/Source/UnrealLibretro/Public/LibretroCoreInstance.h b/Source/UnrealLibretro/Public/LibretroCoreInstance.h index 3f32cc2a..70c626cf 100644 --- a/Source/UnrealLibretro/Public/LibretroCoreInstance.h +++ b/Source/UnrealLibretro/Public/LibretroCoreInstance.h @@ -19,7 +19,7 @@ struct FLibretroControllerDescriptions const FLibretroControllerDescription& operator[](int Port) const { return ControllerDescription[Port]; } }; -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnCoreIsReady, const class UTextureRenderTarget2D*, LibretroFramebuffer, const class USoundWave*, AudioBuffer); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnLaunchComplete, const class UTextureRenderTarget2D*, LibretroFramebuffer, const class USoundWave*, AudioBuffer, const bool, bSuccess); DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnCoreFramebufferResize); @@ -39,10 +39,12 @@ class UNREALLIBRETRO_API ULibretroCoreInstance : public UActorComponent /** Delegate Functions */ /** - * Issued after the emulator has been successfully launched + * Issued after a call to Launch has finished setting up the core on a background thread + * + * @param bSuccess - If true the procedures marked IneffectiveBeforeLaunchComplete will now function */ UPROPERTY(BlueprintAssignable) - FOnCoreIsReady OnCoreIsReady; + FOnLaunchComplete OnLaunchComplete; /** * Issued initially whenever the core framebuffer is changes dimensions. The arguments provided will scale uv's appropriately to exactly fit the framebuffer From 9edff07de5aa89e1f9876a0dbb71d07507ed2d5e Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Sun, 4 Jun 2023 20:19:51 -0700 Subject: [PATCH 38/79] Update documentation --- COMPATIBILITY.md | 17 ++++++++--------- README.md | 8 +++++--- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md index ddff8214..6453e9a7 100644 --- a/COMPATIBILITY.md +++ b/COMPATIBILITY.md @@ -27,7 +27,9 @@ Unfortunately the full Libretro API is quite complex so I have only partially im I know for certain `gearboy` and `mupen64plus_next` work so I'd try testing those first. I'll probably try to set up automated regression tests in the future so a list can be automatically maintained. # How to run the right cores for the right platform -You could manually come up with a way to do this, but the way I provide will package the right cores for the right platform as well. Also I'm pretty sure this would be kind of nasty to do in Blueprints. +You can always manually give a path to a core if needed however the recommended way is to store them in the same way as the `UnrealLibretroEditor` module does when it downloads them. These paths are used when packaging a project and `UnrealLibretro` uses them to load cores in a platform agnostic way. + +Here's how the plugin directory might look after downloading a couple cores. ``` 📦UnrealLibretro ┣ 📂MyCores @@ -39,18 +41,15 @@ You could manually come up with a way to do this, but the way I provide will pac ┃ ┃ ┣ 📜gearboy_libretro_android.so ┃ ┃ ┗ 📜mupen64plus_next_gles3_libretro_android.so ┃ ┗ 📂Win64 - ┃ ┣ 📜gearboy_libretro_android.dll - ┃ ┗ 📜mupen64plus_next_gles3_libretro_android.dll + ┃ ┣ 📜gearboy_libretro.dll + ┃ ┗ 📜mupen64plus_next_libretro.dll ┃ ┗ 📂MyROMs ┣ 📜baserom.us.z64 ┗ 📜Legend of Zelda, The - Link's Awakening DX (USA, Europe) (SGB Enhanced).gbc ``` - - If the directory structure of `MyCores` and `MyROMs` looks like this then if you specify *Core Path* as the name of the core without the extension as is done in this image - - ![](Resources/PathExample.png) - - Then it should load the right Libretro Core for the corresponding platform i.e. on an x86_64 Windows machine it loads `MyCores/Win64/gearboy_libretro_android.dll` (Ignore the fact it says Android I just changed it so the name was the same) +- Cores are organized hierarchically in the same convention Unreal Engine uses for binaries +- Notice how `gearboy_libretro.dll` and `gearboy_libretro_android.so` only differ by a `.dll` and `_android.so`. This is actually a standard naming format used for cores. So I've written the code so `ULibretroCoreInstance::CorePath` can just be set to `gearboy` and the correct core will be chosen for the correct platform +- Unfortunately some cores like `mupen64plus_next_libretro.dll` and `mupen64plus_next_gles3_libretro_android.so` differ by more than just the standard format so they can't be directly associated. So you'll have to come up with your own way to load these correctly for the correct platform [1]: README.md#sometimes-required-download-content-folder \ No newline at end of file diff --git a/README.md b/README.md index 8eee61aa..4b10bc93 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ Then in the Windows file explorer navigate to the root directory of your project ## Integrating into your Project ### Download a Libretro Core -You can download a Libretro Core from [here](https://buildbot.libretro.com/nightly/windows/x86_64/latest/) and place it into the MyCores folder. +You can download Libretro Cores directly from the Unreal Editor. If that isn't working try accessing downloading them from the [buildbot](https://buildbot.libretro.com/nightly/windows/x86_64/latest/) ### Download a ROM You know and I know you know where to get these. Once you have the one you want place it into your MyROMs folder. @@ -38,8 +38,10 @@ You know and I know you know where to get these. Once you have the one you want Some cores require that you also provide a content folder. PPSSPP is [one example](https://docs.libretro.com/library/ppsspp/#bios). Mainly this just involves taking a folder from a release of the emulator and moving it into the `UnrealLibretro/System` directory. There might be weirder ones. You can probably just find them by googling or searching the [Libretro docs](https://docs.libretro.com/). ### In the Unreal Editor -Restart your project if you performed the setup while the Unreal Editor was running. -Navigate to UnrealLibretro's content folder in the Unreal Editor content browser, and open the example map LibretroWorld. Exploring the objects in this folder should give you an idea of how to use the API. +- Restart your project if you performed the setup process while the Unreal Editor was running +- Enable "Show Plugin Content" from the Content Browser options +- Navigate to UnrealLibretro's content folder in the Unreal Editor Content Browser, and open the example map LibretroMap +- Click on the actors to set their Cores and ROMs then hit play ## Contributing From ddcec0fcb1cb09562561f2f0e60a119193adca4f Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Sun, 4 Jun 2023 20:22:17 -0700 Subject: [PATCH 39/79] Rename variable to comply with naming conventions --- .../Private/LibretroCoreInstance.cpp | 14 +++++----- .../UnrealLibretro/Private/UnrealLibretro.cpp | 6 ++-- Source/UnrealLibretro/Private/sdlarch.cpp | 28 +++++++++---------- Source/UnrealLibretro/Private/sdlarch.h | 10 +++---- .../Public/LibretroCoreInstance.h | 2 +- 5 files changed, 30 insertions(+), 30 deletions(-) diff --git a/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp b/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp index ab9aa830..d2e86892 100644 --- a/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp +++ b/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp @@ -25,8 +25,8 @@ void ULibretroCoreInstance::SetController(int Port, int64 ID) { NOT_LAUNCHED_GUARD - // This if statement guards against a datarace on LibretroContext::DeviceIDs - if (CoreInstance.GetValue()->CoreState.load(std::memory_order_acquire) != LibretroContext::ECoreState::Starting) + // This if statement guards against a datarace on FLibretroContext::DeviceIDs + if (CoreInstance.GetValue()->CoreState.load(std::memory_order_acquire) != FLibretroContext::ECoreState::Starting) { CoreInstance.GetValue()->DeviceIDs[Port] = ID; CoreInstance.GetValue()->EnqueueTask([Port, ID](libretro_api_t &libretro_api) @@ -40,8 +40,8 @@ void ULibretroCoreInstance::GetController(int Port, int64& ID, FString& Descript { NOT_LAUNCHED_GUARD - // This if statement guards against a datarace on LibretroContext::DeviceIDs - if (CoreInstance.GetValue()->CoreState.load(std::memory_order_acquire) != LibretroContext::ECoreState::Starting) + // This if statement guards against a datarace on FLibretroContext::DeviceIDs + if (CoreInstance.GetValue()->CoreState.load(std::memory_order_acquire) != FLibretroContext::ECoreState::Starting) { ID = CoreInstance.GetValue()->DeviceIDs[Port]; for (FLibretroControllerDescription& ControllerDescription : CoreInstance.GetValue()->ControllerDescriptions[Port]) @@ -139,9 +139,9 @@ void ULibretroCoreInstance::Launch() //RenderTarget->AddressX = TA_Clamp; //RenderTarget->AddressY = TA_Clamp; - this->CoreInstance = LibretroContext::Launch(this, _CorePath, _RomPath, RenderTarget, static_cast(AudioBuffer), + this->CoreInstance = FLibretroContext::Launch(this, _CorePath, _RomPath, RenderTarget, static_cast(AudioBuffer), [weakThis = MakeWeakObjectPtr(this), SRAMPath = FUnrealLibretroModule::ResolveSRAMPath(_RomPath, SRAMPath)] - (LibretroContext *_CoreInstance, libretro_api_t &libretro_api) + (FLibretroContext *_CoreInstance, libretro_api_t &libretro_api) { // Core has loaded // Load save data into core @todo this is just a weird place to hook this in @@ -255,7 +255,7 @@ void ULibretroCoreInstance::Shutdown() NOT_LAUNCHED_GUARD - LibretroContext::Shutdown(CoreInstance.GetValue()); + FLibretroContext::Shutdown(CoreInstance.GetValue()); CoreInstance.Reset(); } diff --git a/Source/UnrealLibretro/Private/UnrealLibretro.cpp b/Source/UnrealLibretro/Private/UnrealLibretro.cpp index 8ed8281d..f57576b9 100644 --- a/Source/UnrealLibretro/Private/UnrealLibretro.cpp +++ b/Source/UnrealLibretro/Private/UnrealLibretro.cpp @@ -52,10 +52,10 @@ void FUnrealLibretroModule::StartupModule() void FUnrealLibretroModule::ShutdownModule() { // @todo For now I skip resource cleanup. It could be added back if I added isReadyForFinishDestroy(bool) to ULibretroCoreInstance - // in conjunction with waiting for the LibretroContext to destruct since UE uses the outstanding UObjects from this module visible through + // in conjunction with waiting for the FLibretroContext to destruct since UE uses the outstanding UObjects from this module visible through // the reflection system (UProperty, etc) to determine when it is safe to shutdown this module. - // This is because LibretroContext depends on the dlls and paths loaded by this module and is destructed asynchronously and is not a UObject. - // I could also fix the shutdown_audio hack as well as remove the numerous weak pointers in LibretroContext. + // This is because FLibretroContext depends on the dlls and paths loaded by this module and is destructed asynchronously and is not a UObject. + // I could also fix the shutdown_audio hack as well as remove the numerous weak pointers in FLibretroContext. // I'm nervous how much the engine will block the game thread on that condition though so that still might not be a solution. #if 0 diff --git a/Source/UnrealLibretro/Private/sdlarch.cpp b/Source/UnrealLibretro/Private/sdlarch.cpp index 3cdab0c4..41793a32 100644 --- a/Source/UnrealLibretro/Private/sdlarch.cpp +++ b/Source/UnrealLibretro/Private/sdlarch.cpp @@ -128,7 +128,7 @@ static bool GLLogCall(const char* function, const char* file, int line) return true; } - void LibretroContext::create_window() { + void FLibretroContext::create_window() { #if PLATFORM_ANDROID // Get an OpenGL context via EGL const EGLint attribs[] = { @@ -265,7 +265,7 @@ static bool GLLogCall(const char* function, const char* file, int line) UE_LOG(Libretro, Log, TEXT("GL_VERSION: %s\n"), ANSI_TO_TCHAR((char*)glGetString(GL_VERSION))); } - void LibretroContext::video_configure(const struct retro_game_geometry *geom) { + void FLibretroContext::video_configure(const struct retro_game_geometry *geom) { if (!core.gl.pixel_format) { auto data = RETRO_PIXEL_FORMAT_0RGB1555; this->core_environment(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &data); @@ -429,7 +429,7 @@ static bool GLLogCall(const char* function, const char* file, int line) #include "Async/TaskGraphInterfaces.h" // Stripped down code for profiling purposes https://godbolt.org/z/c57esx - void LibretroContext::core_video_refresh(const void *data, unsigned width, unsigned height, unsigned pitch) { + void FLibretroContext::core_video_refresh(const void *data, unsigned width, unsigned height, unsigned pitch) { DECLARE_SCOPE_CYCLE_COUNTER(TEXT("PrepareFrameBufferForRenderThread"), STAT_LibretroPrepareFrameBufferForRenderThread, STATGROUP_UnrealLibretro); unsigned SrcPitch = 4 * core.av.geometry.max_width; @@ -591,7 +591,7 @@ static bool GLLogCall(const char* function, const char* file, int line) } } -size_t LibretroContext::core_audio_write(const int16_t *buf, size_t frames) { +size_t FLibretroContext::core_audio_write(const int16_t *buf, size_t frames) { unsigned FramesEnqueued = 0; while (FramesEnqueued < frames && Unreal.AudioQueue->Enqueue(((int32*)buf)[FramesEnqueued])) { FramesEnqueued++; @@ -632,7 +632,7 @@ static void core_log(enum retro_log_level level, const char *fmt, ...) { } -bool LibretroContext::core_environment(unsigned cmd, void *data) { +bool FLibretroContext::core_environment(unsigned cmd, void *data) { bool delegate_status{false}; if (CoreEnvironmentCallback) { @@ -882,7 +882,7 @@ bool LibretroContext::core_environment(unsigned cmd, void *data) { } // Unfinished experiment with less branchy version of this function https://godbolt.org/z/hYeYxr95r -int16_t LibretroContext::core_input_state(unsigned port, unsigned device, unsigned index, unsigned id) { +int16_t FLibretroContext::core_input_state(unsigned port, unsigned device, unsigned index, unsigned id) { // To get the core to poll for certain types of input sometimes requires setting particular controllers for compatible ports // or changing specific options related to the input you're trying to poll for. If it's not obvious your main resources are // forums, the libretro documentation, or looking through the core's code itself. @@ -903,12 +903,12 @@ int16_t LibretroContext::core_input_state(unsigned port, unsigned device, unsign } -void LibretroContext::core_audio_sample(int16_t left, int16_t right) { +void FLibretroContext::core_audio_sample(int16_t left, int16_t right) { int16_t buf[2] = {left, right}; core_audio_write(buf, (size_t)1); } -void LibretroContext::load(const char *sofile) { +void FLibretroContext::load(const char *sofile) { void (*set_environment)(retro_environment_t) = NULL; void (*set_video_refresh)(retro_video_refresh_t) = NULL; void (*set_input_poll)(retro_input_poll_t) = NULL; @@ -964,7 +964,7 @@ void LibretroContext::load(const char *sofile) { } -void LibretroContext::load_game(const char* filename) { +void FLibretroContext::load_game(const char* filename) { struct retro_game_info info = { filename , nullptr, (size_t)0, "" }; TArray gameBinary; @@ -998,7 +998,7 @@ void LibretroContext::load_game(const char* filename) { video_configure(&core.av.geometry); } -LibretroContext* LibretroContext::Launch(ULibretroCoreInstance* LibretroCoreInstance, FString core, FString game, UTextureRenderTarget2D* RenderTarget, URawAudioSoundWave* SoundBuffer, TUniqueFunction LoadedCallback) +FLibretroContext* FLibretroContext::Launch(ULibretroCoreInstance* LibretroCoreInstance, FString core, FString game, UTextureRenderTarget2D* RenderTarget, URawAudioSoundWave* SoundBuffer, TUniqueFunction LoadedCallback) { check(IsInGameThread()); // So static initialization is safe + UObject access @@ -1006,7 +1006,7 @@ LibretroContext* LibretroContext::Launch(ULibretroCoreInstance* LibretroCoreInst static constexpr uint32 max_instances = sizeof(libretro_callbacks_table) / sizeof(libretro_callbacks_table[0]); static TBitArray> AllocatedInstances(false, max_instances); - LibretroContext *l = new LibretroContext(); + FLibretroContext *l = new FLibretroContext(); // Grab a statically generated callback structure int32 InstanceNumber; @@ -1210,7 +1210,7 @@ LibretroContext* LibretroContext::Launch(ULibretroCoreInstance* LibretroCoreInst return l; } -void LibretroContext::Shutdown(LibretroContext* Instance) +void FLibretroContext::Shutdown(FLibretroContext* Instance) { // We enqueue the shutdown procedure as the final task since we want outstanding tasks to be executed first Instance->EnqueueTask([Instance](auto&&) @@ -1219,7 +1219,7 @@ void LibretroContext::Shutdown(LibretroContext* Instance) }); } -void LibretroContext::Pause(bool ShouldPause) +void FLibretroContext::Pause(bool ShouldPause) { // We enqueue the state change because otherwise we might prematurely unset the Starting state // Alternatively you could add one more state to accomplish this, but this is fine for now @@ -1233,7 +1233,7 @@ void LibretroContext::Pause(bool ShouldPause) } -void LibretroContext::EnqueueTask(TUniqueFunction LibretroAPITask) +void FLibretroContext::EnqueueTask(TUniqueFunction LibretroAPITask) { check(IsInGameThread()); // LibretroAPITasks is a single producer single consumer queue LibretroAPITasks.Enqueue(MoveTemp(LibretroAPITask)); diff --git a/Source/UnrealLibretro/Private/sdlarch.h b/Source/UnrealLibretro/Private/sdlarch.h index 3fc9a358..b1a17d4b 100644 --- a/Source/UnrealLibretro/Private/sdlarch.h +++ b/Source/UnrealLibretro/Private/sdlarch.h @@ -98,18 +98,18 @@ struct libretro_api_t { retro_keyboard_event_t keyboard_event; }; -struct LibretroContext { +struct FLibretroContext { public: /** * @brief analogous to new except asynchronous * @post The LoadedCallback is always called */ - static LibretroContext* Launch(class ULibretroCoreInstance* LibretroCoreInstance, FString core, FString game, UTextureRenderTarget2D* RenderTarget, URawAudioSoundWave* SoundEmitter, TUniqueFunction LoadedCallback); + static FLibretroContext* Launch(class ULibretroCoreInstance* LibretroCoreInstance, FString core, FString game, UTextureRenderTarget2D* RenderTarget, URawAudioSoundWave* SoundEmitter, TUniqueFunction LoadedCallback); /** * @brief analogous to delete except asynchronous */ - static void Shutdown(LibretroContext* Instance); + static void Shutdown(FLibretroContext* Instance); /** * Queued tasks will still execute even if paused @@ -166,8 +166,8 @@ struct LibretroContext { EPixelFormat UnrealPixelFormat{PF_B8G8R8A8}; protected: - LibretroContext() {} - ~LibretroContext() {} + FLibretroContext() {} + ~FLibretroContext() {} libretro_api_t libretro_api = { 0 }; struct libretro_callbacks_t* libretro_callbacks = nullptr; diff --git a/Source/UnrealLibretro/Public/LibretroCoreInstance.h b/Source/UnrealLibretro/Public/LibretroCoreInstance.h index 70c626cf..b997ea22 100644 --- a/Source/UnrealLibretro/Public/LibretroCoreInstance.h +++ b/Source/UnrealLibretro/Public/LibretroCoreInstance.h @@ -228,7 +228,7 @@ class UNREALLIBRETRO_API ULibretroCoreInstance : public UActorComponent protected: // @todo: It'd be nice if I could use something like std::wrapped_reference however Unreal doesn't offer an equivalent for now - TOptional CoreInstance; + TOptional CoreInstance; bool Paused = false; From 459c2c14fabc161ebed8a1f53300f7fc1dac1e44 Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Sun, 4 Jun 2023 20:25:00 -0700 Subject: [PATCH 40/79] Make filename more appropriate --- .../UnrealLibretro/Private/{sdlarch.cpp => LibretroContext.cpp} | 2 +- Source/UnrealLibretro/Private/{sdlarch.h => LibretroContext.h} | 0 Source/UnrealLibretro/Private/LibretroCoreInstance.cpp | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename Source/UnrealLibretro/Private/{sdlarch.cpp => LibretroContext.cpp} (99%) rename Source/UnrealLibretro/Private/{sdlarch.h => LibretroContext.h} (100%) diff --git a/Source/UnrealLibretro/Private/sdlarch.cpp b/Source/UnrealLibretro/Private/LibretroContext.cpp similarity index 99% rename from Source/UnrealLibretro/Private/sdlarch.cpp rename to Source/UnrealLibretro/Private/LibretroContext.cpp index 41793a32..1d6676e0 100644 --- a/Source/UnrealLibretro/Private/sdlarch.cpp +++ b/Source/UnrealLibretro/Private/LibretroContext.cpp @@ -1,5 +1,5 @@ -#include "sdlarch.h" +#include "LibretroContext.h" extern "C" { #include "gfx/scaler/pixconv.h" diff --git a/Source/UnrealLibretro/Private/sdlarch.h b/Source/UnrealLibretro/Private/LibretroContext.h similarity index 100% rename from Source/UnrealLibretro/Private/sdlarch.h rename to Source/UnrealLibretro/Private/LibretroContext.h diff --git a/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp b/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp index d2e86892..e89e1fc5 100644 --- a/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp +++ b/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp @@ -10,7 +10,7 @@ #include "UnrealLibretro.h" #include "LibretroInputDefinitions.h" #include "RawAudioSoundWave.h" -#include "sdlarch.h" +#include "LibretroContext.h" #define NOT_LAUNCHED_GUARD if (!CoreInstance.IsSet()) return; From 139ca7e2031795abedcb395333bee1f6e1f03e29 Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Sun, 4 Jun 2023 20:23:36 -0700 Subject: [PATCH 41/79] Make variable name more appropriate --- Source/UnrealLibretro/Private/LibretroContext.h | 2 +- Source/UnrealLibretro/Private/LibretroCoreInstance.cpp | 4 ++-- Source/UnrealLibretro/Public/LibretroCoreInstance.h | 2 +- Source/UnrealLibretro/Public/LibretroInputDefinitions.h | 2 +- Source/UnrealLibretro/Public/UnrealLibretro.h | 6 +++--- .../Private/LibretroCoreInstanceDetails.cpp | 8 ++++---- .../Private/LibretroCoreInstanceDetails.h | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Source/UnrealLibretro/Private/LibretroContext.h b/Source/UnrealLibretro/Private/LibretroContext.h index b1a17d4b..507b3b2b 100644 --- a/Source/UnrealLibretro/Private/LibretroContext.h +++ b/Source/UnrealLibretro/Private/LibretroContext.h @@ -131,7 +131,7 @@ struct FLibretroContext { TMap StartingOptions; // The following are always safe to access from the game thread and are guranteed to be initialized after exiting the starting state - TArray OptionDescriptions; + TArray OptionDescriptions; TStaticArray, PortCount> ControllerDescriptions; decltype(retro_controller_description::id) DeviceIDs[PortCount]; // The core doesn't keep track of this so we have to diff --git a/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp b/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp index e89e1fc5..0eac0ea9 100644 --- a/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp +++ b/Source/UnrealLibretro/Private/LibretroCoreInstance.cpp @@ -55,9 +55,9 @@ void ULibretroCoreInstance::GetController(int Port, int64& ID, FString& Descript } } -TArray ULibretroCoreInstance::GetOptionDescriptions() +TArray ULibretroCoreInstance::GetOptionDescriptions() { - return CoreInstance.IsSet() ? CoreInstance.GetValue()->OptionDescriptions : TArray{}; + return CoreInstance.IsSet() ? CoreInstance.GetValue()->OptionDescriptions : TArray{}; } TArray ULibretroCoreInstance::GetControllerDescriptions(int Port) diff --git a/Source/UnrealLibretro/Public/LibretroCoreInstance.h b/Source/UnrealLibretro/Public/LibretroCoreInstance.h index b997ea22..704c54df 100644 --- a/Source/UnrealLibretro/Public/LibretroCoreInstance.h +++ b/Source/UnrealLibretro/Public/LibretroCoreInstance.h @@ -124,7 +124,7 @@ class UNREALLIBRETRO_API ULibretroCoreInstance : public UActorComponent * @see `RETRO_ENVIRONMENT_SET_VARIABLES` */ UFUNCTION(BlueprintPure, Category = "Libretro|IneffectiveBeforeLaunchComplete") - TArray GetOptionDescriptions(); + TArray GetOptionDescriptions(); UFUNCTION(BlueprintCallable, Category = "Libretro|IneffectiveBeforeLaunchComplete") void GetOption(const FString& Key, FString& Value, int &Index); diff --git a/Source/UnrealLibretro/Public/LibretroInputDefinitions.h b/Source/UnrealLibretro/Public/LibretroInputDefinitions.h index 2f550f68..f4e55785 100644 --- a/Source/UnrealLibretro/Public/LibretroInputDefinitions.h +++ b/Source/UnrealLibretro/Public/LibretroInputDefinitions.h @@ -16,7 +16,7 @@ #include "LibretroInputDefinitions.generated.h" USTRUCT(BlueprintType) -struct FLibretroOption +struct FLibretroOptionDescription { GENERATED_BODY() diff --git a/Source/UnrealLibretro/Public/UnrealLibretro.h b/Source/UnrealLibretro/Public/UnrealLibretro.h index 00acdb21..5e533563 100644 --- a/Source/UnrealLibretro/Public/UnrealLibretro.h +++ b/Source/UnrealLibretro/Public/UnrealLibretro.h @@ -133,9 +133,9 @@ class FUnrealLibretroModule : public IModuleInterface return ControllerDescriptions; } - static TArray EnvironmentParseOptions(const struct retro_variable* variable_array) + static TArray EnvironmentParseOptions(const struct retro_variable* variable_array) { - TArray ParsedOptions; + TArray ParsedOptions; while (variable_array->key != nullptr) { @@ -151,7 +151,7 @@ class FUnrealLibretroModule : public IModuleInterface continue; // Skip this Option } - FLibretroOption Option; + FLibretroOptionDescription Option; // Extract the description substring Option.Description = Value.Left(SemicolonIndex); diff --git a/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.cpp b/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.cpp index 27359af8..df338fa5 100644 --- a/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.cpp +++ b/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.cpp @@ -129,7 +129,7 @@ void FLibretroCoreInstanceDetails::StartBuildbotBatchDownload(TSharedPtr RefreshCoreListViews(); } -static TArray static_LibretroOptions; +static TArray static_LibretroOptions; static TStaticArray, PortCount> static_ControllerDescriptions; @@ -369,7 +369,7 @@ void FLibretroCoreInstanceDetails::CustomizeDetails(IDetailLayoutBuilder& Detail // Construct Libreto Core Options Panel... It's directly under the Libretro section IDetailCategoryBuilder& OptionsCategory = DetailBuilder.EditCategory("Libretro Core Options"); LibretroOptions = MoveTemp(static_LibretroOptions); - for (const FLibretroOption& Option : LibretroOptions) + for (const FLibretroOptionDescription& Option : LibretroOptions) { OptionsCategory.AddCustomRow(FText::GetEmpty()) #if ENGINE_MAJOR_VERSION >= 5 @@ -413,7 +413,7 @@ void FLibretroCoreInstanceDetails::CustomizeDetails(IDetailLayoutBuilder& Detail if (this->LibretroCoreInstance.IsValid()) { // If default is selected remove the key from core options since no key is implicit default - if (i == FLibretroOption::DefaultOptionIndex) + if (i == FLibretroOptionDescription::DefaultOptionIndex) { if (this->LibretroCoreInstance->EditorPresetOptions.Contains(Option.Key)) { @@ -445,7 +445,7 @@ void FLibretroCoreInstanceDetails::CustomizeDetails(IDetailLayoutBuilder& Detail FString* SelectedOptionString = this->LibretroCoreInstance->EditorPresetOptions.Find(Option.Key); if (SelectedOptionString == nullptr) { - return FText::FromString(Option.Values[FLibretroOption::DefaultOptionIndex]); + return FText::FromString(Option.Values[FLibretroOptionDescription::DefaultOptionIndex]); } else { diff --git a/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.h b/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.h index d711908b..355c4576 100644 --- a/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.h +++ b/Source/UnrealLibretroEditor/Private/LibretroCoreInstanceDetails.h @@ -51,7 +51,7 @@ class FLibretroCoreInstanceDetails : public IDetailCustomization /** IDetailCustomization interface */ virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override; - TArray LibretroOptions; + TArray LibretroOptions; TStaticArray, PortCount> ControllerDescriptions; TWeakObjectPtr LibretroCoreInstance; From 8f419ccf83a85b09afe05abbb7cfda8a7c723b3a Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Mon, 1 May 2023 20:07:48 -0700 Subject: [PATCH 42/79] Ignore line endings change in blame --- .git-blame-ignore-revs | 1 + 1 file changed, 1 insertion(+) create mode 100644 .git-blame-ignore-revs diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 00000000..2958c009 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1 @@ +239ac5549ea07fd966c58d5a58ac20d153597f65 \ No newline at end of file From 0af6e00cedd38da2e67cf3d11e3044842180eb4f Mon Sep 17 00:00:00 2001 From: John Rehbein Date: Tue, 20 Dec 2022 20:33:42 -0800 Subject: [PATCH 43/79] Commit Content --- Content/Blueprints/LibretroActorBase.uasset | Bin 0 -> 148596 bytes .../Blueprints/LibretroArcadeGunActor.uasset | Bin 0 -> 361516 bytes .../LibretroArcadeMachineActor.uasset | Bin 0 -> 260548 bytes Content/Blueprints/LibretroCharacter.uasset | Bin 0 -> 114422 bytes .../Blueprints/LibretroCoreMenuWidget.uasset | Bin 0 -> 108976 bytes .../Blueprints/LibretroFocusViewPawn.uasset | Bin 0 -> 373178 bytes Content/Blueprints/LibretroHUD.uasset | Bin 0 -> 57008 bytes Content/Blueprints/LibretroTVPawn.uasset | Bin 0 -> 120599 bytes .../LibretroVRTeleportVisualizer.uasset | Bin 0 -> 157310 bytes Content/Haptics/GrabHapticEffect.uasset | Bin 0 -> 1928 bytes Content/Haptics/PistolFireHapticEffect.uasset | Bin 0 -> 1952 bytes Content/LibretroMap.umap | Bin 0 -> 99299 bytes .../Functions/MF_OccludedPixels.uasset | Bin 0 -> 106475 bytes Content/Materials/M_ArcadeCabinet.uasset | Bin 0 -> 115783 bytes Content/Materials/M_BaseMaterial.uasset | Bin 0 -> 78662 bytes Content/Materials/M_Cube.uasset | Bin 0 -> 85132 bytes .../Materials/M_CubeMaterialOverride.uasset | Bin 0 -> 77528 bytes Content/Materials/M_GlowingScreen.uasset | Bin 0 -> 104036 bytes Content/Materials/M_SplineArcMat.uasset | Bin 0 -> 90649 bytes Content/Materials/M_TeleportCylinder.uasset | Bin 0 -> 105455 bytes Content/Materials/TV_90s.uasset | Bin 0 -> 111271 bytes Content/Mesh/1MeterCube.uasset | Bin 0 -> 84701 bytes Content/Mesh/ArcadeCabinet.uasset | Bin 0 -> 115974 bytes Content/Mesh/ArcadeGun.uasset | Bin 0 -> 102287 bytes Content/Mesh/ArcadeScreen.uasset | Bin 0 -> 86693 bytes Content/Mesh/BeamMesh.uasset | Bin 0 -> 83380 bytes Content/Mesh/TV_90s_Bezel.uasset | Bin 0 -> 91995 bytes Content/Mesh/TV_90s_TV_90s.uasset | Bin 0 -> 137175 bytes Content/Mesh/TV_90s_screen_mesh.uasset | Bin 0 -> 97334 bytes .../Textures/DefaultMaterial_baseColor.uasset | Bin 0 -> 3484434 bytes .../Textures/DefaultMaterial_emissive.uasset | Bin 0 -> 233763 bytes .../Textures/DefaultMaterial_normal.uasset | Bin 0 -> 5813914 bytes Content/Textures/TV_90s_Albedo.uasset | Bin 0 -> 1684292 bytes Content/Textures/TV_90s_Normal.uasset | Bin 0 -> 2817029 bytes setup.sh | 8 ++------ 35 files changed, 2 insertions(+), 6 deletions(-) create mode 100644 Content/Blueprints/LibretroActorBase.uasset create mode 100644 Content/Blueprints/LibretroArcadeGunActor.uasset create mode 100644 Content/Blueprints/LibretroArcadeMachineActor.uasset create mode 100644 Content/Blueprints/LibretroCharacter.uasset create mode 100644 Content/Blueprints/LibretroCoreMenuWidget.uasset create mode 100644 Content/Blueprints/LibretroFocusViewPawn.uasset create mode 100644 Content/Blueprints/LibretroHUD.uasset create mode 100644 Content/Blueprints/LibretroTVPawn.uasset create mode 100644 Content/Blueprints/LibretroVRTeleportVisualizer.uasset create mode 100644 Content/Haptics/GrabHapticEffect.uasset create mode 100644 Content/Haptics/PistolFireHapticEffect.uasset create mode 100644 Content/LibretroMap.umap create mode 100644 Content/Materials/Functions/MF_OccludedPixels.uasset create mode 100644 Content/Materials/M_ArcadeCabinet.uasset create mode 100644 Content/Materials/M_BaseMaterial.uasset create mode 100644 Content/Materials/M_Cube.uasset create mode 100644 Content/Materials/M_CubeMaterialOverride.uasset create mode 100644 Content/Materials/M_GlowingScreen.uasset create mode 100644 Content/Materials/M_SplineArcMat.uasset create mode 100644 Content/Materials/M_TeleportCylinder.uasset create mode 100644 Content/Materials/TV_90s.uasset create mode 100644 Content/Mesh/1MeterCube.uasset create mode 100644 Content/Mesh/ArcadeCabinet.uasset create mode 100644 Content/Mesh/ArcadeGun.uasset create mode 100644 Content/Mesh/ArcadeScreen.uasset create mode 100644 Content/Mesh/BeamMesh.uasset create mode 100644 Content/Mesh/TV_90s_Bezel.uasset create mode 100644 Content/Mesh/TV_90s_TV_90s.uasset create mode 100644 Content/Mesh/TV_90s_screen_mesh.uasset create mode 100644 Content/Textures/DefaultMaterial_baseColor.uasset create mode 100644 Content/Textures/DefaultMaterial_emissive.uasset create mode 100644 Content/Textures/DefaultMaterial_normal.uasset create mode 100644 Content/Textures/TV_90s_Albedo.uasset create mode 100644 Content/Textures/TV_90s_Normal.uasset diff --git a/Content/Blueprints/LibretroActorBase.uasset b/Content/Blueprints/LibretroActorBase.uasset new file mode 100644 index 0000000000000000000000000000000000000000..002dbe0942f28f25a5537a14882138b11d0f2be0 GIT binary patch literal 148596 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 K84l0tQTTsx8~i%} literal 0 HcmV?d00001 diff --git a/Content/Blueprints/LibretroArcadeGunActor.uasset b/Content/Blueprints/LibretroArcadeGunActor.uasset new file mode 100644 index 0000000000000000000000000000000000000000..05597e6c19eaa00aa005dc0239d771115560b345 GIT binary patch literal 361516 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

sJfKVJ+HV|gO5gwTn-_ZHz} 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

&Wk}GgMnAmYB4O$ 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 literal 0 HcmV?d00001 diff --git a/Content/Blueprints/LibretroCharacter.uasset b/Content/Blueprints/LibretroCharacter.uasset new file mode 100644 index 0000000000000000000000000000000000000000..a4d68066c910536e926e38052e51c80d7db00429 GIT binary patch literal 114422 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 E0~PV37ytkO literal 0 HcmV?d00001 diff --git a/Content/Blueprints/LibretroFocusViewPawn.uasset b/Content/Blueprints/LibretroFocusViewPawn.uasset new file mode 100644 index 0000000000000000000000000000000000000000..636af993603c3f44d5761c0896219692ead43a16 GIT binary patch literal 373178 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

lR{eGcu6yQdO-O_x%`_}H7- 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⪙FDGy)Nj2mNliJEOyVXh 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 ~vJ*>t6)2 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 PLFB@->6dX5zQKP0nI1TQ literal 0 HcmV?d00001 diff --git a/Content/Haptics/PistolFireHapticEffect.uasset b/Content/Haptics/PistolFireHapticEffect.uasset new file mode 100644 index 0000000000000000000000000000000000000000..bc7505cf8a96c031b5363b064713d91f4b0e0350 GIT binary patch literal 1952 zcmb7FOH30%7#OK2M$0?IB=4KCTRWtebfpq#V_6YXa2`G^UwSS9Sn&73&CrAE}oERCC-3trNV9Kw2wZ zZkyL$w!dcAaVQ%JAe_8_&ta3cFW}3 zhsBG|J0)Mv-T&};M)yeF;);PAvIK$wQ;5a+?pcUaNz23=>iuw^maj{;Cwngm4*GK)u6Xmdl`B`PrTqqg+;)&!d` z&?&;M-E!WnwV}E>pio|_6WbKZ6xnj7(Q=XxGEq=1eod}~5BPNA1WG$4dYL+|V!WYZ`0QpSstN@m70TK2`Y9Hs4TywGh}TQxpXa_{TH z-W5LjvJDr{7(axSbBcyl;;Md=vk?ZTVGeCEbA96j@w!^fdY6|2RzJ+ccT%FB=r7#= zcU&h6yd+-ZnJ5S9`5vkUd4ZCA7VgJ1ji5O5c07C(b>GQ0#@4VW0>7Zl89y&G>$ z+Y;Dt9f`oRNQFcjmOkP)aOgd~-EItWK^`GE7Oua?Yeqh7$SGRNE>iJ_=VE8CG#gLypV9`a~4!g3Kc&0INDF%>6I}UKn2Z zdL#wjV1yYE)`tYkFe&hu5s;q-zadQmEo;-sRAQ{ z2v~E9-9~^pqgH~;Q#@ukrBAW1#&As((aFhbLROm0tW!zkFl{Cu770EUh$8sX{$ozS GfAb$313(=B literal 0 HcmV?d00001 diff --git a/Content/LibretroMap.umap b/Content/LibretroMap.umap new file mode 100644 index 0000000000000000000000000000000000000000..7ff95989fb1127058eb5eb8fed967a494ab0b3e1 GIT binary patch literal 99299 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 literal 0 HcmV?d00001 diff --git a/Content/Materials/Functions/MF_OccludedPixels.uasset b/Content/Materials/Functions/MF_OccludedPixels.uasset new file mode 100644 index 0000000000000000000000000000000000000000..a9a2f33bd7b82ca14cf2c4589a04dcea6682c29e GIT binary patch literal 106475 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%0DKB-_sPZb!x!}MY13gAliW0yvO5vaN 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 BB%S~O literal 0 HcmV?d00001 diff --git a/Content/Materials/M_ArcadeCabinet.uasset b/Content/Materials/M_ArcadeCabinet.uasset new file mode 100644 index 0000000000000000000000000000000000000000..f3e2e1486906417013e59f9d118d3edb03a8eaba GIT binary patch literal 115783 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

dhTaJG$_w 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