mirror of
https://github.com/visualboyadvance-m/visualboyadvance-m
synced 2025-10-05 23:52:49 +02:00
Compare commits
203 Commits
v2.2.0
...
043956753b
Author | SHA1 | Date | |
---|---|---|---|
|
043956753b | ||
|
e0eb3b3dab | ||
|
e3e14232f7 | ||
|
7565cac230 | ||
|
33c5aeb80e | ||
|
c5510ba28f | ||
|
96c23628ba | ||
|
37fb449fc9 | ||
|
7519c9b818 | ||
|
24b779c09e | ||
|
a17df26e52 | ||
|
b2dd03c6cb | ||
|
967426e2f0 | ||
|
ee2678c13c | ||
|
337e465741 | ||
|
5007977189 | ||
|
99f97138a2 | ||
|
5b867c1483 | ||
|
fe08f4d326 | ||
|
c334e3ffca | ||
|
35f8ba0b8f | ||
|
fca5fae329 | ||
|
56ea6456f5 | ||
|
0510656ca3 | ||
|
b92fcf7e31 | ||
|
22f381a19e | ||
|
e9fc6c8e13 | ||
|
bd20c79013 | ||
|
203be42665 | ||
|
d75072009b | ||
|
0e32b90002 | ||
|
2640716496 | ||
|
b065db66d9 | ||
|
4d837fd739 | ||
|
1ef356c6f0 | ||
|
a8c82bb4a1 | ||
|
1778d605f4 | ||
|
d6d2a83e7e | ||
|
881667a5f8 | ||
|
b332aa693d | ||
|
7f7035a07b | ||
|
3e355509e9 | ||
|
e68681ffda | ||
|
f83aa940d7 | ||
|
a18559678c | ||
|
d927594af2 | ||
|
06507775e8 | ||
|
b08ecd123d | ||
|
d154a6abc8 | ||
|
b134bacc45 | ||
|
9ae1473f36 | ||
|
9cd0c5c04c | ||
|
9e41c5476a | ||
|
e91171459d | ||
|
16f008b448 | ||
|
bad10342bd | ||
|
06b3cb094e | ||
|
82617ea75d | ||
|
0561ef91a4 | ||
|
afc6a1cc3a | ||
|
35df9d7647 | ||
|
031ae2ebf6 | ||
|
6242679e23 | ||
|
537393a0ab | ||
|
616f7abc4e | ||
|
f1414000e6 | ||
|
e2a235768e | ||
|
1d54ee2135 | ||
|
013a9b87ac | ||
|
a06d3ed1b4 | ||
|
b1bad9d53d | ||
|
d2eed85c28 | ||
|
d5a37e06aa | ||
|
cea049161d | ||
|
3eadc6e78f | ||
|
4f3ed0dc8b | ||
|
d38e1d0f89 | ||
|
f870bf252d | ||
|
536ea705ab | ||
|
802162ad97 | ||
|
25895e8636 | ||
|
27e7aa6864 | ||
|
dd7461103b | ||
|
c892c269fa | ||
|
dcf87c0a71 | ||
|
6d1e7e8069 | ||
|
b8868fce37 | ||
|
d92c4e465f | ||
|
bceb138961 | ||
|
83aa80440e | ||
|
4bd54ba128 | ||
|
27acf80b80 | ||
|
ae09ab7189 | ||
|
ce8de563e3 | ||
|
47a8e7e8ed | ||
|
adbaeacf20 | ||
|
8d8ecd8dfa | ||
|
cfbfc9ecb0 | ||
|
4445a20d9c | ||
|
b83a7c8aa2 | ||
|
66aabab61f | ||
|
a5e6b57fd8 | ||
|
b9061ddafd | ||
|
3c291ef3ac | ||
|
e44c83e1d0 | ||
|
8ce4848dd6 | ||
|
34cba3bb2c | ||
|
9b78b26010 | ||
|
94f9df4baa | ||
|
c726989d67 | ||
|
3344e7cf71 | ||
|
3d69bde8cb | ||
|
09984eed2f | ||
|
69809ff556 | ||
|
80dec6a38d | ||
|
ea7b9e5049 | ||
|
18701fc0a4 | ||
|
6b596c7ad7 | ||
|
3c01643112 | ||
|
0895c7a52e | ||
|
1e52787078 | ||
|
fc0e63d554 | ||
|
9b573f4465 | ||
|
51ae605850 | ||
|
ac4b39b6d8 | ||
|
97ca69d050 | ||
|
b1bcaed3a2 | ||
|
2a1b41040d | ||
|
930d0e282c | ||
|
df23826721 | ||
|
de46284747 | ||
|
715f685447 | ||
|
eabf319c9a | ||
|
c98263603d | ||
|
9d46f8e6d1 | ||
|
dab91fc8ca | ||
|
d672d425be | ||
|
53d60b726d | ||
|
b6ce89bff8 | ||
|
8c3d0f0984 | ||
|
3d4c03d0e6 | ||
|
92230e0003 | ||
|
88d58db0e9 | ||
|
4d4c06b26b | ||
|
23a3d0b20f | ||
|
53f5ec7b7d | ||
|
61f93348ca | ||
|
9a78ecbab3 | ||
|
3ffbe7816a | ||
|
2064af05b4 | ||
|
ae8bfb4ab3 | ||
|
1f3a1cb9dc | ||
|
bbaf70c083 | ||
|
b96e0ad35c | ||
|
96324ddb92 | ||
|
dca119905a | ||
|
6a702ad09b | ||
|
796ecee8cf | ||
|
c1d3d19d6b | ||
|
f7a79bfe2e | ||
|
f8c52377a3 | ||
|
357eccc6eb | ||
|
80f595c0b0 | ||
|
1c73f0c7d3 | ||
|
3aff3784cf | ||
|
59fa419820 | ||
|
b0820a28b6 | ||
|
a782c1c5fe | ||
|
16a73fcbcf | ||
|
9ff3b9e3b7 | ||
|
379be68f69 | ||
|
8bfb39b3ad | ||
|
d97c8eabf5 | ||
|
e42e6323e8 | ||
|
d0463ddb5e | ||
|
1da359ba22 | ||
|
15612c2bcc | ||
|
11a941f33d | ||
|
a808cfb6f5 | ||
|
0266b9e4d1 | ||
|
ef01f4c491 | ||
|
d784b60aaa | ||
|
67b17f50ec | ||
|
be6addf034 | ||
|
d9b58d683e | ||
|
604f5412a6 | ||
|
de64dfd9e9 | ||
|
024ae098e6 | ||
|
3d7cce05aa | ||
|
205459f7ae | ||
|
3ac5aa4f27 | ||
|
6fedbdc512 | ||
|
996a827d8e | ||
|
0c91d45627 | ||
|
3cf6ac5a80 | ||
|
c5d1862e4e | ||
|
c63469ac63 | ||
|
3e0541c639 | ||
|
00e48e11e6 | ||
|
07c6e3119e | ||
|
3d8ed512cf | ||
|
e49cce5b9e | ||
|
1dfd7ec121 |
2
.github/workflows/macos-build.yml
vendored
2
.github/workflows/macos-build.yml
vendored
@@ -36,7 +36,7 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install nix
|
||||
uses: cachix/install-nix-action@v22
|
||||
uses: cachix/install-nix-action@v31
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
|
||||
|
27
CHANGELOG.md
27
CHANGELOG.md
@@ -4,6 +4,33 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [2.2.2] - 2025-07-30
|
||||
|
||||
========================
|
||||
* 27acf80b - Make language configurable (requires restart) [andyvand]
|
||||
* ae09ab71 - Embed translations for Windows [andyvand]
|
||||
* 47a8e7e8 - Update wxWidgets for Mac builder to 3.3.1 [andyvand]
|
||||
* 8ce4848d - Fix Wii U VC opcodes [andyvand]
|
||||
* dab91fc8 - Fix bigger than 32MB ROM files [andyvand]
|
||||
|
||||
## [2.2.1] - 2025-07-19
|
||||
=======================
|
||||
* 23a3d0b2 - build: fix include paths for bundled SFML [rkitover]
|
||||
* 53f5ec7b - build: fix building with OpenAL enabled [rkitover]
|
||||
* 3ffbe781 - translations: remove ja_JP, 0% and ja exists [rkitover]
|
||||
* ae8bfb4a - Several fixes for windows [danialhorton]
|
||||
* bbaf70c0 - build: fix slow CMake vcpkg startup time [rkitover]
|
||||
* dca11990 - Fix GBA sound [andyvand]
|
||||
* 357eccc6 - build: fix checking if bin pkg host deps installed [rkitover]
|
||||
* d0463ddb - Initial tar support [andyvand]
|
||||
* a808cfb6 - Update macOS plist [andyvand]
|
||||
* 604f5412 - Add LZIP support [andyvand]
|
||||
* 6fedbdc5 - Add .xz support [andyvand]
|
||||
* 0c91d456 - Add .bz2 file support [andyvand]
|
||||
* 3e0541c6 - Update unrar to V7.1.8 [andyvand]
|
||||
* 00e48e11 - Fix RAR support [andyvand]
|
||||
* 3d8ed512 - Use macOS 10.10 Sparkle [andyvand]
|
||||
|
||||
## [2.2.0] - 2025-07-08
|
||||
========================
|
||||
* 865add06 - Adjust throttle limit 450 -> 1000 [rkitover]
|
||||
|
@@ -1,6 +1,17 @@
|
||||
cmake_minimum_required(VERSION 3.19)
|
||||
cmake_policy(VERSION 3.19...3.28.3)
|
||||
|
||||
# Use new link library de-duplication behavior.
|
||||
if(POLICY CMP0156)
|
||||
cmake_policy(SET CMP0156 NEW)
|
||||
endif()
|
||||
if(POLICY CMP0179)
|
||||
cmake_policy(SET CMP0179 NEW)
|
||||
endif()
|
||||
#if(POLICY CMP0181)
|
||||
# cmake_policy(SET CMP0181 NEW)
|
||||
#endif()
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
|
||||
|
||||
if(WIN32)
|
||||
@@ -24,7 +35,7 @@ endif()
|
||||
|
||||
option(VCPKG_BINARY_PACKAGES "Use vcpkg binary packages" TRUE)
|
||||
|
||||
set(VCPKG_DEPS pkgconf zlib pthreads gettext-libintl wxwidgets nanosvg)
|
||||
set(VCPKG_DEPS zlib bzip2 "liblzma[tools]" pthreads gettext-libintl wxwidgets nanosvg)
|
||||
|
||||
set(VCPKG_DEPS_OPTIONAL
|
||||
"sdl3[vulkan]" ENABLE_SDL3
|
||||
@@ -83,7 +94,16 @@ if(APPLE)
|
||||
include(CheckLanguage)
|
||||
include(MetalShaderSupport)
|
||||
|
||||
check_language(Metal)
|
||||
execute_process(
|
||||
COMMAND xcrun -f metal
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
OUTPUT_VARIABLE CMAKE_Metal_COMPILER
|
||||
)
|
||||
|
||||
if(NOT CMAKE_Metal_COMPILER)
|
||||
check_language(Metal)
|
||||
endif()
|
||||
|
||||
if(CMAKE_Metal_COMPILER)
|
||||
enable_language(Metal)
|
||||
endif()
|
||||
|
@@ -31,6 +31,15 @@ elseif(VCPKG_TARGET_TRIPLET MATCHES "^[aA][rR][mM]-")
|
||||
set(CMAKE_SYSTEM_PROCESSOR ARM)
|
||||
endif()
|
||||
|
||||
if(APPLE AND
|
||||
(CMAKE_OSX_ARCHITECTURES MATCHES "[xX]86_64") OR
|
||||
(ENV{CFLAGS} MATCHES "[xX]86_64") OR
|
||||
(ENV{CXXFLAGS} MATCHES "[xX]86_64") OR
|
||||
(ENV{LDFLAGS} MATCHES "[xX]86_64"))
|
||||
|
||||
set(CMAKE_SYSTEM_PROCESSOR "x86_64")
|
||||
endif()
|
||||
|
||||
# Turn asm on by default on 32bit x86 and set WINARCH for windows stuff.
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "[xX]86|i[3-9]86|[aA][mM][dD]64")
|
||||
if(CMAKE_C_SIZEOF_DATA_PTR EQUAL 4) # 32 bit
|
||||
@@ -46,14 +55,6 @@ if(CMAKE_SYSTEM_PROCESSOR MATCHES "[xX]86|i[3-9]86|[aA][mM][dD]64")
|
||||
set(WINARCH x64)
|
||||
set(ARCH_NAME x86_64)
|
||||
endif()
|
||||
|
||||
if(DEFINED VCPKG_TARGET_TRIPLET)
|
||||
string(REGEX MATCH "^x[86][64]" target_arch ${VCPKG_TARGET_TRIPLET})
|
||||
|
||||
if(NOT WINARCH STREQUAL target_arch)
|
||||
message(FATAL_ERROR "Wrong build environment architecture for VCPKG_TARGET_TRIPLET, you specified ${target_arch} but your compiler is for ${WINARCH}")
|
||||
endif()
|
||||
endif()
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "[aA][aA][rR][cC][hH]|[aA][rR][mM]")
|
||||
if(CMAKE_C_SIZEOF_DATA_PTR EQUAL 4) # 32 bit
|
||||
set(ARM32 ON)
|
||||
@@ -70,6 +71,14 @@ elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "[aA][aA][rR][cC][hH]|[aA][rR][mM]")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(DEFINED VCPKG_TARGET_TRIPLET)
|
||||
string(REGEX MATCH "^[^-]+" target_arch ${VCPKG_TARGET_TRIPLET})
|
||||
|
||||
if(NOT WINARCH STREQUAL target_arch)
|
||||
message(FATAL_ERROR "Wrong build environment architecture for VCPKG_TARGET_TRIPLET, you specified ${target_arch} but your compiler is for ${WINARCH}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# We do not support amd64 asm yet
|
||||
if(X86_64 AND (ENABLE_ASM_CORE OR ENABLE_ASM_SCALERS OR ENABLE_MMX))
|
||||
message(FATAL_ERROR "The options ASM_CORE, ASM_SCALERS and MMX are not supported on X86_64 yet.")
|
||||
|
@@ -48,16 +48,20 @@ if((NOT ENABLE_SDL3) AND CMAKE_TOOLCHAIN_FILE MATCHES "vcpkg")
|
||||
endif()
|
||||
|
||||
if(ENABLE_SDL3)
|
||||
if(VBAM_STATIC)
|
||||
set(VBAM_SDL_LIBS SDL3::SDL3-static ${SDL_LIBRARY_TEMP})
|
||||
if(UNIX AND NOT APPLE)
|
||||
set(VBAM_SDL_LIBS "${SDL3_LIBRARIES}")
|
||||
else()
|
||||
set(VBAM_SDL_LIBS SDL3::SDL3 ${SDL_LIBRARY_TEMP})
|
||||
if(VBAM_STATIC)
|
||||
set(VBAM_SDL_LIBS SDL3::SDL3-static ${SDL_LIBRARY_TEMP})
|
||||
else()
|
||||
set(VBAM_SDL_LIBS SDL3::SDL3 ${SDL_LIBRARY_TEMP})
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
if(VBAM_STATIC)
|
||||
set(VBAM_SDL_LIBS SDL2::SDL2-static ${SDL_LIBRARY_TEMP})
|
||||
set(VBAM_SDL_LIBS SDL2::SDL2-static ${SDL_LIBRARY_TEMP})
|
||||
else()
|
||||
set(VBAM_SDL_LIBS SDL2::SDL2 ${SDL_LIBRARY_TEMP})
|
||||
set(VBAM_SDL_LIBS SDL2::SDL2 ${SDL_LIBRARY_TEMP})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -70,7 +74,7 @@ if(ENABLE_FFMPEG)
|
||||
list(APPEND FFMPEG_LDFLAGS "SHELL:-framework CoreText" "SHELL:-framework ApplicationServices")
|
||||
|
||||
if(UPSTREAM_RELEASE)
|
||||
list(APPEND FFMPEG_LDFLAGS -lbz2 -ltiff "SHELL:-framework DiskArbitration" -lfreetype -lfontconfig -llzma -lxml2 -lharfbuzz)
|
||||
list(APPEND FFMPEG_LDFLAGS -lbz2 -ltiff "SHELL:-framework DiskArbitration" -lfreetype -lfontconfig -llzma -lxml2 -lharfbuzz -lcrypto -lssl)
|
||||
endif()
|
||||
elseif(WIN32)
|
||||
set(WIN32_MEDIA_FOUNDATION_LIBS dxva2 evr mf mfplat mfplay mfreadwrite mfuuid amstrmid)
|
||||
|
@@ -221,7 +221,7 @@ release notes.
|
||||
|
||||
Run the following commands to commit the change:
|
||||
|
||||
git commit -m'release ${new_tag}' --signoff -S
|
||||
git commit -a -m'release ${new_tag}' --signoff -S
|
||||
git tag -s -m'${new_tag}' ${new_tag}
|
||||
|
||||
To rollback these changes, run this command:
|
||||
|
@@ -40,14 +40,28 @@ if(VBAM_STATIC)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_package(SDL3 QUIET)
|
||||
if(CMAKE_TOOLCHAIN_FILE MATCHES "vcpkg" AND CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "^([xX]86_64|[aA][mM][dD]64)$")
|
||||
set(PKG_CONFIG_EXECUTABLE "$ENV{VCPKG_ROOT}/installed/x64-windows/tools/pkgconf/pkgconf.exe")
|
||||
endif()
|
||||
|
||||
find_package(PkgConfig)
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
pkg_check_modules(SDL3 sdl3 QUIET)
|
||||
else()
|
||||
find_package(SDL3 QUIET)
|
||||
endif()
|
||||
|
||||
option(ENABLE_SDL3 "Use SDL3" "${SDL3_FOUND}")
|
||||
|
||||
if(ENABLE_SDL3)
|
||||
find_package(SDL3 CONFIG REQUIRED)
|
||||
else()
|
||||
find_package(SDL2 CONFIG REQUIRED)
|
||||
if(NOT TRANSLATIONS_ONLY)
|
||||
if(ENABLE_SDL3)
|
||||
if(NOT UNIX)
|
||||
find_package(SDL3 REQUIRED)
|
||||
endif()
|
||||
else()
|
||||
find_package(SDL2 REQUIRED)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
option(ENABLE_GENERIC_FILE_DIALOGS "Use generic file dialogs" OFF)
|
||||
@@ -56,6 +70,8 @@ option(ENABLE_SDL "Build the SDL port" ${ENABLE_SDL_DEFAULT})
|
||||
option(ENABLE_WX "Build the wxWidgets port" ${BUILD_DEFAULT})
|
||||
option(ENABLE_DEBUGGER "Enable the debugger" ON)
|
||||
option(ENABLE_ASAN "Enable -fsanitize=address by default. Requires debug build with GCC/Clang" OFF)
|
||||
option(ENABLE_BZ2 "Enable BZ2 archive support" ON)
|
||||
option(ENABLE_LZMA "Enable LZMA archive support" ON)
|
||||
|
||||
if(ENABLE_SDL3)
|
||||
set(CMAKE_C_FLAGS "-DENABLE_SDL3 ${CMAKE_C_FLAGS}")
|
||||
@@ -89,12 +105,6 @@ if(APPLE AND NOT DISABLE_MACOS_PACKAGE_MANAGERS)
|
||||
include(MacPackageManagers)
|
||||
endif()
|
||||
|
||||
if(CMAKE_TOOLCHAIN_FILE MATCHES "vcpkg" AND CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "^([xX]86_64|[aA][mM][dD]64)$")
|
||||
set(PKG_CONFIG_EXECUTABLE "$ENV{VCPKG_ROOT}/installed/x64-windows/tools/pkgconf/pkgconf.exe")
|
||||
endif()
|
||||
|
||||
find_package(PkgConfig)
|
||||
|
||||
# Link / SFML
|
||||
if(NOT TRANSLATIONS_ONLY)
|
||||
set(ENABLE_LINK_DEFAULT ON)
|
||||
@@ -106,31 +116,25 @@ set(FFMPEG_DEFAULT OFF)
|
||||
set(FFMPEG_COMPONENTS AVFORMAT AVCODEC SWSCALE AVUTIL SWRESAMPLE X264 X265)
|
||||
set(FFMPEG_COMPONENT_VERSIONS AVFORMAT>=58.12.100 AVCODEC>=58.18.100 SWSCALE>=5.1.100 AVUTIL>=56.14.100 SWRESAMPLE>=3.1.100 X264>=0 X265>=0)
|
||||
|
||||
option(FIND_FFMPEG_APPLE "Find using Apple FFMPEG function" OFF)
|
||||
|
||||
if(NOT TRANSLATIONS_ONLY AND (NOT DEFINED ENABLE_FFMPEG OR ENABLE_FFMPEG))
|
||||
set(FFMPEG_DEFAULT ON)
|
||||
|
||||
if (APPLE OR FIND_FFMPEG_APPLE)
|
||||
find_package(AppleFFMPEG COMPONENTS ${FFMPEG_COMPONENTS})
|
||||
else()
|
||||
find_package(FFMPEG COMPONENTS ${FFMPEG_COMPONENTS})
|
||||
endif()
|
||||
find_package(FFmpeg COMPONENTS ${FFMPEG_COMPONENTS})
|
||||
|
||||
# check versions, but only if pkgconfig is available
|
||||
if(FFMPEG_FOUND AND PKG_CONFIG_FOUND AND NOT CMAKE_TOOLCHAIN_FILE MATCHES vcpkg)
|
||||
if(FFmpeg_FOUND AND PKG_CONFIG_FOUND AND NOT CMAKE_TOOLCHAIN_FILE MATCHES vcpkg)
|
||||
foreach(component ${FFMPEG_COMPONENT_VERSIONS})
|
||||
string(REPLACE ">=" ";" parts ${component})
|
||||
list(GET parts 0 name)
|
||||
list(GET parts 1 version)
|
||||
|
||||
if((NOT DEFINED ${name}_VERSION) OR ${name}_VERSION VERSION_LESS ${version})
|
||||
set(FFMPEG_FOUND OFF)
|
||||
set(FFmpeg_FOUND OFF)
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if(NOT FFMPEG_FOUND)
|
||||
if(NOT FFmpeg_FOUND)
|
||||
set(FFMPEG_DEFAULT OFF)
|
||||
endif()
|
||||
endif()
|
||||
|
@@ -159,7 +159,7 @@ function(vcpkg_is_installed vcpkg_exe pkg_name pkg_ver pkg_triplet powershell ou
|
||||
|
||||
string(REGEX REPLACE "\r?\n" ";" vcpkg_list_raw "${vcpkg_list_text}")
|
||||
|
||||
set(VCPKG_INSTALLED_COUNT 0 PARENT_SCOPE)
|
||||
set(VCPKG_INSTALLED_COUNT 0 CACHE INTERNAL "Number of installed vcpkg packages" FORCE)
|
||||
foreach(pkg ${vcpkg_list_raw})
|
||||
if(NOT pkg MATCHES "^([^:[]+)[^:]*:([^ ]+) +([0-9][^ ]*) +.*\$")
|
||||
continue()
|
||||
@@ -181,8 +181,8 @@ function(vcpkg_is_installed vcpkg_exe pkg_name pkg_ver pkg_triplet powershell ou
|
||||
list(APPEND VCPKG_INSTALLED ${inst_pkg_name} ${inst_pkg_ver} ${inst_pkg_rev} ${inst_pkg_triplet})
|
||||
math(EXPR VCPKG_INSTALLED_COUNT "${VCPKG_INSTALLED_COUNT} + 1")
|
||||
endforeach()
|
||||
set(VCPKG_INSTALLED ${VCPKG_INSTALLED} PARENT_SCOPE)
|
||||
set(VCPKG_INSTALLED_COUNT ${VCPKG_INSTALLED_COUNT} PARENT_SCOPE)
|
||||
set(VCPKG_INSTALLED ${VCPKG_INSTALLED} CACHE INTERNAL "List of installed vcpkg packages" FORCE)
|
||||
set(VCPKG_INSTALLED_COUNT ${VCPKG_INSTALLED_COUNT} CACHE INTERNAL "Number of installed vcpkg packages" FORCE)
|
||||
endif()
|
||||
|
||||
if(NOT VCPKG_INSTALLED_COUNT GREATER 0)
|
||||
@@ -267,6 +267,13 @@ function(zip_is_installed vcpkg_exe zip outvar)
|
||||
set(${outvar} ${pkg_installed} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cleanup_binary_packages)
|
||||
file(REMOVE_RECURSE "${CMAKE_BINARY_DIR}/vcpkg-binary-packages")
|
||||
|
||||
unset(VCPKG_INSTALLED CACHE)
|
||||
unset(VCPKG_INSTALLED_COUNT CACHE)
|
||||
endfunction()
|
||||
|
||||
function(get_binary_packages vcpkg_exe)
|
||||
set(binary_packages_installed FALSE PARENT_SCOPE)
|
||||
|
||||
@@ -318,6 +325,8 @@ function(get_binary_packages vcpkg_exe)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
unset(installed_host_deps)
|
||||
|
||||
while(TRUE)
|
||||
# -command "import-module ($env:USERPROFILE + '/source/repos/vcpkg-binpkg-prototype/vcpkg-binpkg.psm1'); vcpkg-listmissing ."
|
||||
execute_process(
|
||||
@@ -334,9 +343,13 @@ function(get_binary_packages vcpkg_exe)
|
||||
return()
|
||||
endif()
|
||||
|
||||
string(REGEX REPLACE "\r?\n" ";" host_deps "${host_deps}")
|
||||
string(REGEX REPLACE "\r?\n" ";" host_deps "${host_deps}")
|
||||
string(REGEX REPLACE " *;+ *$" "" host_deps "${host_deps}")
|
||||
|
||||
if(NOT host_deps)
|
||||
list(LENGTH host_deps host_deps_count)
|
||||
list(LENGTH installed_host_deps installed_host_deps_count)
|
||||
|
||||
if(host_deps_count EQUAL installed_host_deps_count)
|
||||
break()
|
||||
endif()
|
||||
|
||||
@@ -361,15 +374,21 @@ function(get_binary_packages vcpkg_exe)
|
||||
|
||||
string(REGEX REPLACE "<a href=\"([^\"]+[.]zip)\"" "\\1" pkg ${links})
|
||||
|
||||
zip_is_installed(${vcpkg_exe} ${pkg} pkg_installed)
|
||||
list(FIND installed_host_deps "${pkg}" found_idx)
|
||||
|
||||
if(NOT pkg_installed)
|
||||
download_package("${pkg}" "${bin_pkgs_dir}")
|
||||
endif()
|
||||
if(found_idx EQUAL -1)
|
||||
zip_is_installed(${vcpkg_exe} ${pkg} pkg_installed)
|
||||
|
||||
if(NOT EXISTS "${bin_pkgs_dir}/${pkg}")
|
||||
message(STATUS "Failed to download host dependency package '${pkg}', aborting.")
|
||||
return()
|
||||
if(NOT pkg_installed)
|
||||
download_package("${pkg}" "${bin_pkgs_dir}")
|
||||
|
||||
if(NOT EXISTS "${bin_pkgs_dir}/${pkg}")
|
||||
message(STATUS "Failed to download host dependency package '${pkg}', aborting.")
|
||||
return()
|
||||
endif()
|
||||
else()
|
||||
list(APPEND installed_host_deps "${pkg}")
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
endwhile()
|
||||
@@ -384,6 +403,8 @@ function(get_binary_packages vcpkg_exe)
|
||||
file(REMOVE_RECURSE ${bin_pkgs_dir})
|
||||
endif()
|
||||
|
||||
cleanup_binary_packages()
|
||||
|
||||
set(binary_packages_installed TRUE PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
@@ -554,12 +575,6 @@ function(vcpkg_set_toolchain)
|
||||
set(vcpkg_exe "${VCPKG_ROOT}/vcpkg")
|
||||
endif()
|
||||
|
||||
# update portfiles
|
||||
execute_process(
|
||||
COMMAND ${vcpkg_exe} update
|
||||
WORKING_DIRECTORY ${VCPKG_ROOT}
|
||||
)
|
||||
|
||||
if (NOT (NO_VCPKG_UPDATES OR (NOT VCPKG_BINARY_PACKAGES)))
|
||||
get_binary_packages(${vcpkg_exe})
|
||||
endif()
|
||||
|
@@ -37,8 +37,48 @@ function(check_clean_exit var)
|
||||
set(${var} ${exit_status} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(try_wx_util var util conf_suffix major_version minor_version)
|
||||
unset(suffix)
|
||||
if(conf_suffix)
|
||||
set(suffix "-${conf_suffix}")
|
||||
endif()
|
||||
if(major_version)
|
||||
set(suffix "${suffix}-${major_version}")
|
||||
|
||||
if(NOT minor_version EQUAL -1)
|
||||
set(suffix "${suffix}.${minor_version}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# find_program caches the result
|
||||
set(exe NOTFOUND CACHE INTERNAL "" FORCE)
|
||||
find_program(exe NAMES "${util}${suffix}")
|
||||
|
||||
# try infix variant, as on FreeBSD
|
||||
if(NOT EXISTS "${exe}")
|
||||
string(REGEX REPLACE "^-" "" suffix "${suffix}")
|
||||
|
||||
string(REGEX REPLACE "-" "${suffix}-" try "${util}")
|
||||
|
||||
set(exe NOTFOUND CACHE INTERNAL "" FORCE)
|
||||
find_program(exe NAMES "${try}")
|
||||
endif()
|
||||
|
||||
if(EXISTS "${exe}")
|
||||
# check that the utility can be executed cleanly
|
||||
# in case we find e.g. the wrong architecture binary
|
||||
# when cross-compiling
|
||||
check_clean_exit(exit_status "${exe}" --help)
|
||||
|
||||
if(exit_status EQUAL 0)
|
||||
set("${var}" "${exe}" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(find_wx_util var util)
|
||||
if(WIN32 OR EXISTS /etc/gentoo-release)
|
||||
if((WIN32 AND (NOT CMAKE_TOOLCHAIN_FILE MATCHES "vcpkg")) OR EXISTS /etc/gentoo-release)
|
||||
# On win32, including cross builds we prefer the plain utility
|
||||
# name first from PATH, with the exception of -static for static
|
||||
# builds.
|
||||
@@ -57,48 +97,34 @@ function(find_wx_util var util)
|
||||
set(major_versions ";")
|
||||
endif()
|
||||
|
||||
list(APPEND conf_suffixes gtk4u gtk4 gtk3u gtk3 gtk2u gtk2 "")
|
||||
list(APPEND major_versions 4 3 2 "")
|
||||
list(APPEND conf_suffixes "" gtk3u gtk3 gtk2u gtk2)
|
||||
list(APPEND major_versions "" 3)
|
||||
|
||||
get_target_property(wx_base_lib_prop wx::base LOCATION)
|
||||
string(STRIP "${wx_base_lib_prop}" wx_base_lib)
|
||||
|
||||
if(wx_base_lib MATCHES "wx_baseu?-([0-9]+)\\.([0-9]+)\\.")
|
||||
set(lib_major "${CMAKE_MATCH_1}")
|
||||
set(lib_minor "${CMAKE_MATCH_2}")
|
||||
endif()
|
||||
|
||||
foreach(conf_suffix IN LISTS conf_suffixes)
|
||||
if(lib_major AND lib_minor)
|
||||
try_wx_util(exe "${util}" "${conf_suffix}" "${lib_major}" "${lib_minor}")
|
||||
|
||||
if(exe)
|
||||
set("${var}" "${exe}" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
foreach(major_version IN LISTS major_versions)
|
||||
foreach(minor_version RANGE 100 -1 -1)
|
||||
unset(suffix)
|
||||
if(conf_suffix)
|
||||
set(suffix "-${conf_suffix}")
|
||||
endif()
|
||||
if(major_version)
|
||||
set(suffix "${suffix}-${major_version}")
|
||||
foreach(minor_version RANGE 30 -1 -1)
|
||||
try_wx_util(exe "${util}" "${conf_suffix}" "${major_version}" "${minor_version}")
|
||||
|
||||
if(NOT minor_version EQUAL -1)
|
||||
set(suffix "${suffix}.${minor_version}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# find_program caches the result
|
||||
set(exe NOTFOUND CACHE INTERNAL "" FORCE)
|
||||
find_program(exe NAMES "${util}${suffix}")
|
||||
|
||||
# try infix variant, as on FreeBSD
|
||||
if(NOT EXISTS ${exe})
|
||||
string(REGEX REPLACE "^-" "" suffix "${suffix}")
|
||||
|
||||
string(REGEX REPLACE "-" "${suffix}-" try ${util})
|
||||
|
||||
set(exe NOTFOUND CACHE INTERNAL "" FORCE)
|
||||
find_program(exe NAMES ${try})
|
||||
endif()
|
||||
|
||||
if(EXISTS ${exe})
|
||||
# check that the utility can be executed cleanly
|
||||
# in case we find e.g. the wrong architecture binary
|
||||
# when cross-compiling
|
||||
check_clean_exit(exit_status ${exe} --help)
|
||||
|
||||
if(exit_status EQUAL 0)
|
||||
set(${var} ${exe} PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
if(exe)
|
||||
set("${var}" "${exe}" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# don't iterate over minor versions for empty major version
|
||||
|
@@ -2,7 +2,7 @@ with import <nixpkgs> {};
|
||||
stdenv.mkDerivation {
|
||||
name = "visualboyadvance-m";
|
||||
buildInputs = if stdenv.isDarwin then
|
||||
[ ninja cmake nasm faudio gettext libintl libtiff pkg-config zip zlib openal ffmpeg wxGTK32 sdl3 pcre pcre2 darwin.apple_sdk.frameworks.System darwin.apple_sdk.frameworks.IOKit darwin.apple_sdk.frameworks.Carbon darwin.apple_sdk.frameworks.Cocoa darwin.apple_sdk.frameworks.QuartzCore darwin.apple_sdk.frameworks.AudioToolbox darwin.apple_sdk.frameworks.OpenGL darwin.apple_sdk.frameworks.OpenAL llvmPackages_latest.clang llvmPackages_latest.bintools ]
|
||||
[ ninja cmake nasm faudio gettext libintl libtiff pkg-config zip zlib openal ffmpeg wxGTK32 sdl3 pcre pcre2 llvmPackages_latest.clang llvmPackages_latest.bintools ]
|
||||
else
|
||||
[ ninja cmake gcc clang llvm llvmPackages.libcxx nasm faudio gettext libintl libtiff pkg-config zip zlib openal ffmpeg wxGTK32 libGL libGLU glfw sdl3 gtk3-x11 pcre pcre2 util-linuxMinimal libselinux libsepol libthai libdatrie xorg.libXdmcp xorg.libXtst libxkbcommon libepoxy dbus at-spi2-core ];
|
||||
}
|
||||
|
@@ -342,9 +342,12 @@ debian_installdeps() {
|
||||
;;
|
||||
esac
|
||||
|
||||
pkgs="build-essential g++ nasm cmake ccache gettext zlib1g-dev libgl1-mesa-dev libgettextpo-dev libsdl3-dev libglu1-mesa-dev libglu1-mesa libgles2-mesa-dev $glew_lib $wx_libs libgtk2.0-dev libgtk-3-dev ccache zip ninja-build libopenal-dev"
|
||||
sdl_lib=$(apt-cache search libsdl3-dev | grep libsdl3-dev | awk '{ print $1 }')
|
||||
[ -z "$sdl_lib" ] && sdl_lib=libsdl2-dev
|
||||
|
||||
[ -n "$ENABLE_FFMPEG" ] && pkgs="$pkgs libavcodec-dev libavformat-dev libswscale-dev libavutil-dev $libswresample_dev"
|
||||
pkgs="build-essential g++ nasm cmake ccache gettext zlib1g-dev libgl1-mesa-dev libgettextpo-dev $sdl_lib libglu1-mesa-dev libglu1-mesa libgles2-mesa-dev $glew_lib $wx_libs libgtk2.0-dev libgtk-3-dev ccache zip ninja-build libopenal-dev"
|
||||
|
||||
[ -n "$ENABLE_FFMPEG" ] && pkgs="$pkgs libavcodec-dev libavformat-dev libswscale-dev libavutil-dev $libswresample_dev libx264-dev libx265-dev"
|
||||
|
||||
check sudo apt-get -qy install $pkgs
|
||||
else
|
||||
|
781
po/wxvbam/bg.po
781
po/wxvbam/bg.po
File diff suppressed because it is too large
Load Diff
781
po/wxvbam/br.po
781
po/wxvbam/br.po
File diff suppressed because it is too large
Load Diff
781
po/wxvbam/cs.po
781
po/wxvbam/cs.po
File diff suppressed because it is too large
Load Diff
3974
po/wxvbam/de.po
3974
po/wxvbam/de.po
File diff suppressed because it is too large
Load Diff
787
po/wxvbam/el.po
787
po/wxvbam/el.po
File diff suppressed because it is too large
Load Diff
1146
po/wxvbam/es.po
1146
po/wxvbam/es.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1085
po/wxvbam/fr_FR.po
1085
po/wxvbam/fr_FR.po
File diff suppressed because it is too large
Load Diff
781
po/wxvbam/gl.po
781
po/wxvbam/gl.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
787
po/wxvbam/id.po
787
po/wxvbam/id.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
787
po/wxvbam/ja.po
787
po/wxvbam/ja.po
File diff suppressed because it is too large
Load Diff
3894
po/wxvbam/ja_JP.po
3894
po/wxvbam/ja_JP.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
781
po/wxvbam/nb.po
781
po/wxvbam/nb.po
File diff suppressed because it is too large
Load Diff
781
po/wxvbam/nl.po
781
po/wxvbam/nl.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1129
po/wxvbam/sv.po
1129
po/wxvbam/sv.po
File diff suppressed because it is too large
Load Diff
787
po/wxvbam/tr.po
787
po/wxvbam/tr.po
File diff suppressed because it is too large
Load Diff
759
po/wxvbam/uk.po
759
po/wxvbam/uk.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
BIN
resource.xrs
Normal file
BIN
resource.xrs
Normal file
Binary file not shown.
@@ -2,5 +2,6 @@ add_subdirectory(av_recording)
|
||||
add_subdirectory(draw_text)
|
||||
add_subdirectory(filters)
|
||||
add_subdirectory(filters_agb)
|
||||
add_subdirectory(filters_cgb)
|
||||
add_subdirectory(filters_interframe)
|
||||
add_subdirectory(user_config)
|
||||
|
@@ -278,7 +278,7 @@ recording::MediaRet recording::MediaRecorder::setup_video_stream_info(int width,
|
||||
switch (pixsize)
|
||||
{
|
||||
case 1:
|
||||
tbord = 1; rbord = 2;
|
||||
tbord = 1; rbord = 4;
|
||||
break;
|
||||
case 2:
|
||||
// 16-bit: 2 @ right, 1 @ top
|
||||
|
@@ -26,10 +26,6 @@ extern "C"
|
||||
bool cpu_mmx = 1;
|
||||
#endif
|
||||
}
|
||||
static uint32_t colorMask = 0xF7DEF7DE;
|
||||
static uint32_t lowPixelMask = 0x08210821;
|
||||
static uint32_t qcolorMask = 0xE79CE79C;
|
||||
static uint32_t qlowpixelMask = 0x18631863;
|
||||
static uint32_t redblueMask = 0xF81F;
|
||||
static uint32_t greenMask = 0x7E0;
|
||||
|
||||
@@ -41,20 +37,12 @@ int Init_2xSaI(uint32_t BitFormat)
|
||||
{
|
||||
if(systemColorDepth == 16) {
|
||||
if (BitFormat == 565) {
|
||||
colorMask = 0xF7DEF7DE;
|
||||
lowPixelMask = 0x08210821;
|
||||
qcolorMask = 0xE79CE79C;
|
||||
qlowpixelMask = 0x18631863;
|
||||
redblueMask = 0xF81F;
|
||||
greenMask = 0x7E0;
|
||||
qRGB_COLOR_MASK[0] = qRGB_COLOR_MASK[1] = 0xF7DEF7DE;
|
||||
hq2x_init(16);
|
||||
RGB_LOW_BITS_MASK = 0x0821;
|
||||
} else if (BitFormat == 555) {
|
||||
colorMask = 0x7BDE7BDE;
|
||||
lowPixelMask = 0x04210421;
|
||||
qcolorMask = 0x739C739C;
|
||||
qlowpixelMask = 0x0C630C63;
|
||||
redblueMask = 0x7C1F;
|
||||
greenMask = 0x3E0;
|
||||
qRGB_COLOR_MASK[0] = qRGB_COLOR_MASK[1] = 0x7BDE7BDE;
|
||||
@@ -64,10 +52,6 @@ int Init_2xSaI(uint32_t BitFormat)
|
||||
return 0;
|
||||
}
|
||||
} else if(systemColorDepth == 32) {
|
||||
colorMask = 0xfefefe;
|
||||
lowPixelMask = 0x010101;
|
||||
qcolorMask = 0xfcfcfc;
|
||||
qlowpixelMask = 0x030303;
|
||||
qRGB_COLOR_MASK[0] = qRGB_COLOR_MASK[1] = 0xfefefe;
|
||||
hq2x_init(32);
|
||||
RGB_LOW_BITS_MASK = 0x010101;
|
||||
@@ -146,25 +130,24 @@ static inline int GetResult (uint32_t A, uint32_t B, uint32_t C, uint32_t D)
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline uint32_t INTERPOLATE (uint32_t A, uint32_t B)
|
||||
{
|
||||
if (A != B) {
|
||||
return (((A & colorMask) >> 1) + ((B & colorMask) >> 1) +
|
||||
(A & B & lowPixelMask));
|
||||
} else
|
||||
return A;
|
||||
static inline uint32_t INTERPOLATE(uint32_t A, uint32_t B) {
|
||||
if (A == B) {
|
||||
return A;
|
||||
}
|
||||
return (
|
||||
(((((A >> 16) & 0xFF) & 0xFE) >> 1) + ((((B >> 16) & 0xFF) & 0xFE) >> 1) + (((A >> 16) & 0xFF) & ((B >> 16) & 0xFF) & 0x01)) << 16) |
|
||||
((((((A >> 8) & 0xFF) & 0xFE) >> 1) + ((((B >> 8) & 0xFF) & 0xFE) >> 1) + (((A >> 8) & 0xFF) & ((B >> 8) & 0xFF) & 0x01)) << 8) |
|
||||
(((((A & 0xFF) & 0xFE) >> 1) + (((B & 0xFF) & 0xFE) >> 1) + ((A & 0xFF) & (B & 0xFF) & 0x01)));
|
||||
}
|
||||
|
||||
static inline uint32_t Q_INTERPOLATE (uint32_t A, uint32_t B, uint32_t C, uint32_t D)
|
||||
{
|
||||
uint32_t x = ((A & qcolorMask) >> 2) +
|
||||
((B & qcolorMask) >> 2) +
|
||||
((C & qcolorMask) >> 2) + ((D & qcolorMask) >> 2);
|
||||
uint32_t y = (A & qlowpixelMask) +
|
||||
(B & qlowpixelMask) + (C & qlowpixelMask) + (D & qlowpixelMask);
|
||||
|
||||
y = (y >> 2) & qlowpixelMask;
|
||||
return x + y;
|
||||
static inline uint32_t Q_INTERPOLATE(uint32_t A, uint32_t B, uint32_t C, uint32_t D) {
|
||||
return (
|
||||
(((((A >> 16) & 0xFC) >> 2) + (((B >> 16) & 0xFC) >> 2) + (((C >> 16) & 0xFC) >> 2) + (((D >> 16) & 0xFC) >> 2) +
|
||||
(((A >> 16) & 0x03) + ((B >> 16) & 0x03) + ((C >> 16) & 0x03) + ((D >> 16) & 0x03))) << 16) |
|
||||
(((((A >> 8) & 0xFC) >> 2) + (((B >> 8) & 0xFC) >> 2) + (((C >> 8) & 0xFC) >> 2) + (((D >> 8) & 0xFC) >> 2) +
|
||||
(((A >> 8) & 0x03) + ((B >> 8) & 0x03) + ((C >> 8) & 0x03) + ((D >> 8) & 0x03))) << 8) |
|
||||
((((A & 0xFC) >> 2) + ((B & 0xFC) >> 2) + ((C & 0xFC) >> 2) + ((D & 0xFC) >> 2) +
|
||||
((A & 0x03) + (B & 0x03) + (C & 0x03) + (D & 0x03)))));
|
||||
}
|
||||
|
||||
static inline int GetResult1_32 (uint32_t A, uint32_t B, uint32_t C, uint32_t D,
|
||||
@@ -1275,4 +1258,3 @@ void Scale_2xSaI (uint8_t *srcPtr, uint32_t srcPitch, uint8_t * /* deltaPtr */,
|
||||
dstPtr += dstPitch;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,4 +1,17 @@
|
||||
/*
|
||||
* GBA Color Correction Shader Implementation
|
||||
*
|
||||
* Shader modified by Pokefan531.
|
||||
* Color Mangler
|
||||
* Original Author: hunterk
|
||||
* Original License: Public domain
|
||||
*
|
||||
* This code is adapted from the original shader logic.
|
||||
*/
|
||||
|
||||
#include "components/filters_agb/filters_agb.h"
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
|
||||
extern int systemColorDepth;
|
||||
extern int systemRedShift;
|
||||
@@ -9,272 +22,332 @@ extern uint8_t systemColorMap8[0x10000];
|
||||
extern uint16_t systemColorMap16[0x10000];
|
||||
extern uint32_t systemColorMap32[0x10000];
|
||||
|
||||
static const unsigned char curve[32] = { 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0e, 0x10, 0x12,
|
||||
0x14, 0x16, 0x18, 0x1c, 0x20, 0x28, 0x30, 0x38,
|
||||
0x40, 0x48, 0x50, 0x58, 0x60, 0x68, 0x70, 0x80,
|
||||
0x88, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0 };
|
||||
// --- Global Constants and Variables for GBA Color Correction ---
|
||||
// Define the color profile matrix as a static const float 2D array
|
||||
// This replicates the column-major order of GLSL mat4 for easier translation.
|
||||
// Format: { {col0_row0, col0_row1, col0_row2, col0_row3}, ... }
|
||||
static const float GBA_sRGB[4][4] = {
|
||||
{0.905f, 0.10f, 0.1575f, 0.0f}, // Column 0 (R output contributions from R, G, B, A)
|
||||
{0.195f, 0.65f, 0.1425f, 0.0f}, // Column 1 (G output contributions from R, G, B, A)
|
||||
{-0.10f, 0.25f, 0.70f, 0.0f}, // Column 2 (B output contributions from R, G, B, A)
|
||||
{0.0f, 0.0f, 0.0f, 0.91f} // Column 3 (A/Luminance contribution)
|
||||
};
|
||||
|
||||
// output R G B
|
||||
static const unsigned char influence[3 * 3] = { 16, 4, 4, // red
|
||||
8, 16, 8, // green
|
||||
0, 8, 16 }; // blue
|
||||
static const float GBA_DCI[4][4] = {
|
||||
{0.76f, 0.125f, 0.16f, 0.0f},
|
||||
{0.27f, 0.6375f, 0.18f, 0.0f},
|
||||
{-0.03f, 0.2375f, 0.66f, 0.0f},
|
||||
{0.0f, 0.0f, 0.0f, 0.97f}
|
||||
};
|
||||
|
||||
inline void swap(short& a, short& b)
|
||||
{
|
||||
short temp = a;
|
||||
a = b;
|
||||
b = temp;
|
||||
static const float GBA_Rec2020[4][4] = {
|
||||
{0.61f, 0.155f, 0.16f, 0.0f},
|
||||
{0.345f, 0.615f, 0.1875f, 0.0f},
|
||||
{0.045f, 0.23f, 0.6525f, 0.0f},
|
||||
{0.0f, 0.0f, 0.0f, 1.0f}
|
||||
};
|
||||
|
||||
// Screen darkening factor. Default to 0.0f
|
||||
static float darken_screen = 0.0f;
|
||||
|
||||
// Color mode (0 for sRGB, 1 for DCI, 2 for Rec2020). Default to sRGB (0).
|
||||
static int color_mode = 0;
|
||||
|
||||
// Pointer to the currently selected color profile matrix.
|
||||
static const float (*profile)[4];
|
||||
|
||||
// Global constants from the shader for gamma correction values
|
||||
static const float target_gamma = 2.2f;
|
||||
static const float display_gamma = 2.2f;
|
||||
|
||||
|
||||
// --- Function Implementations ---
|
||||
|
||||
// Forward declaration of a helper function to set the profile based on color_mode
|
||||
static void set_profile_from_mode();
|
||||
|
||||
// This constructor-like function runs once when the program starts.
|
||||
struct GbafilterInitializer {
|
||||
GbafilterInitializer() {
|
||||
set_profile_from_mode();
|
||||
}
|
||||
};
|
||||
static GbafilterInitializer __gbafilter_initializer;
|
||||
|
||||
|
||||
// Helper function to set the 'profile' pointer based on the 'color_mode' variable.
|
||||
static void set_profile_from_mode() {
|
||||
if (color_mode == 0) {
|
||||
profile = GBA_sRGB;
|
||||
}
|
||||
else if (color_mode == 1) {
|
||||
profile = GBA_DCI;
|
||||
}
|
||||
else if (color_mode == 2) {
|
||||
profile = GBA_Rec2020;
|
||||
}
|
||||
else {
|
||||
profile = GBA_sRGB; // Default to sRGB if an invalid mode is set
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Public function to set color mode and darken screen from external calls
|
||||
void gbafilter_set_params(int new_color_mode, float new_darken_screen) {
|
||||
color_mode = new_color_mode;
|
||||
darken_screen = fmaxf(0.0f, fminf(1.0f, new_darken_screen)); // Clamp to 0.0-1.0
|
||||
|
||||
// Call the helper to update 'profile' based on the new 'color_mode'
|
||||
set_profile_from_mode();
|
||||
}
|
||||
|
||||
void gbafilter_update_colors(bool lcd) {
|
||||
switch (systemColorDepth) {
|
||||
case 8: {
|
||||
for (int i = 0; i < 0x10000; i++) {
|
||||
systemColorMap8[i] = (uint8_t)((((i & 0x1f) << 3) & 0xE0) |
|
||||
((((i & 0x3e0) >> 5) << 0) & 0x1C) |
|
||||
((((i & 0x7c00) >> 10) >> 3) & 0x3));
|
||||
}
|
||||
} break;
|
||||
case 16: {
|
||||
for (int i = 0; i < 0x10000; i++) {
|
||||
systemColorMap16[i] = ((i & 0x1f) << systemRedShift) |
|
||||
(((i & 0x3e0) >> 5) << systemGreenShift) |
|
||||
(((i & 0x7c00) >> 10) << systemBlueShift);
|
||||
}
|
||||
if (lcd)
|
||||
gbafilter_pal(systemColorMap16, 0x10000);
|
||||
} break;
|
||||
case 24:
|
||||
case 32: {
|
||||
for (int i = 0; i < 0x10000; i++) {
|
||||
systemColorMap32[i] = ((i & 0x1f) << systemRedShift) |
|
||||
(((i & 0x3e0) >> 5) << systemGreenShift) |
|
||||
(((i & 0x7c00) >> 10) << systemBlueShift);
|
||||
}
|
||||
if (lcd)
|
||||
gbafilter_pal32(systemColorMap32, 0x10000);
|
||||
} break;
|
||||
case 8: {
|
||||
for (int i = 0; i < 0x10000; i++) {
|
||||
systemColorMap8[i] = (uint8_t)((((i & 0x1f) << 3) & 0xE0) |
|
||||
((((i & 0x3e0) >> 5) << 0) & 0x1C) |
|
||||
((((i & 0x7c00) >> 10) >> 3) & 0x3));
|
||||
}
|
||||
if (lcd)
|
||||
gbafilter_pal8(systemColorMap8, 0x10000);
|
||||
} break;
|
||||
case 16: {
|
||||
for (int i = 0x0; i < 0x10000; i++) {
|
||||
systemColorMap16[i] = ((i & 0x1f) << systemRedShift) |
|
||||
(((i & 0x3e0) >> 5) << systemGreenShift) |
|
||||
(((i & 0x7c00) >> 10) << systemBlueShift);
|
||||
}
|
||||
if (lcd)
|
||||
gbafilter_pal(systemColorMap16, 0x10000);
|
||||
} break;
|
||||
case 24:
|
||||
case 32: {
|
||||
for (int i = 0; i < 0x10000; i++) {
|
||||
systemColorMap32[i] = ((i & 0x1f) << systemRedShift) |
|
||||
(((i & 0x3e0) >> 5) << systemGreenShift) |
|
||||
(((i & 0x7c00) >> 10) << systemBlueShift);
|
||||
}
|
||||
if (lcd)
|
||||
gbafilter_pal32(systemColorMap32, 0x10000);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void gbafilter_pal8(uint8_t* buf, int count)
|
||||
{
|
||||
// Pre-calculate constants for efficiency within function scope
|
||||
const float target_gamma_exponent = target_gamma + darken_screen;
|
||||
const float display_gamma_reciprocal = 1.0f / display_gamma;
|
||||
const float luminance_factor = profile[3][3]; // profile[3].w from GLSL
|
||||
|
||||
while (count--) {
|
||||
uint8_t pix = *buf;
|
||||
|
||||
uint8_t original_r_val_3bit = (uint8_t)((pix & 0xE0) >> 5);
|
||||
uint8_t original_g_val_3bit = (uint8_t)((pix & 0x1C) >> 2);
|
||||
uint8_t original_b_val_2bit = (uint8_t)(pix & 0x3);
|
||||
|
||||
// Normalize to 0.0-1.0 for calculations
|
||||
float r = (float)original_r_val_3bit / 7.0f;
|
||||
float g = (float)original_g_val_3bit / 7.0f;
|
||||
float b = (float)original_b_val_2bit / 3.0f;
|
||||
|
||||
// 1. Apply initial gamma (including darken_screen as exponent) to convert to linear space.
|
||||
// This step will affect non-"white" values.
|
||||
r = powf(r, target_gamma_exponent);
|
||||
g = powf(g, target_gamma_exponent);
|
||||
b = powf(b, target_gamma_exponent);
|
||||
|
||||
// 2. Apply luminance factor and clamp.
|
||||
r = fmaxf(0.0f, fminf(1.0f, r * luminance_factor));
|
||||
g = fmaxf(0.0f, fminf(1.0f, g * luminance_factor));
|
||||
b = fmaxf(0.0f, fminf(1.0f, b * luminance_factor));
|
||||
|
||||
// 3. Apply color profile matrix (using profile[column][row] access)
|
||||
float transformed_r = profile[0][0] * r + profile[1][0] * g + profile[2][0] * b;
|
||||
float transformed_g = profile[0][1] * r + profile[1][1] * g + profile[2][1] * b;
|
||||
float transformed_b = profile[0][2] * r + profile[1][2] * g + profile[2][2] * b;
|
||||
|
||||
// 4. Apply display gamma to convert back for display.
|
||||
transformed_r = copysignf(powf(fabsf(transformed_r), display_gamma_reciprocal), transformed_r);
|
||||
transformed_g = copysignf(powf(fabsf(transformed_g), display_gamma_reciprocal), transformed_g);
|
||||
transformed_b = copysignf(powf(fabsf(transformed_b), display_gamma_reciprocal), transformed_b);
|
||||
|
||||
// Final clamp: ensure values are within 0.0-1.0 range
|
||||
transformed_r = fmaxf(0.0f, fminf(1.0f, transformed_r));
|
||||
transformed_g = fmaxf(0.0f, fminf(1.0f, transformed_g));
|
||||
transformed_b = fmaxf(0.0f, fminf(1.0f, transformed_b));
|
||||
|
||||
// Convert back to 3-bit or 2-bit (0-7 or 0-3) integer and combine into uint8_t
|
||||
// Apply 5-bit to 5-bit conversion, as this palette is for 16-bit output.
|
||||
uint8_t final_red = (uint8_t)(transformed_r * 7.0f + 0.5f);
|
||||
uint8_t final_green = (uint8_t)(transformed_g * 7.0f + 0.5f);
|
||||
uint8_t final_blue = (uint8_t)(transformed_b * 3.0f + 0.5f);
|
||||
|
||||
// Ensure values are strictly within 0-7 or 0-3 range after rounding
|
||||
if (final_red > 7) final_red = 7;
|
||||
if (final_green > 7) final_green = 7;
|
||||
if (final_blue > 3) final_blue = 3;
|
||||
|
||||
*buf++ = ((final_red & 0x7) << 5) |
|
||||
((final_green & 0x7) << 2) |
|
||||
(final_blue & 0x3);
|
||||
}
|
||||
}
|
||||
|
||||
void gbafilter_pal(uint16_t* buf, int count)
|
||||
{
|
||||
short temp[3 * 3], s;
|
||||
uint16_t pix;
|
||||
uint8_t red, green, blue;
|
||||
// Pre-calculate constants for efficiency within function scope
|
||||
const float target_gamma_exponent = target_gamma + darken_screen;
|
||||
const float display_gamma_reciprocal = 1.0f / display_gamma;
|
||||
const float luminance_factor = profile[3][3]; // profile[3].w from GLSL
|
||||
|
||||
while (count--) {
|
||||
pix = *buf;
|
||||
uint16_t pix = *buf;
|
||||
|
||||
s = curve[(pix >> systemGreenShift) & 0x1f];
|
||||
temp[3] = s * influence[3];
|
||||
temp[4] = s * influence[4];
|
||||
temp[5] = s * influence[5];
|
||||
uint8_t original_r_val_5bit = (uint8_t)((pix >> systemRedShift) & 0x1f);
|
||||
uint8_t original_g_val_5bit = (uint8_t)((pix >> systemGreenShift) & 0x1f);
|
||||
uint8_t original_b_val_5bit = (uint8_t)((pix >> systemBlueShift) & 0x1f);
|
||||
|
||||
s = curve[(pix >> systemRedShift) & 0x1f];
|
||||
temp[0] = s * influence[0];
|
||||
temp[1] = s * influence[1];
|
||||
temp[2] = s * influence[2];
|
||||
// Normalize to 0.0-1.0 for calculations
|
||||
float r = (float)original_r_val_5bit / 31.0f;
|
||||
float g = (float)original_g_val_5bit / 31.0f;
|
||||
float b = (float)original_b_val_5bit / 31.0f;
|
||||
|
||||
s = curve[(pix >> systemBlueShift) & 0x1f];
|
||||
temp[6] = s * influence[6];
|
||||
temp[7] = s * influence[7];
|
||||
temp[8] = s * influence[8];
|
||||
// 1. Apply initial gamma (including darken_screen as exponent) to convert to linear space.
|
||||
// This step will affect non-"white" values.
|
||||
r = powf(r, target_gamma_exponent);
|
||||
g = powf(g, target_gamma_exponent);
|
||||
b = powf(b, target_gamma_exponent);
|
||||
|
||||
if (temp[0] < temp[3])
|
||||
swap(temp[0], temp[3]);
|
||||
if (temp[0] < temp[6])
|
||||
swap(temp[0], temp[6]);
|
||||
if (temp[3] < temp[6])
|
||||
swap(temp[3], temp[6]);
|
||||
temp[3] <<= 1;
|
||||
temp[0] <<= 2;
|
||||
temp[0] += temp[3] + temp[6];
|
||||
// 2. Apply luminance factor and clamp.
|
||||
r = fmaxf(0.0f, fminf(1.0f, r * luminance_factor));
|
||||
g = fmaxf(0.0f, fminf(1.0f, g * luminance_factor));
|
||||
b = fmaxf(0.0f, fminf(1.0f, b * luminance_factor));
|
||||
|
||||
red = ((int(temp[0]) * 160) >> 17) + 4;
|
||||
if (red > 31)
|
||||
red = 31;
|
||||
// 3. Apply color profile matrix (using profile[column][row] access)
|
||||
float transformed_r = profile[0][0] * r + profile[1][0] * g + profile[2][0] * b;
|
||||
float transformed_g = profile[0][1] * r + profile[1][1] * g + profile[2][1] * b;
|
||||
float transformed_b = profile[0][2] * r + profile[1][2] * g + profile[2][2] * b;
|
||||
|
||||
if (temp[2] < temp[5])
|
||||
swap(temp[2], temp[5]);
|
||||
if (temp[2] < temp[8])
|
||||
swap(temp[2], temp[8]);
|
||||
if (temp[5] < temp[8])
|
||||
swap(temp[5], temp[8]);
|
||||
temp[5] <<= 1;
|
||||
temp[2] <<= 2;
|
||||
temp[2] += temp[5] + temp[8];
|
||||
// 4. Apply display gamma to convert back for display.
|
||||
transformed_r = copysignf(powf(fabsf(transformed_r), display_gamma_reciprocal), transformed_r);
|
||||
transformed_g = copysignf(powf(fabsf(transformed_g), display_gamma_reciprocal), transformed_g);
|
||||
transformed_b = copysignf(powf(fabsf(transformed_b), display_gamma_reciprocal), transformed_b);
|
||||
|
||||
blue = ((int(temp[2]) * 160) >> 17) + 4;
|
||||
if (blue > 31)
|
||||
blue = 31;
|
||||
// Final clamp: ensure values are within 0.0-1.0 range
|
||||
transformed_r = fmaxf(0.0f, fminf(1.0f, transformed_r));
|
||||
transformed_g = fmaxf(0.0f, fminf(1.0f, transformed_g));
|
||||
transformed_b = fmaxf(0.0f, fminf(1.0f, transformed_b));
|
||||
|
||||
if (temp[1] < temp[4])
|
||||
swap(temp[1], temp[4]);
|
||||
if (temp[1] < temp[7])
|
||||
swap(temp[1], temp[7]);
|
||||
if (temp[4] < temp[7])
|
||||
swap(temp[4], temp[7]);
|
||||
temp[4] <<= 1;
|
||||
temp[1] <<= 2;
|
||||
temp[1] += temp[4] + temp[7];
|
||||
// Convert back to 5-bit (0-31) integer and combine into uint16_t
|
||||
// Apply 5-bit to 5-bit conversion, as this palette is for 16-bit output.
|
||||
uint8_t final_red = (uint8_t)(transformed_r * 31.0f + 0.5f);
|
||||
uint8_t final_green = (uint8_t)(transformed_g * 31.0f + 0.5f);
|
||||
uint8_t final_blue = (uint8_t)(transformed_b * 31.0f + 0.5f);
|
||||
|
||||
green = ((int(temp[1]) * 160) >> 17) + 4;
|
||||
if (green > 31)
|
||||
green = 31;
|
||||
// Ensure values are strictly within 0-31 range after rounding
|
||||
if (final_red > 31) final_red = 31;
|
||||
if (final_green > 31) final_green = 31;
|
||||
if (final_blue > 31) final_blue = 31;
|
||||
|
||||
pix = red << systemRedShift;
|
||||
pix += green << systemGreenShift;
|
||||
pix += blue << systemBlueShift;
|
||||
|
||||
*buf++ = pix;
|
||||
*buf++ = (final_red << systemRedShift) |
|
||||
(final_green << systemGreenShift) |
|
||||
(final_blue << systemBlueShift);
|
||||
}
|
||||
}
|
||||
|
||||
void gbafilter_pal32(uint32_t* buf, int count)
|
||||
{
|
||||
short temp[3 * 3], s;
|
||||
unsigned pix;
|
||||
uint8_t red, green, blue;
|
||||
// Pre-calculate constants for efficiency within function scope
|
||||
const float target_gamma_exponent = target_gamma + darken_screen;
|
||||
const float display_gamma_reciprocal = 1.0f / display_gamma;
|
||||
const float luminance_factor = profile[3][3]; // profile[3].w from GLSL
|
||||
|
||||
while (count--) {
|
||||
pix = *buf;
|
||||
uint32_t pix = *buf;
|
||||
|
||||
s = curve[(pix >> systemGreenShift) & 0x1f];
|
||||
temp[3] = s * influence[3];
|
||||
temp[4] = s * influence[4];
|
||||
temp[5] = s * influence[5];
|
||||
// Extract original 5-bit R, G, B values from the shifted positions in the 32-bit pixel.
|
||||
// These shifts pull out the 5-bit value from its shifted position (e.g., bits 3-7 for Red).
|
||||
uint8_t original_r_val_5bit = (uint8_t)((pix >> systemRedShift) & 0x1f);
|
||||
uint8_t original_g_val_5bit = (uint8_t)((pix >> systemGreenShift) & 0x1f);
|
||||
uint8_t original_b_val_5bit = (uint8_t)((pix >> systemBlueShift) & 0x1f);
|
||||
|
||||
s = curve[(pix >> systemRedShift) & 0x1f];
|
||||
temp[0] = s * influence[0];
|
||||
temp[1] = s * influence[1];
|
||||
temp[2] = s * influence[2];
|
||||
|
||||
s = curve[(pix >> systemBlueShift) & 0x1f];
|
||||
temp[6] = s * influence[6];
|
||||
temp[7] = s * influence[7];
|
||||
temp[8] = s * influence[8];
|
||||
// Normalize to 0.0-1.0 for calculations
|
||||
float r = (float)original_r_val_5bit / 31.0f;
|
||||
float g = (float)original_g_val_5bit / 31.0f;
|
||||
float b = (float)original_b_val_5bit / 31.0f;
|
||||
|
||||
if (temp[0] < temp[3])
|
||||
swap(temp[0], temp[3]);
|
||||
if (temp[0] < temp[6])
|
||||
swap(temp[0], temp[6]);
|
||||
if (temp[3] < temp[6])
|
||||
swap(temp[3], temp[6]);
|
||||
temp[3] <<= 1;
|
||||
temp[0] <<= 2;
|
||||
temp[0] += temp[3] + temp[6];
|
||||
// 1. Apply initial gamma (including darken_screen as exponent) to convert to linear space.
|
||||
r = powf(r, target_gamma_exponent);
|
||||
g = powf(g, target_gamma_exponent);
|
||||
b = powf(b, target_gamma_exponent);
|
||||
|
||||
//red = ((int(temp[0]) * 160) >> 17) + 4;
|
||||
red = ((int(temp[0]) * 160) >> 14) + 32;
|
||||
// 2. Apply luminance factor and clamp.
|
||||
r = fmaxf(0.0f, fminf(1.0f, r * luminance_factor));
|
||||
g = fmaxf(0.0f, fminf(1.0f, g * luminance_factor));
|
||||
b = fmaxf(0.0f, fminf(1.0f, b * luminance_factor));
|
||||
|
||||
if (temp[2] < temp[5])
|
||||
swap(temp[2], temp[5]);
|
||||
if (temp[2] < temp[8])
|
||||
swap(temp[2], temp[8]);
|
||||
if (temp[5] < temp[8])
|
||||
swap(temp[5], temp[8]);
|
||||
temp[5] <<= 1;
|
||||
temp[2] <<= 2;
|
||||
temp[2] += temp[5] + temp[8];
|
||||
// 3. Apply color profile matrix
|
||||
float transformed_r = profile[0][0] * r + profile[1][0] * g + profile[2][0] * b;
|
||||
float transformed_g = profile[0][1] * r + profile[1][1] * g + profile[2][1] * b;
|
||||
float transformed_b = profile[0][2] * r + profile[1][2] * g + profile[2][2] * b;
|
||||
|
||||
//blue = ((int(temp[2]) * 160) >> 17) + 4;
|
||||
blue = ((int(temp[2]) * 160) >> 14) + 32;
|
||||
// 4. Apply display gamma.
|
||||
transformed_r = copysignf(powf(fabsf(transformed_r), display_gamma_reciprocal), transformed_r);
|
||||
transformed_g = copysignf(powf(fabsf(transformed_g), display_gamma_reciprocal), transformed_g);
|
||||
transformed_b = copysignf(powf(fabsf(transformed_b), display_gamma_reciprocal), transformed_b);
|
||||
|
||||
if (temp[1] < temp[4])
|
||||
swap(temp[1], temp[4]);
|
||||
if (temp[1] < temp[7])
|
||||
swap(temp[1], temp[7]);
|
||||
if (temp[4] < temp[7])
|
||||
swap(temp[4], temp[7]);
|
||||
temp[4] <<= 1;
|
||||
temp[1] <<= 2;
|
||||
temp[1] += temp[4] + temp[7];
|
||||
// Final clamp: ensure values are within 0.0-1.0 range
|
||||
transformed_r = fmaxf(0.0f, fminf(1.0f, transformed_r));
|
||||
transformed_g = fmaxf(0.0f, fminf(1.0f, transformed_g));
|
||||
transformed_b = fmaxf(0.0f, fminf(1.0f, transformed_b));
|
||||
|
||||
//green = ((int(temp[1]) * 160) >> 17) + 4;
|
||||
green = ((int(temp[1]) * 160) >> 14) + 32;
|
||||
|
||||
//pix = red << redshift;
|
||||
//pix += green << greenshift;
|
||||
//pix += blue << blueshift;
|
||||
// Convert the floating-point values to 8-bit integer components (0-255).
|
||||
uint8_t final_red_8bit = (uint8_t)(transformed_r * 255.0f + 0.5f);
|
||||
uint8_t final_green_8bit = (uint8_t)(transformed_g * 255.0f + 0.5f);
|
||||
uint8_t final_blue_8bit = (uint8_t)(transformed_b * 255.0f + 0.5f);
|
||||
|
||||
pix = red << (systemRedShift - 3);
|
||||
pix += green << (systemGreenShift - 3);
|
||||
pix += blue << (systemBlueShift - 3);
|
||||
// Ensure values are strictly within 0-255 range after rounding
|
||||
if (final_red_8bit > 255) final_red_8bit = 255;
|
||||
if (final_green_8bit > 255) final_green_8bit = 255;
|
||||
if (final_blue_8bit > 255) final_blue_8bit = 255;
|
||||
|
||||
*buf++ = pix;
|
||||
}
|
||||
}
|
||||
// --- NEW PACKING LOGIC ---
|
||||
// This is the critical change to correctly map 8-bit color to the 5-bit shifted format,
|
||||
// while allowing FFFFFF.
|
||||
// It uses the top 5 bits of the 8-bit value for the GBA's 5-bit component position,
|
||||
// and the bottom 3 bits to fill the lower, normally zeroed, positions.
|
||||
|
||||
// for palette mode to work with the three spoony filters in 32bpp depth
|
||||
uint32_t final_pix = 0;
|
||||
|
||||
void gbafilter_pad(uint8_t* buf, int count)
|
||||
{
|
||||
union {
|
||||
struct
|
||||
{
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
uint8_t a;
|
||||
} part;
|
||||
unsigned whole;
|
||||
} mask;
|
||||
// Red component
|
||||
// 5 most significant bits (MSBs) for the 'systemRedShift' position
|
||||
final_pix |= ((final_red_8bit >> 3) & 0x1f) << systemRedShift;
|
||||
// 3 least significant bits (LSBs) for the 'base' position (systemRedShift - 3)
|
||||
final_pix |= (final_red_8bit & 0x07) << (systemRedShift - 3);
|
||||
|
||||
mask.whole = 0x1f << systemRedShift;
|
||||
mask.whole += 0x1f << systemGreenShift;
|
||||
mask.whole += 0x1f << systemBlueShift;
|
||||
|
||||
switch (systemColorDepth) {
|
||||
case 24:
|
||||
while (count--) {
|
||||
*buf++ &= mask.part.r;
|
||||
*buf++ &= mask.part.g;
|
||||
*buf++ &= mask.part.b;
|
||||
// Green component
|
||||
// 5 MSBs for the 'systemGreenShift' position
|
||||
final_pix |= ((final_green_8bit >> 3) & 0x1f) << systemGreenShift;
|
||||
// 3 LSBs for the 'base' position (systemGreenShift - 3)
|
||||
final_pix |= (final_green_8bit & 0x07) << (systemGreenShift - 3);
|
||||
|
||||
// Blue component
|
||||
// 5 MSBs for the 'systemBlueShift' position
|
||||
final_pix |= ((final_blue_8bit >> 3) & 0x1f) << systemBlueShift;
|
||||
// 3 LSBs for the 'base' position (systemBlueShift - 3)
|
||||
final_pix |= (final_blue_8bit & 0x07) << (systemBlueShift - 3);
|
||||
|
||||
// Preserve existing alpha if present (assuming it's at bits 24-31 for 32-bit depth)
|
||||
if (systemColorDepth == 32) {
|
||||
final_pix |= (pix & (0xFF << 24));
|
||||
}
|
||||
break;
|
||||
case 32:
|
||||
while (count--) {
|
||||
*((uint32_t*)buf) &= mask.whole;
|
||||
buf += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
void UpdateSystemColorMaps(int lcd)
|
||||
{
|
||||
switch(systemColorDepth) {
|
||||
case 8:
|
||||
{
|
||||
for(int i = 0; i < 0x10000; i++) {
|
||||
systemColorMap8[i] = (((i & 0x1f) << systemRedShift) & 0xE0) |
|
||||
((((i & 0x3e0) >> 5) << systemGreenShift) & 0x1C) |
|
||||
((((i & 0x7c00) >> 10) << systemBlueShift) & 0x3);
|
||||
}
|
||||
*buf++ = final_pix;
|
||||
}
|
||||
break;
|
||||
case 16:
|
||||
{
|
||||
for(int i = 0; i < 0x10000; i++) {
|
||||
systemColorMap16[i] = ((i & 0x1f) << systemRedShift) |
|
||||
(((i & 0x3e0) >> 5) << systemGreenShift) |
|
||||
(((i & 0x7c00) >> 10) << systemBlueShift);
|
||||
}
|
||||
if (lcd == 1) gbafilter_pal(systemColorMap16, 0x10000);
|
||||
}
|
||||
break;
|
||||
case 24:
|
||||
case 32:
|
||||
{
|
||||
for(int i = 0; i < 0x10000; i++) {
|
||||
systemColorMap32[i] = ((i & 0x1f) << systemRedShift) |
|
||||
(((i & 0x3e0) >> 5) << systemGreenShift) |
|
||||
(((i & 0x7c00) >> 10) << systemBlueShift);
|
||||
}
|
||||
if (lcd == 1) gbafilter_pal32(systemColorMap32, 0x10000);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
@@ -4,8 +4,9 @@
|
||||
#include <cstdint>
|
||||
|
||||
void gbafilter_update_colors(bool lcd = false);
|
||||
void gbafilter_pal8(uint8_t* buf, int count);
|
||||
void gbafilter_pal(uint16_t* buf, int count);
|
||||
void gbafilter_pal32(uint32_t* buf, int count);
|
||||
void gbafilter_pad(uint8_t* buf, int count);
|
||||
void gbafilter_set_params(int color_mode, float darken_screen);
|
||||
|
||||
#endif // VBAM_COMPONENTS_FILTERS_AGB_FILTERS_AGB_H_
|
||||
|
6
src/components/filters_cgb/CMakeLists.txt
Normal file
6
src/components/filters_cgb/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
add_library(vbam-components-filters-cgb OBJECT)
|
||||
|
||||
target_sources(vbam-components-filters-cgb
|
||||
PRIVATE filters_cgb.cpp
|
||||
PUBLIC filters_cgb.h
|
||||
)
|
352
src/components/filters_cgb/filters_cgb.cpp
Normal file
352
src/components/filters_cgb/filters_cgb.cpp
Normal file
@@ -0,0 +1,352 @@
|
||||
/*
|
||||
* GBC Color Correction Shader Implementation
|
||||
*
|
||||
* Shader modified by Pokefan531.
|
||||
* Color Mangler
|
||||
* Original Author: hunterk
|
||||
* Original License: Public domain
|
||||
*
|
||||
* This code is adapted from the original shader logic.
|
||||
*/
|
||||
|
||||
#include "components/filters_cgb/filters_cgb.h"
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
|
||||
extern int systemColorDepth;
|
||||
extern int systemRedShift;
|
||||
extern int systemGreenShift;
|
||||
extern int systemBlueShift;
|
||||
|
||||
extern uint8_t systemColorMap8[0x10000];
|
||||
extern uint16_t systemColorMap16[0x10000];
|
||||
extern uint32_t systemColorMap32[0x10000];
|
||||
|
||||
// --- Global Constants and Variables for GBC Color Correction ---
|
||||
// Define the color profile matrix as a static const float 2D array
|
||||
// This replicates the column-major order of GLSL mat4 for easier translation.
|
||||
// Format: { {col0_row0, col0_row1, col0_row2, col0_row3}, ... }
|
||||
static const float GBC_sRGB[4][4] = {
|
||||
{0.905f, 0.10f, 0.1575f, 0.0f}, // Column 0 (R output contributions from R, G, B, A)
|
||||
{0.195f, 0.65f, 0.1425f, 0.0f}, // Column 1 (G output contributions from R, G, B, A)
|
||||
{-0.10f, 0.25f, 0.70f, 0.0f}, // Column 2 (B output contributions from R, G, B, A)
|
||||
{0.0f, 0.0f, 0.0f, 0.91f} // Column 3 (A/Luminance contribution)
|
||||
};
|
||||
|
||||
static const float GBC_DCI[4][4] = {
|
||||
{0.76f, 0.125f, 0.16f, 0.0f},
|
||||
{0.27f, 0.6375f, 0.18f, 0.0f},
|
||||
{-0.03f, 0.2375f, 0.66f, 0.0f},
|
||||
{0.0f, 0.0f, 0.0f, 0.97f}
|
||||
};
|
||||
|
||||
static const float GBC_Rec2020[4][4] = {
|
||||
{0.61f, 0.155f, 0.16f, 0.0f},
|
||||
{0.345f, 0.615f, 0.1875f, 0.0f},
|
||||
{0.045f, 0.23f, 0.6525f, 0.0f},
|
||||
{0.0f, 0.0f, 0.0f, 1.0f}
|
||||
};
|
||||
|
||||
// Screen lightening factor. Default to 0.0f.
|
||||
static float lighten_screen = 0.0f;
|
||||
|
||||
// Color mode (0 for sRGB, 1 for DCI, 2 for Rec2020). Default to sRGB (0).
|
||||
static int color_mode = 0;
|
||||
|
||||
// Pointer to the currently selected color profile matrix.
|
||||
static const float (*profile)[4];
|
||||
|
||||
// Global constants from the shader for gamma correction values
|
||||
static const float target_gamma = 2.2f;
|
||||
static const float display_gamma = 2.2f;
|
||||
|
||||
|
||||
// --- Function Implementations ---
|
||||
|
||||
// Forward declaration of a helper function to set the profile based on color_mode
|
||||
static void set_profile_from_mode();
|
||||
|
||||
// This constructor-like function runs once when the program starts.
|
||||
struct GbcfilterInitializer {
|
||||
GbcfilterInitializer() {
|
||||
set_profile_from_mode();
|
||||
}
|
||||
};
|
||||
static GbcfilterInitializer __gbcfilter_initializer;
|
||||
|
||||
|
||||
// Helper function to set the 'profile' pointer based on the 'color_mode' variable.
|
||||
static void set_profile_from_mode() {
|
||||
if (color_mode == 0) {
|
||||
profile = GBC_sRGB;
|
||||
}
|
||||
else if (color_mode == 1) {
|
||||
profile = GBC_DCI;
|
||||
}
|
||||
else if (color_mode == 2) {
|
||||
profile = GBC_Rec2020;
|
||||
}
|
||||
else {
|
||||
profile = GBC_sRGB; // Default to sRGB if an invalid mode is set
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Public function to set color mode and darken screen from external calls
|
||||
void gbcfilter_set_params(int new_color_mode, float new_lighten_screen) {
|
||||
color_mode = new_color_mode;
|
||||
lighten_screen = fmaxf(0.0f, fminf(1.0f, new_lighten_screen)); // Clamp to 0.0-1.0
|
||||
|
||||
// Call the helper to update 'profile' based on the new 'color_mode'
|
||||
set_profile_from_mode();
|
||||
}
|
||||
|
||||
void gbcfilter_update_colors(bool lcd) {
|
||||
switch (systemColorDepth) {
|
||||
case 8: {
|
||||
for (int i = 0; i < 0x10000; i++) {
|
||||
systemColorMap8[i] = (uint8_t)((((i & 0x1f) << 3) & 0xE0) |
|
||||
((((i & 0x3e0) >> 5) << 0) & 0x1C) |
|
||||
((((i & 0x7c00) >> 10) >> 3) & 0x3));
|
||||
}
|
||||
if (lcd)
|
||||
gbcfilter_pal8(systemColorMap8, 0x10000);
|
||||
} break;
|
||||
case 16: {
|
||||
for (int i = 0x0; i < 0x10000; i++) {
|
||||
systemColorMap16[i] = ((i & 0x1f) << systemRedShift) |
|
||||
(((i & 0x3e0) >> 5) << systemGreenShift) |
|
||||
(((i & 0x7c00) >> 10) << systemBlueShift);
|
||||
}
|
||||
if (lcd)
|
||||
gbcfilter_pal(systemColorMap16, 0x10000);
|
||||
} break;
|
||||
case 24:
|
||||
case 32: {
|
||||
for (int i = 0; i < 0x10000; i++) {
|
||||
systemColorMap32[i] = ((i & 0x1f) << systemRedShift) |
|
||||
(((i & 0x3e0) >> 5) << systemGreenShift) |
|
||||
(((i & 0x7c00) >> 10) << systemBlueShift);
|
||||
}
|
||||
if (lcd)
|
||||
gbcfilter_pal32(systemColorMap32, 0x10000);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void gbcfilter_pal8(uint8_t* buf, int count)
|
||||
{
|
||||
// Pre-calculate constants for efficiency within function scope
|
||||
const float target_gamma_exponent = target_gamma + (lighten_screen * -1.0f);
|
||||
const float display_gamma_reciprocal = 1.0f / display_gamma;
|
||||
const float luminance_factor = profile[3][3]; // profile[3].w from GLSL
|
||||
|
||||
while (count--) {
|
||||
uint8_t pix = *buf;
|
||||
|
||||
uint8_t original_r_val_3bit = (uint8_t)((pix & 0xE0) >> 5);
|
||||
uint8_t original_g_val_3bit = (uint8_t)((pix & 0x1C) >> 2);
|
||||
uint8_t original_b_val_2bit = (uint8_t)(pix & 0x3);
|
||||
|
||||
// Normalize to 0.0-1.0 for calculations
|
||||
float r = (float)original_r_val_3bit / 7.0f;
|
||||
float g = (float)original_g_val_3bit / 7.0f;
|
||||
float b = (float)original_b_val_2bit / 3.0f;
|
||||
|
||||
// 1. Apply initial gamma (including lighten_screen as exponent) to convert to linear space.
|
||||
// This step will affect non-"white" values.
|
||||
r = powf(r, target_gamma_exponent);
|
||||
g = powf(g, target_gamma_exponent);
|
||||
b = powf(b, target_gamma_exponent);
|
||||
|
||||
// 2. Apply luminance factor and clamp.
|
||||
r = fmaxf(0.0f, fminf(1.0f, r * luminance_factor));
|
||||
g = fmaxf(0.0f, fminf(1.0f, g * luminance_factor));
|
||||
b = fmaxf(0.0f, fminf(1.0f, b * luminance_factor));
|
||||
|
||||
// 3. Apply color profile matrix (using profile[column][row] access)
|
||||
float transformed_r = profile[0][0] * r + profile[1][0] * g + profile[2][0] * b;
|
||||
float transformed_g = profile[0][1] * r + profile[1][1] * g + profile[2][1] * b;
|
||||
float transformed_b = profile[0][2] * r + profile[1][2] * g + profile[2][2] * b;
|
||||
|
||||
// 4. Apply display gamma to convert back for display.
|
||||
transformed_r = copysignf(powf(fabsf(transformed_r), display_gamma_reciprocal), transformed_r);
|
||||
transformed_g = copysignf(powf(fabsf(transformed_g), display_gamma_reciprocal), transformed_g);
|
||||
transformed_b = copysignf(powf(fabsf(transformed_b), display_gamma_reciprocal), transformed_b);
|
||||
|
||||
// Final clamp: ensure values are within 0.0-1.0 range
|
||||
transformed_r = fmaxf(0.0f, fminf(1.0f, transformed_r));
|
||||
transformed_g = fmaxf(0.0f, fminf(1.0f, transformed_g));
|
||||
transformed_b = fmaxf(0.0f, fminf(1.0f, transformed_b));
|
||||
|
||||
// Convert back to 3-bit or 2-bit (0-7 or 0-3) integer and combine into uint8_t
|
||||
// Apply 3-bit or 2-bit to 8-bit conversion, as this palette is for 8-bit output.
|
||||
uint8_t final_red = (uint8_t)(transformed_r * 7.0f + 0.5f);
|
||||
uint8_t final_green = (uint8_t)(transformed_g * 7.0f + 0.5f);
|
||||
uint8_t final_blue = (uint8_t)(transformed_b * 3.0f + 0.5f);
|
||||
|
||||
// Ensure values are strictly within 0-7 or 0-3 range after rounding
|
||||
if (final_red > 7) final_red = 7;
|
||||
if (final_green > 7) final_green = 7;
|
||||
if (final_blue > 3) final_blue = 3;
|
||||
|
||||
*buf++ = ((final_red & 0x7) << 5) |
|
||||
((final_green & 0x7) << 2) |
|
||||
(final_blue & 0x3);
|
||||
}
|
||||
}
|
||||
void gbcfilter_pal(uint16_t* buf, int count)
|
||||
{
|
||||
// Pre-calculate constants for efficiency within function scope
|
||||
const float target_gamma_exponent = target_gamma + (lighten_screen * -1.0f);
|
||||
const float display_gamma_reciprocal = 1.0f / display_gamma;
|
||||
const float luminance_factor = profile[3][3]; // profile[3].w from GLSL
|
||||
|
||||
while (count--) {
|
||||
uint16_t pix = *buf;
|
||||
|
||||
uint8_t original_r_val_5bit = (uint8_t)((pix >> systemRedShift) & 0x1f);
|
||||
uint8_t original_g_val_5bit = (uint8_t)((pix >> systemGreenShift) & 0x1f);
|
||||
uint8_t original_b_val_5bit = (uint8_t)((pix >> systemBlueShift) & 0x1f);
|
||||
|
||||
// Normalize to 0.0-1.0 for calculations
|
||||
float r = (float)original_r_val_5bit / 31.0f;
|
||||
float g = (float)original_g_val_5bit / 31.0f;
|
||||
float b = (float)original_b_val_5bit / 31.0f;
|
||||
|
||||
// 1. Apply initial gamma (including lighten_screen as exponent) to convert to linear space.
|
||||
// This step will affect non-"white" values.
|
||||
r = powf(r, target_gamma_exponent);
|
||||
g = powf(g, target_gamma_exponent);
|
||||
b = powf(b, target_gamma_exponent);
|
||||
|
||||
// 2. Apply luminance factor and clamp.
|
||||
r = fmaxf(0.0f, fminf(1.0f, r * luminance_factor));
|
||||
g = fmaxf(0.0f, fminf(1.0f, g * luminance_factor));
|
||||
b = fmaxf(0.0f, fminf(1.0f, b * luminance_factor));
|
||||
|
||||
// 3. Apply color profile matrix (using profile[column][row] access)
|
||||
float transformed_r = profile[0][0] * r + profile[1][0] * g + profile[2][0] * b;
|
||||
float transformed_g = profile[0][1] * r + profile[1][1] * g + profile[2][1] * b;
|
||||
float transformed_b = profile[0][2] * r + profile[1][2] * g + profile[2][2] * b;
|
||||
|
||||
// 4. Apply display gamma to convert back for display.
|
||||
transformed_r = copysignf(powf(fabsf(transformed_r), display_gamma_reciprocal), transformed_r);
|
||||
transformed_g = copysignf(powf(fabsf(transformed_g), display_gamma_reciprocal), transformed_g);
|
||||
transformed_b = copysignf(powf(fabsf(transformed_b), display_gamma_reciprocal), transformed_b);
|
||||
|
||||
// Final clamp: ensure values are within 0.0-1.0 range
|
||||
transformed_r = fmaxf(0.0f, fminf(1.0f, transformed_r));
|
||||
transformed_g = fmaxf(0.0f, fminf(1.0f, transformed_g));
|
||||
transformed_b = fmaxf(0.0f, fminf(1.0f, transformed_b));
|
||||
|
||||
// Convert back to 5-bit (0-31) integer and combine into uint16_t
|
||||
// Apply 5-bit to 5-bit conversion, as this palette is for 16-bit output.
|
||||
uint8_t final_red = (uint8_t)(transformed_r * 31.0f + 0.5f);
|
||||
uint8_t final_green = (uint8_t)(transformed_g * 31.0f + 0.5f);
|
||||
uint8_t final_blue = (uint8_t)(transformed_b * 31.0f + 0.5f);
|
||||
|
||||
// Ensure values are strictly within 0-31 range after rounding
|
||||
if (final_red > 31) final_red = 31;
|
||||
if (final_green > 31) final_green = 31;
|
||||
if (final_blue > 31) final_blue = 31;
|
||||
|
||||
*buf++ = (final_red << systemRedShift) |
|
||||
(final_green << systemGreenShift) |
|
||||
(final_blue << systemBlueShift);
|
||||
}
|
||||
}
|
||||
|
||||
void gbcfilter_pal32(uint32_t* buf, int count)
|
||||
{
|
||||
// Pre-calculate constants for efficiency within function scope
|
||||
const float target_gamma_exponent = target_gamma + (lighten_screen * -1.0f);
|
||||
const float display_gamma_reciprocal = 1.0f / display_gamma;
|
||||
const float luminance_factor = profile[3][3]; // profile[3].w from GLSL
|
||||
|
||||
while (count--) {
|
||||
uint32_t pix = *buf;
|
||||
|
||||
// Extract original 5-bit R, G, B values from the shifted positions in the 32-bit pixel.
|
||||
// These shifts pull out the 5-bit value from its shifted position (e.g., bits 3-7 for Red).
|
||||
uint8_t original_r_val_5bit = (uint8_t)((pix >> systemRedShift) & 0x1f);
|
||||
uint8_t original_g_val_5bit = (uint8_t)((pix >> systemGreenShift) & 0x1f);
|
||||
uint8_t original_b_val_5bit = (uint8_t)((pix >> systemBlueShift) & 0x1f);
|
||||
|
||||
|
||||
// Normalize to 0.0-1.0 for calculations
|
||||
float r = (float)original_r_val_5bit / 31.0f;
|
||||
float g = (float)original_g_val_5bit / 31.0f;
|
||||
float b = (float)original_b_val_5bit / 31.0f;
|
||||
|
||||
// 1. Apply initial gamma (including lighten_screen as exponent) to convert to linear space.
|
||||
r = powf(r, target_gamma_exponent);
|
||||
g = powf(g, target_gamma_exponent);
|
||||
b = powf(b, target_gamma_exponent);
|
||||
|
||||
// 2. Apply luminance factor and clamp.
|
||||
r = fmaxf(0.0f, fminf(1.0f, r * luminance_factor));
|
||||
g = fmaxf(0.0f, fminf(1.0f, g * luminance_factor));
|
||||
b = fmaxf(0.0f, fminf(1.0f, b * luminance_factor));
|
||||
|
||||
// 3. Apply color profile matrix
|
||||
float transformed_r = profile[0][0] * r + profile[1][0] * g + profile[2][0] * b;
|
||||
float transformed_g = profile[0][1] * r + profile[1][1] * g + profile[2][1] * b;
|
||||
float transformed_b = profile[0][2] * r + profile[1][2] * g + profile[2][2] * b;
|
||||
|
||||
// 4. Apply display gamma.
|
||||
transformed_r = copysignf(powf(fabsf(transformed_r), display_gamma_reciprocal), transformed_r);
|
||||
transformed_g = copysignf(powf(fabsf(transformed_g), display_gamma_reciprocal), transformed_g);
|
||||
transformed_b = copysignf(powf(fabsf(transformed_b), display_gamma_reciprocal), transformed_b);
|
||||
|
||||
// Final clamp: ensure values are within 0.0-1.0 range
|
||||
transformed_r = fmaxf(0.0f, fminf(1.0f, transformed_r));
|
||||
transformed_g = fmaxf(0.0f, fminf(1.0f, transformed_g));
|
||||
transformed_b = fmaxf(0.0f, fminf(1.0f, transformed_b));
|
||||
|
||||
|
||||
// Convert the floating-point values to 8-bit integer components (0-255).
|
||||
uint8_t final_red_8bit = (uint8_t)(transformed_r * 255.0f + 0.5f);
|
||||
uint8_t final_green_8bit = (uint8_t)(transformed_g * 255.0f + 0.5f);
|
||||
uint8_t final_blue_8bit = (uint8_t)(transformed_b * 255.0f + 0.5f);
|
||||
|
||||
// Ensure values are strictly within 0-255 range after rounding
|
||||
if (final_red_8bit > 255) final_red_8bit = 255;
|
||||
if (final_green_8bit > 255) final_green_8bit = 255;
|
||||
if (final_blue_8bit > 255) final_blue_8bit = 255;
|
||||
|
||||
// --- NEW PACKING LOGIC ---
|
||||
// This is the critical change to correctly map 8-bit color to the 5-bit shifted format,
|
||||
// while allowing FFFFFF.
|
||||
// It uses the top 5 bits of the 8-bit value for the GBC's 5-bit component position,
|
||||
// and the bottom 3 bits to fill the lower, normally zeroed, positions.
|
||||
|
||||
uint32_t final_pix = 0;
|
||||
|
||||
// Red component
|
||||
// 5 most significant bits (MSBs) for the 'systemRedShift' position
|
||||
final_pix |= ((final_red_8bit >> 3) & 0x1f) << systemRedShift;
|
||||
// 3 least significant bits (LSBs) for the 'base' position (systemRedShift - 3)
|
||||
final_pix |= (final_red_8bit & 0x07) << (systemRedShift - 3);
|
||||
|
||||
|
||||
// Green component
|
||||
// 5 MSBs for the 'systemGreenShift' position
|
||||
final_pix |= ((final_green_8bit >> 3) & 0x1f) << systemGreenShift;
|
||||
// 3 LSBs for the 'base' position (systemGreenShift - 3)
|
||||
final_pix |= (final_green_8bit & 0x07) << (systemGreenShift - 3);
|
||||
|
||||
// Blue component
|
||||
// 5 MSBs for the 'systemBlueShift' position
|
||||
final_pix |= ((final_blue_8bit >> 3) & 0x1f) << systemBlueShift;
|
||||
// 3 LSBs for the 'base' position (systemBlueShift - 3)
|
||||
final_pix |= (final_blue_8bit & 0x07) << (systemBlueShift - 3);
|
||||
|
||||
// Preserve existing alpha if present (assuming it's at bits 24-31 for 32-bit depth)
|
||||
if (systemColorDepth == 32) {
|
||||
final_pix |= (pix & (0xFF << 24));
|
||||
}
|
||||
|
||||
*buf++ = final_pix;
|
||||
}
|
||||
}
|
12
src/components/filters_cgb/filters_cgb.h
Normal file
12
src/components/filters_cgb/filters_cgb.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef VBAM_COMPONENTS_FILTERS_CGB_FILTERS_CGB_H_
|
||||
#define VBAM_COMPONENTS_FILTERS_CGB_FILTERS_CGB_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
void gbcfilter_update_colors(bool lcd = false);
|
||||
void gbcfilter_pal8(uint8_t* buf, int count);
|
||||
void gbcfilter_pal(uint16_t* buf, int count);
|
||||
void gbcfilter_pal32(uint32_t* buf, int count);
|
||||
void gbcfilter_set_params(int color_mode, float lighten_screen);
|
||||
|
||||
#endif // VBAM_COMPONENTS_FILTERS_CGB_FILTERS_CGB_H_
|
@@ -120,9 +120,7 @@ if(ENABLE_LINK)
|
||||
gba/gbaLink.h
|
||||
)
|
||||
|
||||
target_include_directories(vbam-core
|
||||
PRIVATE ${SFML_INCLUDE_DIR}
|
||||
)
|
||||
target_include_directories(vbam-core PRIVATE ${SFML_INCLUDE_DIR} ${SFML_INCLUDE_DIR}/SFML/Network ${SFML_INCLUDE_DIR}/SFML/System)
|
||||
|
||||
target_link_libraries(vbam-core
|
||||
PRIVATE ${SFML_LIBRARIES}
|
||||
|
@@ -26,6 +26,7 @@ uint8_t *utilLoad(const char *, bool (*)(const char *), uint8_t *, int &);
|
||||
IMAGE_TYPE utilFindType(const char *);
|
||||
bool utilIsGBAImage(const char *);
|
||||
bool utilIsGBImage(const char *);
|
||||
bool utilIsTARAchive(const char *);
|
||||
|
||||
#if defined(__LIBRETRO__)
|
||||
|
||||
@@ -58,4 +59,4 @@ void utilWriteInt(gzFile, int);
|
||||
|
||||
#endif // defined(__LIBRETRO__)
|
||||
|
||||
#endif // VBAM_CORE_BASE_FILE_UTIL_H_
|
||||
#endif // VBAM_CORE_BASE_FILE_UTIL_H_
|
||||
|
@@ -19,12 +19,12 @@ FILE* utilOpenFile(const char* filename, const char* mode) {
|
||||
#ifdef _WIN32
|
||||
std::wstring wfilename = core::internal::ToUTF16(filename);
|
||||
if (wfilename.empty()) {
|
||||
return nullptr;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
std::wstring wmode = core::internal::ToUTF16(mode);
|
||||
if (wmode.empty()) {
|
||||
return nullptr;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if __STDC_WANT_SECURE_LIB__
|
||||
@@ -39,12 +39,21 @@ FILE* utilOpenFile(const char* filename, const char* mode) {
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
bool utilIsTARAchive(const char* file) {
|
||||
const char* p = strrchr(file, '.');
|
||||
|
||||
if ((strcasecmp(p, ".tar") == 0))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool utilIsGBAImage(const char* file) {
|
||||
coreOptions.cpuIsMultiBoot = false;
|
||||
if (strlen(file) > 4) {
|
||||
const char* p = strrchr(file, '.');
|
||||
|
||||
if (p != nullptr) {
|
||||
if (p != NULL) {
|
||||
if ((strcasecmp(p, ".agb") == 0) || (strcasecmp(p, ".gba") == 0) ||
|
||||
(strcasecmp(p, ".bin") == 0) || (strcasecmp(p, ".elf") == 0))
|
||||
return true;
|
||||
@@ -62,7 +71,7 @@ bool utilIsGBImage(const char* file) {
|
||||
if (strlen(file) > 4) {
|
||||
const char* p = strrchr(file, '.');
|
||||
|
||||
if (p != nullptr) {
|
||||
if (p != NULL) {
|
||||
if ((strcasecmp(p, ".dmg") == 0) || (strcasecmp(p, ".gb") == 0) ||
|
||||
(strcasecmp(p, ".gbc") == 0) || (strcasecmp(p, ".cgb") == 0) ||
|
||||
(strcasecmp(p, ".sgb") == 0))
|
||||
|
@@ -24,7 +24,7 @@ bool utilIsGzipFile(const char* file) {
|
||||
if (strlen(file) > 3) {
|
||||
const char* p = strrchr(file, '.');
|
||||
|
||||
if (p != nullptr) {
|
||||
if (p != NULL) {
|
||||
if (strcasecmp(p, ".gz") == 0)
|
||||
return true;
|
||||
if (strcasecmp(p, ".z") == 0)
|
||||
@@ -36,13 +36,13 @@ bool utilIsGzipFile(const char* file) {
|
||||
}
|
||||
|
||||
// Opens and scans archive using accept(). Returns fex_t if found.
|
||||
// If error or not found, displays message and returns nullptr.
|
||||
// If error or not found, displays message and returns NULL.
|
||||
fex_t* scanArchive(const char* file, bool (*accept)(const char*), char (&buffer)[2048]) {
|
||||
fex_t* fe;
|
||||
fex_err_t err = fex_open(&fe, file);
|
||||
if (!fe) {
|
||||
systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s: %s"), file, err);
|
||||
return nullptr;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Scan filenames
|
||||
@@ -67,14 +67,14 @@ fex_t* scanArchive(const char* file, bool (*accept)(const char*), char (&buffer)
|
||||
if (err) {
|
||||
systemMessage(MSG_BAD_ZIP_FILE, N_("Cannot read archive %s: %s"), file, err);
|
||||
fex_close(fe);
|
||||
return nullptr;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
systemMessage(MSG_NO_IMAGE_ON_ZIP, N_("No image found in file %s"), file);
|
||||
fex_close(fe);
|
||||
return nullptr;
|
||||
return NULL;
|
||||
}
|
||||
return fe;
|
||||
}
|
||||
@@ -101,10 +101,10 @@ IMAGE_TYPE utilFindType(const char* file, char (&buffer)[2048]) {
|
||||
return utilIsGBAImage(file) ? IMAGE_GBA : IMAGE_GB;
|
||||
}
|
||||
|
||||
int(ZEXPORT* utilGzWriteFunc)(gzFile, const voidp, unsigned int) = nullptr;
|
||||
int(ZEXPORT* utilGzReadFunc)(gzFile, voidp, unsigned int) = nullptr;
|
||||
int(ZEXPORT* utilGzCloseFunc)(gzFile) = nullptr;
|
||||
z_off_t(ZEXPORT* utilGzSeekFunc)(gzFile, z_off_t, int) = nullptr;
|
||||
int(ZEXPORT* utilGzWriteFunc)(gzFile, const voidp, unsigned int) = NULL;
|
||||
int(ZEXPORT* utilGzReadFunc)(gzFile, voidp, unsigned int) = NULL;
|
||||
int(ZEXPORT* utilGzCloseFunc)(gzFile) = NULL;
|
||||
z_off_t(ZEXPORT* utilGzSeekFunc)(gzFile, z_off_t, int) = NULL;
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -113,7 +113,7 @@ uint8_t* utilLoad(const char* file, bool (*accept)(const char*), uint8_t* data,
|
||||
char buffer[2048];
|
||||
fex_t* fe = scanArchive(file, accept, buffer);
|
||||
if (!fe)
|
||||
return nullptr;
|
||||
return NULL;
|
||||
|
||||
// Allocate space for image
|
||||
fex_err_t err = fex_stat(fe);
|
||||
@@ -122,17 +122,17 @@ uint8_t* utilLoad(const char* file, bool (*accept)(const char*), uint8_t* data,
|
||||
size = fileSize;
|
||||
|
||||
if (size > MAX_CART_SIZE)
|
||||
return nullptr;
|
||||
return NULL;
|
||||
|
||||
uint8_t* image = data;
|
||||
|
||||
if (image == nullptr) {
|
||||
if (image == NULL) {
|
||||
// allocate buffer memory if none was passed to the function
|
||||
image = (uint8_t*)malloc(utilGetSize(size));
|
||||
if (image == nullptr) {
|
||||
if (image == NULL) {
|
||||
fex_close(fe);
|
||||
systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), "data");
|
||||
return nullptr;
|
||||
return NULL;
|
||||
}
|
||||
size = fileSize;
|
||||
}
|
||||
@@ -141,11 +141,12 @@ uint8_t* utilLoad(const char* file, bool (*accept)(const char*), uint8_t* data,
|
||||
int read = fileSize <= size ? fileSize : size; // do not read beyond file
|
||||
err = fex_read(fe, image, read);
|
||||
fex_close(fe);
|
||||
|
||||
if (err) {
|
||||
systemMessage(MSG_ERROR_READING_IMAGE, N_("Error reading image from %s: %s"), buffer, err);
|
||||
if (data == nullptr)
|
||||
if (data == NULL)
|
||||
free(image);
|
||||
return nullptr;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size = fileSize;
|
||||
@@ -182,7 +183,7 @@ gzFile utilAutoGzOpen(const char* file, const char* mode) {
|
||||
|
||||
std::wstring wfile = core::internal::ToUTF16(file);
|
||||
if (wfile.empty()) {
|
||||
return nullptr;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return gzopen_w(wfile.data(), mode);
|
||||
|
@@ -25,12 +25,12 @@ static int utilGetSize(int size) {
|
||||
}
|
||||
|
||||
uint8_t* utilLoad(const char* file, bool (*)(const char*), uint8_t* data, int& size) {
|
||||
FILE* fp = nullptr;
|
||||
FILE* fp = NULL;
|
||||
|
||||
fp = fopen(file, "rb");
|
||||
if (!fp) {
|
||||
log("Failed to open file %s", file);
|
||||
return nullptr;
|
||||
return NULL;
|
||||
}
|
||||
fseek(fp, 0, SEEK_END); // go to end
|
||||
|
||||
@@ -38,18 +38,18 @@ uint8_t* utilLoad(const char* file, bool (*)(const char*), uint8_t* data, int& s
|
||||
rewind(fp);
|
||||
|
||||
uint8_t* image = data;
|
||||
if (image == nullptr) {
|
||||
if (image == NULL) {
|
||||
image = (uint8_t*)malloc(utilGetSize(size));
|
||||
if (image == nullptr) {
|
||||
if (image == NULL) {
|
||||
log("Failed to allocate memory for %s", file);
|
||||
return nullptr;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (fread(image, 1, size, fp) != (size_t)size) {
|
||||
log("Failed to read from %s", file);
|
||||
fclose(fp);
|
||||
return nullptr;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
@@ -10,8 +10,6 @@ extern "C" {
|
||||
#include "core/base/system.h"
|
||||
#include "core/base/message.h"
|
||||
|
||||
bool no_border = false;
|
||||
|
||||
bool utilWritePNGFile(const char* fileName, int w, int h, uint8_t* pix) {
|
||||
static constexpr size_t kNumChannels = 3;
|
||||
uint8_t* writeBuffer = new uint8_t[w * h * kNumChannels];
|
||||
@@ -23,7 +21,7 @@ bool utilWritePNGFile(const char* fileName, int w, int h, uint8_t* pix) {
|
||||
|
||||
switch (systemColorDepth) {
|
||||
case 8: {
|
||||
uint8_t* pixU8 = (uint8_t*)pix + (w);
|
||||
uint8_t* pixU8 = (uint8_t*)pix + (w + 4);
|
||||
for (int y = 0; y < sizeY; y++) {
|
||||
for (int x = 0; x < sizeX; x++, pixU8++) {
|
||||
// White color fix
|
||||
@@ -38,9 +36,7 @@ bool utilWritePNGFile(const char* fileName, int w, int h, uint8_t* pix) {
|
||||
}
|
||||
}
|
||||
|
||||
if (no_border == false) {
|
||||
pixU8 += 2;
|
||||
}
|
||||
pixU8 += 4;
|
||||
}
|
||||
} break;
|
||||
case 16: {
|
||||
@@ -155,11 +151,7 @@ bool utilWriteBMPFile(const char* fileName, int w, int h, uint8_t* pix) {
|
||||
switch (systemColorDepth) {
|
||||
case 8: {
|
||||
uint8_t* pixU8 = 0;
|
||||
if (no_border == false) {
|
||||
pixU8 = (uint8_t*)pix + ((w + 2) * (h));
|
||||
} else {
|
||||
pixU8 = (uint8_t*)pix + ((w) * (h));
|
||||
}
|
||||
pixU8 = (uint8_t*)pix + ((w + 4) * (h));
|
||||
|
||||
for (int y = 0; y < sizeY; y++) {
|
||||
for (int x = 0; x < sizeX; x++, pixU8++) {
|
||||
@@ -175,13 +167,11 @@ bool utilWriteBMPFile(const char* fileName, int w, int h, uint8_t* pix) {
|
||||
}
|
||||
}
|
||||
|
||||
if (no_border == false) {
|
||||
pixU8++;
|
||||
pixU8++;
|
||||
pixU8 -= 2 * (w + 2);
|
||||
} else {
|
||||
pixU8 -= 2 * (w);
|
||||
}
|
||||
pixU8++;
|
||||
pixU8++;
|
||||
pixU8++;
|
||||
pixU8++;
|
||||
pixU8 -= 2 * (w + 4);
|
||||
|
||||
fwrite(writeBuffer, 1, 3 * w, fp);
|
||||
b = writeBuffer;
|
||||
|
@@ -10,7 +10,7 @@ namespace internal {
|
||||
#if defined(_WIN32)
|
||||
|
||||
std::wstring ToUTF16(const char* utf8) {
|
||||
int len = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, nullptr, 0);
|
||||
int len = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0);
|
||||
if (len == 0) {
|
||||
return std::wstring();
|
||||
}
|
||||
|
@@ -74,8 +74,8 @@ extern struct CoreOptions {
|
||||
uint32_t speedup_throttle = 100;
|
||||
uint32_t speedup_frame_skip = 9;
|
||||
uint32_t throttle = 100;
|
||||
const char* loadDotCodeFile = nullptr;
|
||||
const char* saveDotCodeFile = nullptr;
|
||||
const char* loadDotCodeFile = NULL;
|
||||
const char* saveDotCodeFile = NULL;
|
||||
} coreOptions;
|
||||
|
||||
// The following functions must be implemented by the emulator.
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/* Compiler.h : Compiler specific defines and pragmas
|
||||
2024-01-22 : Igor Pavlov : Public domain */
|
||||
: Igor Pavlov : Public domain */
|
||||
|
||||
#ifndef ZIP7_INC_COMPILER_H
|
||||
#define ZIP7_INC_COMPILER_H
|
||||
@@ -183,6 +183,16 @@ typedef void (*Z7_void_Function)(void);
|
||||
#define Z7_ATTRIB_NO_VECTORIZE
|
||||
#endif
|
||||
|
||||
#if defined(Z7_MSC_VER_ORIGINAL) && (Z7_MSC_VER_ORIGINAL >= 1920)
|
||||
#define Z7_PRAGMA_OPTIMIZE_FOR_CODE_SIZE _Pragma("optimize ( \"s\", on )")
|
||||
#define Z7_PRAGMA_OPTIMIZE_DEFAULT _Pragma("optimize ( \"\", on )")
|
||||
#else
|
||||
#define Z7_PRAGMA_OPTIMIZE_FOR_CODE_SIZE
|
||||
#define Z7_PRAGMA_OPTIMIZE_DEFAULT
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if defined(MY_CPU_X86_OR_AMD64) && ( \
|
||||
defined(__clang__) && (__clang_major__ >= 4) \
|
||||
|| defined(__GNUC__) && (__GNUC__ >= 5))
|
||||
|
@@ -47,6 +47,12 @@ MY_CPU_64BIT means that processor can work with 64-bit registers.
|
||||
#define MY_CPU_SIZEOF_POINTER 4
|
||||
#endif
|
||||
|
||||
#if defined(__SSE2__) \
|
||||
|| defined(MY_CPU_AMD64) \
|
||||
|| defined(_M_IX86_FP) && (_M_IX86_FP >= 2)
|
||||
#define MY_CPU_SSE2
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(_M_ARM64) \
|
||||
|| defined(_M_ARM64EC) \
|
||||
@@ -571,10 +577,12 @@ problem-4 : performace:
|
||||
#define Z7_CONV_BE_TO_NATIVE_CONST32(v) (v)
|
||||
#define Z7_CONV_LE_TO_NATIVE_CONST32(v) Z7_BSWAP32_CONST(v)
|
||||
#define Z7_CONV_NATIVE_TO_BE_32(v) (v)
|
||||
// #define Z7_GET_NATIVE16_FROM_2_BYTES(b0, b1) ((b1) | ((b0) << 8))
|
||||
#elif defined(MY_CPU_LE)
|
||||
#define Z7_CONV_BE_TO_NATIVE_CONST32(v) Z7_BSWAP32_CONST(v)
|
||||
#define Z7_CONV_LE_TO_NATIVE_CONST32(v) (v)
|
||||
#define Z7_CONV_NATIVE_TO_BE_32(v) Z7_BSWAP32(v)
|
||||
// #define Z7_GET_NATIVE16_FROM_2_BYTES(b0, b1) ((b0) | ((b1) << 8))
|
||||
#else
|
||||
#error Stop_Compiling_Unknown_Endian_CONV
|
||||
#endif
|
||||
|
@@ -57,15 +57,64 @@ target_sources(vbam-fex
|
||||
fex/Gzip_Extractor.h
|
||||
fex/Gzip_Reader.cpp
|
||||
fex/Gzip_Reader.h
|
||||
fex/BZ2_Extractor.cpp
|
||||
fex/BZ2_Extractor.h
|
||||
fex/BZ2_Reader.cpp
|
||||
fex/BZ2_Reader.h
|
||||
fex/XZ_Extractor.cpp
|
||||
fex/XZ_Extractor.h
|
||||
fex/XZ_Reader.cpp
|
||||
fex/XZ_Reader.h
|
||||
fex/LZ_Extractor.cpp
|
||||
fex/LZ_Extractor.h
|
||||
fex/LZ_Reader.cpp
|
||||
fex/LZ_Reader.h
|
||||
fex/Rar_Extractor.cpp
|
||||
fex/Rar_Extractor.h
|
||||
fex/Tar_Extractor.cpp
|
||||
fex/Tar_Extractor.h
|
||||
fex/Zip7_Extractor.cpp
|
||||
fex/Zip7_Extractor.h
|
||||
fex/Zip_Extractor.cpp
|
||||
fex/Zip_Extractor.h
|
||||
fex/Zlib_Inflater.cpp
|
||||
fex/Zlib_Inflater.h
|
||||
|
||||
fex/LZMA_Inflater.cpp
|
||||
fex/LZMA_Inflater.h
|
||||
fex/BZ2_Inflater.cpp
|
||||
fex/BZ2_Inflater.h
|
||||
unrar/archive.cpp
|
||||
unrar/arcread.cpp
|
||||
unrar/blake2s_sse.cpp
|
||||
unrar/blake2s.cpp
|
||||
unrar/blake2sp.cpp
|
||||
unrar/coder.cpp
|
||||
unrar/crc.cpp
|
||||
unrar/encname.cpp
|
||||
unrar/extract.cpp
|
||||
unrar/getbits.cpp
|
||||
unrar/hash.cpp
|
||||
unrar/headers.cpp
|
||||
unrar/model.cpp
|
||||
unrar/pathfn.cpp
|
||||
unrar/rarvm.cpp
|
||||
unrar/rarvmtbl.cpp
|
||||
unrar/rawread.cpp
|
||||
unrar/secpassword.cpp
|
||||
unrar/strfn.cpp
|
||||
unrar/suballoc.cpp
|
||||
unrar/timefn.cpp
|
||||
unrar/unicode.cpp
|
||||
unrar/unpack.cpp
|
||||
unrar/unpack15.cpp
|
||||
unrar/unpack20.cpp
|
||||
unrar/unpack30.cpp
|
||||
unrar/unpack50.cpp
|
||||
unrar/unpack50frag.cpp
|
||||
unrar/unpackinline.cpp
|
||||
unrar/unrar_misc.cpp
|
||||
unrar/unrar_open.cpp
|
||||
unrar/unrar.cpp
|
||||
PUBLIC
|
||||
fex.h
|
||||
)
|
||||
@@ -74,6 +123,28 @@ target_include_directories(vbam-fex
|
||||
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${ZLIB_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
find_library(BZ2_LIBRARY NAMES bz2d bz2)
|
||||
else()
|
||||
find_library(BZ2_LIBRARY bz2)
|
||||
endif()
|
||||
|
||||
find_library(LZMA_LIBRARY lzma)
|
||||
|
||||
if (BZ2_LIBRARY AND ENABLE_BZ2)
|
||||
target_compile_definitions(vbam-fex PRIVATE FEX_ENABLE_BZ2=1)
|
||||
target_link_libraries(vbam-fex
|
||||
PRIVATE ${BZ2_LIBRARY}
|
||||
)
|
||||
endif()
|
||||
|
||||
if (LZMA_LIBRARY AND ENABLE_LZMA)
|
||||
target_compile_definitions(vbam-fex PRIVATE FEX_ENABLE_LZMA=1)
|
||||
target_link_libraries(vbam-fex
|
||||
PRIVATE ${LZMA_LIBRARY}
|
||||
)
|
||||
endif()
|
||||
|
||||
target_link_libraries(vbam-fex
|
||||
PRIVATE ${ZLIB_LIBRARY}
|
||||
)
|
||||
|
@@ -56,7 +56,7 @@ int fex_has_extension(const char str[], const char extension[]);
|
||||
Returns usual file extension this should have (e.g. ".zip", ".gz", etc.).
|
||||
Returns "" if file header is not recognized. */
|
||||
const char *fex_identify_header(const void *header);
|
||||
enum { fex_identify_header_size = 16 };
|
||||
enum { fex_identify_header_size = 263 };
|
||||
|
||||
/** Determines type based on extension of a file path, or just a lone extension
|
||||
(must include '.', e.g. ".zip", NOT just "zip"). Returns NULL if extension is
|
||||
|
103
src/core/fex/fex/BZ2_Extractor.cpp
Normal file
103
src/core/fex/fex/BZ2_Extractor.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
// File_Extractor 1.0.0. http://www.slack.net/~ant/
|
||||
|
||||
#if FEX_ENABLE_BZ2
|
||||
|
||||
#include "BZ2_Extractor.h"
|
||||
#include <zlib.h>
|
||||
|
||||
/* Copyright (C) 2005-2009 Shay Green. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
General Public License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version. This
|
||||
module is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
details. You should have received a copy of the GNU Lesser General Public
|
||||
License along with this module; if not, write to the Free Software Foundation,
|
||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||
|
||||
#include "blargg_source.h"
|
||||
|
||||
// TODO: could close file once data has been read into memory
|
||||
|
||||
static blargg_err_t init_bz2_file()
|
||||
{
|
||||
get_crc_table(); // initialize zlib's CRC-32 tables
|
||||
return blargg_ok;
|
||||
}
|
||||
|
||||
static File_Extractor* new_bzip2()
|
||||
{
|
||||
return BLARGG_NEW BZ2_Extractor;
|
||||
}
|
||||
|
||||
fex_type_t_ const fex_bz2_type [1] = {{
|
||||
".bz2",
|
||||
&new_bzip2,
|
||||
"bzip2 file",
|
||||
&init_bz2_file
|
||||
}};
|
||||
|
||||
BZ2_Extractor::BZ2_Extractor() :
|
||||
File_Extractor( fex_bz2_type )
|
||||
{ }
|
||||
|
||||
BZ2_Extractor::~BZ2_Extractor()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
blargg_err_t BZ2_Extractor::open_path_v()
|
||||
{
|
||||
// skip opening file
|
||||
return open_v();
|
||||
}
|
||||
|
||||
blargg_err_t BZ2_Extractor::stat_v()
|
||||
{
|
||||
RETURN_ERR( open_arc_file( true ) );
|
||||
if ( !gr.opened() || gr.tell() != 0 )
|
||||
RETURN_ERR( gr.open( &arc() ) );
|
||||
|
||||
set_info( gr.remain(), 0, gr.crc32() );
|
||||
return blargg_ok;
|
||||
}
|
||||
|
||||
blargg_err_t BZ2_Extractor::open_v()
|
||||
{
|
||||
// Remove .gz suffix
|
||||
size_t len = strlen( arc_path() );
|
||||
if ( fex_has_extension( arc_path(), ".bz2" ) )
|
||||
len -= 4;
|
||||
|
||||
RETURN_ERR( name.resize( len + 1 ) );
|
||||
memcpy( name.begin(), arc_path(), name.size() );
|
||||
name [name.size() - 1] = '\0';
|
||||
|
||||
set_name( name.begin() );
|
||||
return blargg_ok;
|
||||
}
|
||||
|
||||
void BZ2_Extractor::close_v()
|
||||
{
|
||||
name.clear();
|
||||
gr.close();
|
||||
}
|
||||
|
||||
blargg_err_t BZ2_Extractor::next_v()
|
||||
{
|
||||
return blargg_ok;
|
||||
}
|
||||
|
||||
blargg_err_t BZ2_Extractor::rewind_v()
|
||||
{
|
||||
set_name( name.begin() );
|
||||
return blargg_ok;
|
||||
}
|
||||
|
||||
blargg_err_t BZ2_Extractor::extract_v( void* p, int n )
|
||||
{
|
||||
return gr.read( p, n );
|
||||
}
|
||||
|
||||
#endif
|
35
src/core/fex/fex/BZ2_Extractor.h
Normal file
35
src/core/fex/fex/BZ2_Extractor.h
Normal file
@@ -0,0 +1,35 @@
|
||||
// Presents a gzipped file as an "archive" of just that file.
|
||||
// Also handles non-gzipped files.
|
||||
|
||||
// File_Extractor 1.0.0
|
||||
#ifndef BZ2_EXTRACTOR_H
|
||||
#define BZ2_EXTRACTOR_H
|
||||
|
||||
#include "File_Extractor.h"
|
||||
#include "BZ2_Reader.h"
|
||||
|
||||
class BZ2_Extractor : public File_Extractor
|
||||
{
|
||||
public:
|
||||
BZ2_Extractor();
|
||||
virtual ~BZ2_Extractor();
|
||||
|
||||
protected:
|
||||
virtual blargg_err_t open_path_v();
|
||||
virtual blargg_err_t open_v();
|
||||
virtual void close_v();
|
||||
|
||||
virtual blargg_err_t next_v();
|
||||
virtual blargg_err_t rewind_v();
|
||||
|
||||
virtual blargg_err_t stat_v();
|
||||
virtual blargg_err_t extract_v(void *, int);
|
||||
|
||||
private:
|
||||
BZ2_Reader gr;
|
||||
blargg_vector<char> name;
|
||||
|
||||
void set_info_();
|
||||
};
|
||||
|
||||
#endif
|
284
src/core/fex/fex/BZ2_Inflater.cpp
Normal file
284
src/core/fex/fex/BZ2_Inflater.cpp
Normal file
@@ -0,0 +1,284 @@
|
||||
// File_Extractor 1.0.0. http://www.slack.net/~ant/
|
||||
|
||||
#if FEX_ENABLE_BZ2
|
||||
|
||||
#include "BZ2_Inflater.h"
|
||||
|
||||
/* Copyright (C) 2025 Andy Vandijck. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
General Public License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version. This
|
||||
module is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
details. You should have received a copy of the GNU Lesser General Public
|
||||
License along with this module; if not, write to the Free Software Foundation,
|
||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||
|
||||
#include "blargg_source.h"
|
||||
|
||||
int const block_size = 100 * 1024;
|
||||
|
||||
static const char* get_bz2_err( int code )
|
||||
{
|
||||
assert( code != BZ_OK );
|
||||
switch ( code )
|
||||
{
|
||||
case BZ_MEM_ERROR: return blargg_err_memory;
|
||||
case BZ_DATA_ERROR: return blargg_err_file_corrupt;
|
||||
// TODO: handle more error codes
|
||||
}
|
||||
|
||||
const char* str = BLARGG_ERR( BLARGG_ERR_GENERIC, "problem unzipping data" );
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
void BZ2_Inflater::end()
|
||||
{
|
||||
if ( deflated_ )
|
||||
{
|
||||
deflated_ = false;
|
||||
if ( BZ2_bzDecompressEnd ( &zbuf ) )
|
||||
check( false );
|
||||
}
|
||||
buf.clear();
|
||||
|
||||
static bz_stream const empty = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
memcpy( &zbuf, &empty, sizeof zbuf );
|
||||
}
|
||||
|
||||
BZ2_Inflater::BZ2_Inflater()
|
||||
{
|
||||
deflated_ = false;
|
||||
end(); // initialize things
|
||||
}
|
||||
|
||||
BZ2_Inflater::~BZ2_Inflater()
|
||||
{
|
||||
end();
|
||||
}
|
||||
|
||||
blargg_err_t BZ2_Inflater::skip_buf( int count )
|
||||
{
|
||||
byte* out = buf.end() - count;
|
||||
zbuf.avail_in = count;
|
||||
zbuf.next_in = (char *)out;
|
||||
return blargg_ok;
|
||||
}
|
||||
|
||||
blargg_err_t BZ2_Inflater::fill_buf( int count )
|
||||
{
|
||||
byte* out = buf.end() - count;
|
||||
RETURN_ERR( callback( user_data, out, &count ) );
|
||||
zbuf.avail_in = count;
|
||||
zbuf.next_in = (char *)out;
|
||||
return blargg_ok;
|
||||
}
|
||||
|
||||
blargg_err_t BZ2_Inflater::resize_buffer( int count )
|
||||
{
|
||||
return buf.resize(count);
|
||||
}
|
||||
|
||||
blargg_err_t BZ2_Inflater::begin( callback_t new_callback, void* new_user_data,
|
||||
int new_buf_size, int initial_read )
|
||||
{
|
||||
callback = new_callback;
|
||||
user_data = new_user_data;
|
||||
|
||||
end();
|
||||
|
||||
// TODO: decide whether using different size on alloc failure is a good idea
|
||||
//RETURN_ERR( buf.resize( new_buf_size ? new_buf_size : 4 * block_size ) );
|
||||
if ( new_buf_size && buf.resize( new_buf_size ) )
|
||||
{
|
||||
ACK_FAILURE();
|
||||
new_buf_size = 0;
|
||||
}
|
||||
|
||||
if ( !new_buf_size )
|
||||
{
|
||||
RETURN_ERR( buf.resize( 9 * block_size ) );
|
||||
initial_read = 0;
|
||||
}
|
||||
|
||||
// Fill buffer with some data, less than normal buffer size since caller might
|
||||
// just be examining beginning of file.
|
||||
return fill_buf( initial_read ? initial_read : block_size );
|
||||
}
|
||||
|
||||
blargg_err_t BZ2_Inflater::set_mode( mode_t mode, int data_offset )
|
||||
{
|
||||
zbuf.next_in += data_offset;
|
||||
zbuf.avail_in -= data_offset;
|
||||
|
||||
if ( mode == mode_auto )
|
||||
{
|
||||
// examine buffer for gzip header
|
||||
mode = mode_copy;
|
||||
unsigned const min_gzip_size = 2 + 8 + 8;
|
||||
if ( zbuf.avail_in >= min_gzip_size &&
|
||||
zbuf.next_in [0] == 0x42 && zbuf.next_in [1] == 0x5A )
|
||||
mode = mode_unbz2;
|
||||
}
|
||||
|
||||
if ( mode != mode_copy )
|
||||
{
|
||||
int zerr = BZ2_bzDecompressInit( &zbuf, 0, 0 );
|
||||
if ( zerr )
|
||||
{
|
||||
zbuf.next_in = NULL;
|
||||
return get_bz2_err( zerr );
|
||||
}
|
||||
|
||||
deflated_ = true;
|
||||
}
|
||||
return blargg_ok;
|
||||
}
|
||||
|
||||
/*
|
||||
// Reads/inflates entire stream. All input must be in buffer, and count must be total
|
||||
// of all output.
|
||||
blargg_err_t read_all( void* out, int count );
|
||||
|
||||
|
||||
// zlib automatically applies this optimization (uses inflateFast)
|
||||
// TODO: remove
|
||||
blargg_err_t BZ2_Inflater::read_all( void* out, int count )
|
||||
{
|
||||
if ( deflated_ )
|
||||
{
|
||||
zbuf.next_out = (char*) out;
|
||||
zbuf.avail_out = count;
|
||||
|
||||
int err = BZ2_bzDecompress( &zbuf );
|
||||
|
||||
if ( zbuf.avail_out || err != Z_STREAM_END )
|
||||
return blargg_err_file_corrupt;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( zbuf.avail_in < count )
|
||||
return blargg_err_file_corrupt;
|
||||
|
||||
memcpy( out, zbuf.next_in, count );
|
||||
|
||||
zbuf.next_in += count;
|
||||
zbuf.avail_in -= count;
|
||||
}
|
||||
|
||||
return blargg_ok;
|
||||
}
|
||||
*/
|
||||
|
||||
blargg_err_t BZ2_Inflater::get_size( int* count_io )
|
||||
{
|
||||
char *buffer = (char *)malloc(*count_io);
|
||||
|
||||
if (buffer == NULL) {
|
||||
return blargg_err_memory;
|
||||
}
|
||||
|
||||
read(buffer, count_io);
|
||||
free(buffer);
|
||||
|
||||
return blargg_ok;
|
||||
}
|
||||
|
||||
blargg_err_t BZ2_Inflater::read( void* out, int* count_io )
|
||||
{
|
||||
int remain = *count_io;
|
||||
if ( remain && zbuf.next_in )
|
||||
{
|
||||
if ( deflated_ )
|
||||
{
|
||||
zbuf.next_out = (char*) out;
|
||||
zbuf.avail_out = remain;
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
unsigned int old_avail_in = (unsigned int)zbuf.avail_in;
|
||||
int err = BZ2_bzDecompress( &zbuf );
|
||||
|
||||
if ( err == BZ_STREAM_END )
|
||||
{
|
||||
remain = zbuf.avail_out;
|
||||
fprintf(stderr, "BZ2 stream end: %d remaining, %u total size\n", remain, zbuf.total_out_lo32);
|
||||
end();
|
||||
break; // no more data to inflate
|
||||
}
|
||||
|
||||
if ( err && (err != BZ_OUTBUFF_FULL || old_avail_in) )
|
||||
return get_bz2_err( err );
|
||||
|
||||
if ( !zbuf.avail_out )
|
||||
{
|
||||
remain = 0;
|
||||
break; // requested number of bytes inflated
|
||||
}
|
||||
|
||||
if ( zbuf.avail_in )
|
||||
{
|
||||
// inflate() should never leave input if there's still space for output
|
||||
check( false );
|
||||
return blargg_err_file_corrupt;
|
||||
}
|
||||
|
||||
RETURN_ERR( fill_buf( (int)buf.size() ) );
|
||||
if ( !zbuf.avail_in )
|
||||
return blargg_err_file_corrupt; // stream didn't end but there's no more data
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while ( 1 )
|
||||
{
|
||||
// copy buffered data
|
||||
if ( zbuf.avail_in )
|
||||
{
|
||||
long count = zbuf.avail_in;
|
||||
if ( count > remain )
|
||||
count = remain;
|
||||
memcpy( out, zbuf.next_in, count );
|
||||
zbuf.total_out_lo32 += count;
|
||||
out = (char*) out + count;
|
||||
remain -= count;
|
||||
zbuf.next_in += count;
|
||||
zbuf.avail_in -= count;
|
||||
}
|
||||
|
||||
if ( !zbuf.avail_in && zbuf.next_in < (char *)buf.end() )
|
||||
{
|
||||
end();
|
||||
break;
|
||||
}
|
||||
|
||||
// read large request directly
|
||||
if ( remain + zbuf.total_out_lo32 % block_size >= buf.size() )
|
||||
{
|
||||
int count = remain;
|
||||
RETURN_ERR( callback( user_data, out, &count ) );
|
||||
zbuf.total_out_lo32 += count;
|
||||
out = (char*) out + count;
|
||||
remain -= count;
|
||||
|
||||
if ( remain )
|
||||
{
|
||||
end();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !remain )
|
||||
break;
|
||||
|
||||
RETURN_ERR( fill_buf( (int)(buf.size() - zbuf.total_out_lo32 % block_size) ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
*count_io -= remain;
|
||||
return blargg_ok;
|
||||
}
|
||||
|
||||
#endif
|
92
src/core/fex/fex/BZ2_Inflater.h
Normal file
92
src/core/fex/fex/BZ2_Inflater.h
Normal file
@@ -0,0 +1,92 @@
|
||||
// Simplifies use of zlib for inflating data
|
||||
|
||||
// File_Extractor 1.0.0
|
||||
#ifndef BZ2_INFLATER_H
|
||||
#define BZ2_INFLATER_H
|
||||
|
||||
#include <bzlib.h>
|
||||
|
||||
#include "Data_Reader.h"
|
||||
#include "blargg_common.h"
|
||||
|
||||
class BZ2_Inflater
|
||||
{
|
||||
public:
|
||||
// Reads at most min(*count,bytes_until_eof()) bytes into *out and set *count
|
||||
// to that number, or returns error if that many can't be read.
|
||||
typedef blargg_err_t (*callback_t)(void *user_data, void *out, int *count);
|
||||
|
||||
// Begins by setting callback and filling buffer. Default buffer is 16K and
|
||||
// filled to 4K, or specify buf_size and initial_read for custom buffer size
|
||||
// and how much to read initially.
|
||||
blargg_err_t begin(callback_t, void *user_data, int buf_size = 0, int initial_read = 0);
|
||||
blargg_err_t get_size(int *count_io);
|
||||
|
||||
// Data read into buffer by begin()
|
||||
const unsigned char *data() const
|
||||
{
|
||||
return (const unsigned char *)zbuf.next_in;
|
||||
}
|
||||
|
||||
int filled() const
|
||||
{
|
||||
return zbuf.avail_in;
|
||||
}
|
||||
|
||||
int totalOut() const
|
||||
{
|
||||
return zbuf.total_out_lo32;
|
||||
}
|
||||
|
||||
// Begins inflation using specified mode. Using mode_auto selects between
|
||||
// mode_copy and mode_ungz by examining first two bytes of buffer. Use
|
||||
// buf_offset to specify where data begins in buffer, in case there is
|
||||
// header data that should be skipped.
|
||||
enum mode_t { mode_copy, mode_unbz2, mode_raw_deflate, mode_auto };
|
||||
blargg_err_t set_mode(mode_t, int buf_offset = 0);
|
||||
|
||||
// True if set_mode() has been called with mode_ungz or mode_raw_deflate
|
||||
bool deflated() const
|
||||
{
|
||||
return deflated_;
|
||||
}
|
||||
|
||||
// Reads/inflates at most *count_io bytes into *out and sets *count_io to actual
|
||||
// number of bytes read (less than requested if end of data was reached).
|
||||
// Buffers source data internally, even in copy mode, so input file can be
|
||||
// unbuffered without sacrificing performance.
|
||||
blargg_err_t read(void *out, int *count_io);
|
||||
|
||||
// Total number of bytes read since begin()
|
||||
int tell() const
|
||||
{
|
||||
return zbuf.total_out_lo32;
|
||||
}
|
||||
|
||||
blargg_err_t resize_buffer(int count);
|
||||
|
||||
// Ends inflation and frees memory
|
||||
void end();
|
||||
|
||||
private:
|
||||
// noncopyable
|
||||
BZ2_Inflater(const BZ2_Inflater &);
|
||||
BZ2_Inflater &operator=(const BZ2_Inflater &);
|
||||
|
||||
// Implementation
|
||||
public:
|
||||
BZ2_Inflater();
|
||||
~BZ2_Inflater();
|
||||
|
||||
private:
|
||||
bz_stream zbuf;
|
||||
blargg_vector<unsigned char> buf;
|
||||
bool deflated_;
|
||||
callback_t callback;
|
||||
void *user_data;
|
||||
|
||||
blargg_err_t fill_buf(int count);
|
||||
blargg_err_t skip_buf(int count);
|
||||
};
|
||||
|
||||
#endif
|
90
src/core/fex/fex/BZ2_Reader.cpp
Normal file
90
src/core/fex/fex/BZ2_Reader.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
// File_Extractor 1.0.0. http://www.slack.net/~ant/
|
||||
|
||||
#if FEX_ENABLE_BZ2
|
||||
|
||||
#include "BZ2_Reader.h"
|
||||
|
||||
#include "blargg_endian.h"
|
||||
|
||||
/* Copyright (C) 2025 Andy Vandijck. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
General Public License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version. This
|
||||
module is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
details. You should have received a copy of the GNU Lesser General Public
|
||||
License along with this module; if not, write to the Free Software Foundation,
|
||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||
|
||||
#include "blargg_source.h"
|
||||
|
||||
#include <bzlib.h>
|
||||
|
||||
BZ2_Reader::BZ2_Reader()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
BZ2_Reader::~BZ2_Reader()
|
||||
{ }
|
||||
|
||||
static blargg_err_t BZ2_reader_read( void* file, void* out, int* count )
|
||||
{
|
||||
return STATIC_CAST(File_Reader*,file)->read_avail( out, count );
|
||||
}
|
||||
|
||||
blargg_err_t BZ2_Reader::calc_size()
|
||||
{
|
||||
size_ = 0x8000000; // Max cart size
|
||||
crc32_ = 0;
|
||||
|
||||
set_remain(size_);
|
||||
inflater.get_size(&size_);
|
||||
|
||||
fprintf(stderr, "Calculated BZ2 size: %d\n", size_);
|
||||
|
||||
in->seek(0);
|
||||
|
||||
return blargg_ok;
|
||||
}
|
||||
|
||||
blargg_err_t BZ2_Reader::open( File_Reader* new_in )
|
||||
{
|
||||
close();
|
||||
|
||||
in = new_in;
|
||||
RETURN_ERR( in->seek( 0 ) );
|
||||
RETURN_ERR( inflater.begin( BZ2_reader_read, new_in ) );
|
||||
RETURN_ERR( inflater.set_mode( inflater.mode_auto ) );
|
||||
RETURN_ERR( calc_size() );
|
||||
inflater.end();
|
||||
RETURN_ERR( inflater.begin( BZ2_reader_read, new_in ) );
|
||||
RETURN_ERR( inflater.set_mode( inflater.mode_auto ) );
|
||||
set_remain( size_ );
|
||||
|
||||
return blargg_ok;
|
||||
}
|
||||
|
||||
void BZ2_Reader::close()
|
||||
{
|
||||
in = NULL;
|
||||
inflater.end();
|
||||
}
|
||||
|
||||
blargg_err_t BZ2_Reader::read_v( void* out, int count )
|
||||
{
|
||||
assert( in );
|
||||
int actual = count;
|
||||
RETURN_ERR( inflater.read( out, &actual ) );
|
||||
|
||||
if ( actual < size_ ) {
|
||||
size_ = actual;
|
||||
inflater.resize_buffer(actual);
|
||||
set_remain(0);
|
||||
}
|
||||
|
||||
return blargg_ok;
|
||||
}
|
||||
|
||||
#endif
|
59
src/core/fex/fex/BZ2_Reader.h
Normal file
59
src/core/fex/fex/BZ2_Reader.h
Normal file
@@ -0,0 +1,59 @@
|
||||
// Transparently decompresses gzip files, as well as uncompressed
|
||||
|
||||
// File_Extractor 1.0.0
|
||||
#ifndef BZ2_READER_H
|
||||
#define BZ2_READER_H
|
||||
|
||||
#include "Data_Reader.h"
|
||||
#include "BZ2_Inflater.h"
|
||||
|
||||
class BZ2_Reader : public Data_Reader
|
||||
{
|
||||
public:
|
||||
// Keeps pointer to reader until close(). If
|
||||
blargg_err_t open(File_Reader *);
|
||||
|
||||
// True if file is open
|
||||
bool opened() const
|
||||
{
|
||||
return in != NULL;
|
||||
}
|
||||
|
||||
// Frees memory
|
||||
void close();
|
||||
|
||||
// True if file is compressed
|
||||
bool deflated() const
|
||||
{
|
||||
return inflater.deflated();
|
||||
}
|
||||
|
||||
// CRC-32 of data, of 0 if unavailable
|
||||
unsigned int crc32() const
|
||||
{
|
||||
return crc32_;
|
||||
}
|
||||
|
||||
// Number of bytes read since opening
|
||||
int tell() const
|
||||
{
|
||||
return size_ - remain();
|
||||
}
|
||||
|
||||
public:
|
||||
BZ2_Reader();
|
||||
virtual ~BZ2_Reader();
|
||||
|
||||
protected:
|
||||
virtual blargg_err_t read_v(void *, int);
|
||||
|
||||
private:
|
||||
File_Reader *in;
|
||||
unsigned crc32_;
|
||||
int size_;
|
||||
BZ2_Inflater inflater;
|
||||
|
||||
blargg_err_t calc_size();
|
||||
};
|
||||
|
||||
#endif
|
@@ -6,7 +6,7 @@
|
||||
|
||||
#include "Data_Reader.h"
|
||||
#include "blargg_common.h"
|
||||
#include "fex.h"
|
||||
#include "../fex.h"
|
||||
|
||||
struct fex_t : private Data_Reader {
|
||||
public:
|
||||
@@ -26,6 +26,7 @@ struct fex_t : private Data_Reader {
|
||||
|
||||
// See fex.h
|
||||
blargg_err_t open(const char path[]);
|
||||
|
||||
fex_type_t type() const
|
||||
{
|
||||
return type_;
|
||||
@@ -213,7 +214,7 @@ struct fex_type_t_ {
|
||||
blargg_err_t (*init)(); // Called by fex_init(). Can be NULL.
|
||||
};
|
||||
|
||||
extern const fex_type_t_ fex_7z_type[1], fex_gz_type[1], fex_rar_type[1], fex_zip_type[1],
|
||||
extern const fex_type_t_ fex_7z_type[1], fex_gz_type[1], fex_bz2_type[1], fex_xz_type[1], fex_lz_type[1], fex_tar_type[1], fex_rar_type[1], fex_zip_type[1],
|
||||
fex_bin_type[1];
|
||||
|
||||
inline blargg_err_t File_Extractor::open_v()
|
||||
|
371
src/core/fex/fex/LZMA_Inflater.cpp
Normal file
371
src/core/fex/fex/LZMA_Inflater.cpp
Normal file
@@ -0,0 +1,371 @@
|
||||
// File_Extractor 1.0.0. http://www.slack.net/~ant/
|
||||
|
||||
#if FEX_ENABLE_LZMA
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "LZMA_Inflater.h"
|
||||
|
||||
/* Copyright (C) 2025 Andy Vandijck. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
General Public License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version. This
|
||||
module is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
details. You should have received a copy of the GNU Lesser General Public
|
||||
License along with this module; if not, write to the Free Software Foundation,
|
||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||
|
||||
#include "blargg_source.h"
|
||||
|
||||
int const block_size = 4096;
|
||||
|
||||
static const char* get_lzma_err( int code )
|
||||
{
|
||||
assert( code != LZMA_OK );
|
||||
switch ( code )
|
||||
{
|
||||
case LZMA_MEM_ERROR: return blargg_err_memory;
|
||||
case LZMA_DATA_ERROR: return blargg_err_file_corrupt;
|
||||
// TODO: handle more error codes
|
||||
}
|
||||
|
||||
const char* str = BLARGG_ERR( BLARGG_ERR_GENERIC, "problem uncompressing LZMA data" );
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
void LZMA_Inflater::end()
|
||||
{
|
||||
if ( deflated_ )
|
||||
{
|
||||
deflated_ = false;
|
||||
lzma_end(&zbuf);
|
||||
}
|
||||
buf.clear();
|
||||
|
||||
static lzma_stream const empty = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (lzma_reserved_enum)0, (lzma_reserved_enum)0 };
|
||||
memcpy( &zbuf, &empty, sizeof zbuf );
|
||||
}
|
||||
|
||||
LZMA_Inflater::LZMA_Inflater()
|
||||
{
|
||||
deflated_ = false;
|
||||
end(); // initialize things
|
||||
}
|
||||
|
||||
LZMA_Inflater::~LZMA_Inflater()
|
||||
{
|
||||
end();
|
||||
}
|
||||
|
||||
blargg_err_t LZMA_Inflater::fill_buf( int count )
|
||||
{
|
||||
byte* out = buf.end() - count;
|
||||
RETURN_ERR( callback( user_data, out, &count ) );
|
||||
zbuf.avail_in = count;
|
||||
zbuf.next_in = (uint8_t *)out;
|
||||
return blargg_ok;
|
||||
}
|
||||
|
||||
blargg_err_t LZMA_Inflater::begin( callback_t new_callback, void* new_user_data,
|
||||
int new_buf_size, int initial_read )
|
||||
{
|
||||
callback = new_callback;
|
||||
user_data = new_user_data;
|
||||
|
||||
end();
|
||||
|
||||
// TODO: decide whether using different size on alloc failure is a good idea
|
||||
//RETURN_ERR( buf.resize( new_buf_size ? new_buf_size : 4 * block_size ) );
|
||||
if ( new_buf_size && buf.resize( new_buf_size ) )
|
||||
{
|
||||
ACK_FAILURE();
|
||||
new_buf_size = 0;
|
||||
}
|
||||
|
||||
if ( !new_buf_size )
|
||||
{
|
||||
RETURN_ERR( buf.resize( 4 * block_size ) );
|
||||
initial_read = 0;
|
||||
}
|
||||
|
||||
// Fill buffer with some data, less than normal buffer size since caller might
|
||||
// just be examining beginning of file.
|
||||
return fill_buf( initial_read ? initial_read : block_size );
|
||||
}
|
||||
|
||||
bool LZMA_Inflater::is_format_xz(void)
|
||||
{
|
||||
// Specify the magic as hex to be compatible with EBCDIC systems.
|
||||
static const uint8_t magic[6] = { 0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00 };
|
||||
return zbuf.avail_in >= sizeof(magic)
|
||||
&& memcmp(zbuf.next_in, magic, sizeof(magic)) == 0;
|
||||
}
|
||||
|
||||
|
||||
/// Return true if the data in in_buf seems to be in the .lzma format.
|
||||
bool LZMA_Inflater::is_format_lzma(void)
|
||||
{
|
||||
// The .lzma header is 13 bytes.
|
||||
if (zbuf.avail_in < 13)
|
||||
return false;
|
||||
|
||||
// Decode the LZMA1 properties.
|
||||
lzma_filter filter;
|
||||
filter.id = LZMA_FILTER_LZMA1;
|
||||
|
||||
if (lzma_properties_decode(&filter, NULL, zbuf.next_in, 5) != LZMA_OK)
|
||||
return false;
|
||||
|
||||
// A hack to ditch tons of false positives: We allow only dictionary
|
||||
// sizes that are 2^n or 2^n + 2^(n-1) or UINT32_MAX. LZMA_Alone
|
||||
// created only files with 2^n, but accepts any dictionary size.
|
||||
// If someone complains, this will be reconsidered.
|
||||
lzma_options_lzma *opt = (lzma_options_lzma *)filter.options;
|
||||
const uint32_t dict_size = opt->dict_size;
|
||||
free(opt);
|
||||
|
||||
if (dict_size != UINT32_MAX) {
|
||||
uint32_t d = dict_size - 1;
|
||||
d |= d >> 2;
|
||||
d |= d >> 3;
|
||||
d |= d >> 4;
|
||||
d |= d >> 8;
|
||||
d |= d >> 16;
|
||||
++d;
|
||||
if (d != dict_size || dict_size == 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Another hack to ditch false positives: Assume that if the
|
||||
// uncompressed size is known, it must be less than 256 GiB.
|
||||
// Again, if someone complains, this will be reconsidered.
|
||||
uint64_t uncompressed_size = 0;
|
||||
for (size_t i = 0; i < 8; ++i)
|
||||
uncompressed_size |= (uint64_t)(zbuf.next_in[5 + i]) << (i * 8);
|
||||
|
||||
if (uncompressed_size != UINT64_MAX
|
||||
&& uncompressed_size > (UINT64_C(1) << 38))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// Return true if the data in in_buf seems to be in the .lz format.
|
||||
bool LZMA_Inflater::is_format_lzip(void)
|
||||
{
|
||||
static const uint8_t magic[4] = { 0x4C, 0x5A, 0x49, 0x50 };
|
||||
return zbuf.avail_in >= sizeof(magic)
|
||||
&& memcmp(zbuf.next_in, magic, sizeof(magic)) == 0;
|
||||
}
|
||||
|
||||
blargg_err_t LZMA_Inflater::set_mode( mode_t mode, int data_offset )
|
||||
{
|
||||
int err = LZMA_OK;
|
||||
|
||||
zbuf.next_in += data_offset;
|
||||
zbuf.avail_in -= data_offset;
|
||||
buf_ptr = zbuf.next_in;
|
||||
|
||||
if ( mode == mode_auto )
|
||||
{
|
||||
// examine buffer for gzip header
|
||||
mode = mode_copy;
|
||||
if ( is_format_lzip() ) {
|
||||
fprintf(stderr, "LZIP detected\n");
|
||||
mode = mode_unlz;
|
||||
}
|
||||
|
||||
if ( is_format_xz() ) {
|
||||
fprintf(stderr, "XZ detected\n");
|
||||
mode = mode_unxz;
|
||||
}
|
||||
|
||||
if ( is_format_lzma() ) {
|
||||
fprintf(stderr, "LZMA detected\n");
|
||||
mode = mode_raw_deflate;
|
||||
}
|
||||
}
|
||||
|
||||
if ( mode != mode_copy )
|
||||
{
|
||||
zbuf = LZMA_STREAM_INIT;
|
||||
|
||||
if (mode == mode_raw_deflate)
|
||||
err = lzma_alone_decoder( &zbuf, UINT64_MAX);
|
||||
else if (mode == mode_unlz)
|
||||
err = lzma_lzip_decoder( &zbuf, UINT64_MAX, LZMA_CONCATENATED);
|
||||
else
|
||||
err = lzma_stream_decoder( &zbuf, UINT64_MAX, LZMA_CONCATENATED);
|
||||
|
||||
if (err != LZMA_OK) {
|
||||
fprintf(stderr, "Couldn't initialize LZMA stream decoder\n");
|
||||
return blargg_err_file_corrupt;
|
||||
}
|
||||
|
||||
deflated_ = true;
|
||||
}
|
||||
|
||||
mode_ = mode;
|
||||
|
||||
return blargg_ok;
|
||||
}
|
||||
|
||||
/*
|
||||
// Reads/inflates entire stream. All input must be in buffer, and count must be total
|
||||
// of all output.
|
||||
blargg_err_t read_all( void* out, int count );
|
||||
|
||||
|
||||
// zlib automatically applies this optimization (uses inflateFast)
|
||||
// TODO: remove
|
||||
blargg_err_t LZMA_Inflater::read_all( void* out, int count )
|
||||
{
|
||||
int err = LZMA_OK;
|
||||
|
||||
if ( deflated_ )
|
||||
{
|
||||
zbuf.next_out = (char*) out;
|
||||
zbuf.avail_out = count;
|
||||
|
||||
if ((buf.size() - zbuf.total_out) <= block_size)
|
||||
action = LZMA_FINISH;
|
||||
else
|
||||
action = LZMA_RUN;
|
||||
|
||||
err = lzma_code(&zbuf, action);
|
||||
|
||||
if ( zbuf.avail_out || err != Z_STREAM_END )
|
||||
return blargg_err_file_corrupt;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( zbuf.avail_in < count )
|
||||
return blargg_err_file_corrupt;
|
||||
|
||||
memcpy( out, zbuf.next_in, count );
|
||||
|
||||
zbuf.next_in += count;
|
||||
zbuf.avail_in -= count;
|
||||
}
|
||||
|
||||
return blargg_ok;
|
||||
}
|
||||
*/
|
||||
|
||||
blargg_err_t LZMA_Inflater::read( void* out, int* count_io )
|
||||
{
|
||||
int remain = *count_io;
|
||||
|
||||
zbuf.next_in = buf_ptr;
|
||||
|
||||
fprintf(stderr, "LZMA - Read remaining: %d, next in: 0x%p\n", remain, zbuf.next_in);
|
||||
if ( remain && zbuf.next_in )
|
||||
{
|
||||
fprintf(stderr, "LZMA - deflated: %d\n", deflated_);
|
||||
|
||||
if ( deflated_ )
|
||||
{
|
||||
zbuf.next_out = (uint8_t*) out;
|
||||
zbuf.avail_out = remain;
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
int err = LZMA_OK;
|
||||
unsigned int old_avail_in = (unsigned int)zbuf.avail_in;
|
||||
|
||||
if ((buf.size() - zbuf.total_out) <= block_size)
|
||||
action = LZMA_FINISH;
|
||||
else
|
||||
action = LZMA_RUN;
|
||||
|
||||
err = lzma_code(&zbuf, action);
|
||||
|
||||
if ( err == LZMA_STREAM_END )
|
||||
{
|
||||
remain = zbuf.avail_out;
|
||||
end();
|
||||
break; // no more data to inflate
|
||||
}
|
||||
|
||||
if ( err && (err != LZMA_BUF_ERROR || old_avail_in) ) {
|
||||
fprintf(stderr, "LZMA error: %d, old available in: %d\n", err, old_avail_in);
|
||||
return get_lzma_err( err );
|
||||
}
|
||||
|
||||
if ( !zbuf.avail_out )
|
||||
{
|
||||
remain = 0;
|
||||
break; // requested number of bytes inflated
|
||||
}
|
||||
|
||||
if ( zbuf.avail_in )
|
||||
{
|
||||
fprintf(stderr, "Available in: %zu, file corrupt\n", zbuf.avail_in);
|
||||
// inflate() should never leave input if there's still space for output
|
||||
check( false );
|
||||
return blargg_err_file_corrupt;
|
||||
}
|
||||
|
||||
RETURN_ERR( fill_buf( (int)buf.size() ) );
|
||||
if ( !zbuf.avail_in ) {
|
||||
fprintf(stderr, "No more available input data\n");
|
||||
return blargg_err_file_corrupt; // stream didn't end but there's no more data
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while ( 1 )
|
||||
{
|
||||
// copy buffered data
|
||||
if ( zbuf.avail_in )
|
||||
{
|
||||
long count = zbuf.avail_in;
|
||||
if ( count > remain )
|
||||
count = remain;
|
||||
memcpy( out, zbuf.next_in, count );
|
||||
zbuf.total_out += count;
|
||||
out = (char*) out + count;
|
||||
remain -= count;
|
||||
zbuf.next_in += count;
|
||||
zbuf.avail_in -= count;
|
||||
}
|
||||
|
||||
if ( !zbuf.avail_in && zbuf.next_in < (uint8_t *)buf.end() )
|
||||
{
|
||||
end();
|
||||
break;
|
||||
}
|
||||
|
||||
// read large request directly
|
||||
if ( remain + zbuf.total_out % block_size >= buf.size() )
|
||||
{
|
||||
int count = remain;
|
||||
RETURN_ERR( callback( user_data, out, &count ) );
|
||||
zbuf.total_out += count;
|
||||
out = (char*) out + count;
|
||||
remain -= count;
|
||||
|
||||
if ( remain )
|
||||
{
|
||||
end();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !remain )
|
||||
break;
|
||||
|
||||
RETURN_ERR( fill_buf( (int)(buf.size() - zbuf.total_out % block_size) ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
*count_io -= remain;
|
||||
return blargg_ok;
|
||||
}
|
||||
|
||||
#endif
|
94
src/core/fex/fex/LZMA_Inflater.h
Normal file
94
src/core/fex/fex/LZMA_Inflater.h
Normal file
@@ -0,0 +1,94 @@
|
||||
// Simplifies use of zlib for inflating data
|
||||
|
||||
// File_Extractor 1.0.0
|
||||
#ifndef LZMA_INFLATER_H
|
||||
#define LZMA_INFLATER_H
|
||||
|
||||
#include <lzma.h>
|
||||
|
||||
#include "Data_Reader.h"
|
||||
#include "blargg_common.h"
|
||||
|
||||
class LZMA_Inflater
|
||||
{
|
||||
public:
|
||||
// Reads at most min(*count,bytes_until_eof()) bytes into *out and set *count
|
||||
// to that number, or returns error if that many can't be read.
|
||||
typedef blargg_err_t (*callback_t)(void *user_data, void *out, int *count);
|
||||
|
||||
// Begins by setting callback and filling buffer. Default buffer is 16K and
|
||||
// filled to 4K, or specify buf_size and initial_read for custom buffer size
|
||||
// and how much to read initially.
|
||||
blargg_err_t begin(callback_t, void *user_data, int buf_size = 0, int initial_read = 0);
|
||||
|
||||
// Data read into buffer by begin()
|
||||
const unsigned char *data() const
|
||||
{
|
||||
return (const unsigned char *)zbuf.next_in;
|
||||
}
|
||||
|
||||
int filled() const
|
||||
{
|
||||
return zbuf.avail_in;
|
||||
}
|
||||
|
||||
int totalOut() const
|
||||
{
|
||||
return zbuf.total_out;
|
||||
}
|
||||
|
||||
// Begins inflation using specified mode. Using mode_auto selects between
|
||||
// mode_copy and mode_ungz by examining first two bytes of buffer. Use
|
||||
// buf_offset to specify where data begins in buffer, in case there is
|
||||
// header data that should be skipped.
|
||||
enum mode_t { mode_copy, mode_unxz, mode_unlz, mode_raw_deflate, mode_auto };
|
||||
blargg_err_t set_mode(mode_t, int buf_offset = 0);
|
||||
|
||||
// True if set_mode() has been called with mode_ungz or mode_raw_deflate
|
||||
bool deflated() const
|
||||
{
|
||||
return deflated_;
|
||||
}
|
||||
|
||||
// Reads/inflates at most *count_io bytes into *out and sets *count_io to actual
|
||||
// number of bytes read (less than requested if end of data was reached).
|
||||
// Buffers source data internally, even in copy mode, so input file can be
|
||||
// unbuffered without sacrificing performance.
|
||||
blargg_err_t read(void *out, int *count_io);
|
||||
|
||||
// Total number of bytes read since begin()
|
||||
int tell() const
|
||||
{
|
||||
return zbuf.total_out;
|
||||
}
|
||||
|
||||
// Ends inflation and frees memory
|
||||
void end();
|
||||
|
||||
private:
|
||||
// noncopyable
|
||||
LZMA_Inflater(const LZMA_Inflater &);
|
||||
LZMA_Inflater &operator=(const LZMA_Inflater &);
|
||||
|
||||
// Implementation
|
||||
public:
|
||||
LZMA_Inflater();
|
||||
~LZMA_Inflater();
|
||||
bool is_format_xz(void);
|
||||
bool is_format_lzma(void);
|
||||
bool is_format_lzip(void);
|
||||
|
||||
private:
|
||||
lzma_stream zbuf;
|
||||
lzma_action action;
|
||||
blargg_vector<unsigned char> buf;
|
||||
bool deflated_;
|
||||
callback_t callback;
|
||||
void *user_data;
|
||||
int mode_;
|
||||
const uint8_t *buf_ptr;
|
||||
|
||||
blargg_err_t fill_buf(int count);
|
||||
};
|
||||
|
||||
#endif
|
103
src/core/fex/fex/LZ_Extractor.cpp
Normal file
103
src/core/fex/fex/LZ_Extractor.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
// File_Extractor 1.0.0. http://www.slack.net/~ant/
|
||||
|
||||
#if FEX_ENABLE_LZMA
|
||||
|
||||
#include "LZ_Extractor.h"
|
||||
#include <zlib.h>
|
||||
|
||||
/* Copyright (C) 2005-2009 Shay Green. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
General Public License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version. This
|
||||
module is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
details. You should have received a copy of the GNU Lesser General Public
|
||||
License along with this module; if not, write to the Free Software Foundation,
|
||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||
|
||||
#include "blargg_source.h"
|
||||
|
||||
// TODO: could close file once data has been read into memory
|
||||
|
||||
static blargg_err_t init_lz_file()
|
||||
{
|
||||
get_crc_table(); // initialize zlib's CRC-32 tables
|
||||
return blargg_ok;
|
||||
}
|
||||
|
||||
static File_Extractor* new_lz()
|
||||
{
|
||||
return BLARGG_NEW LZ_Extractor;
|
||||
}
|
||||
|
||||
fex_type_t_ const fex_lz_type [1] = {{
|
||||
".lz",
|
||||
&new_lz,
|
||||
"lzip file",
|
||||
&init_lz_file
|
||||
}};
|
||||
|
||||
LZ_Extractor::LZ_Extractor() :
|
||||
File_Extractor( fex_lz_type )
|
||||
{ }
|
||||
|
||||
LZ_Extractor::~LZ_Extractor()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
blargg_err_t LZ_Extractor::open_path_v()
|
||||
{
|
||||
// skip opening file
|
||||
return open_v();
|
||||
}
|
||||
|
||||
blargg_err_t LZ_Extractor::stat_v()
|
||||
{
|
||||
RETURN_ERR( open_arc_file( true ) );
|
||||
if ( !gr.opened() || gr.tell() != 0 )
|
||||
RETURN_ERR( gr.open( &arc() ) );
|
||||
|
||||
set_info( gr.remain(), 0, gr.crc32() );
|
||||
return blargg_ok;
|
||||
}
|
||||
|
||||
blargg_err_t LZ_Extractor::open_v()
|
||||
{
|
||||
// Remove .gz suffix
|
||||
size_t len = strlen( arc_path() );
|
||||
if ( fex_has_extension( arc_path(), ".lz" ) )
|
||||
len -= 3;
|
||||
|
||||
RETURN_ERR( name.resize( len + 1 ) );
|
||||
memcpy( name.begin(), arc_path(), name.size() );
|
||||
name [name.size() - 1] = '\0';
|
||||
|
||||
set_name( name.begin() );
|
||||
return blargg_ok;
|
||||
}
|
||||
|
||||
void LZ_Extractor::close_v()
|
||||
{
|
||||
name.clear();
|
||||
gr.close();
|
||||
}
|
||||
|
||||
blargg_err_t LZ_Extractor::next_v()
|
||||
{
|
||||
return blargg_ok;
|
||||
}
|
||||
|
||||
blargg_err_t LZ_Extractor::rewind_v()
|
||||
{
|
||||
set_name( name.begin() );
|
||||
return blargg_ok;
|
||||
}
|
||||
|
||||
blargg_err_t LZ_Extractor::extract_v( void* p, int n )
|
||||
{
|
||||
return gr.read( p, n );
|
||||
}
|
||||
|
||||
#endif
|
35
src/core/fex/fex/LZ_Extractor.h
Normal file
35
src/core/fex/fex/LZ_Extractor.h
Normal file
@@ -0,0 +1,35 @@
|
||||
// Presents a gzipped file as an "archive" of just that file.
|
||||
// Also handles non-gzipped files.
|
||||
|
||||
// File_Extractor 1.0.0
|
||||
#ifndef LZ_EXTRACTOR_H
|
||||
#define LZ_EXTRACTOR_H
|
||||
|
||||
#include "File_Extractor.h"
|
||||
#include "LZ_Reader.h"
|
||||
|
||||
class LZ_Extractor : public File_Extractor
|
||||
{
|
||||
public:
|
||||
LZ_Extractor();
|
||||
virtual ~LZ_Extractor();
|
||||
|
||||
protected:
|
||||
virtual blargg_err_t open_path_v();
|
||||
virtual blargg_err_t open_v();
|
||||
virtual void close_v();
|
||||
|
||||
virtual blargg_err_t next_v();
|
||||
virtual blargg_err_t rewind_v();
|
||||
|
||||
virtual blargg_err_t stat_v();
|
||||
virtual blargg_err_t extract_v(void *, int);
|
||||
|
||||
private:
|
||||
LZ_Reader gr;
|
||||
blargg_vector<char> name;
|
||||
|
||||
void set_info_();
|
||||
};
|
||||
|
||||
#endif
|
121
src/core/fex/fex/LZ_Reader.cpp
Normal file
121
src/core/fex/fex/LZ_Reader.cpp
Normal file
@@ -0,0 +1,121 @@
|
||||
// File_Extractor 1.0.0. http://www.slack.net/~ant/
|
||||
|
||||
#if FEX_ENABLE_LZMA
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "LZ_Reader.h"
|
||||
|
||||
#include "blargg_endian.h"
|
||||
|
||||
/* Copyright (C) 2025 Andy Vandijck. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
General Public License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version. This
|
||||
module is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
details. You should have received a copy of the GNU Lesser General Public
|
||||
License along with this module; if not, write to the Free Software Foundation,
|
||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||
|
||||
#include "blargg_source.h"
|
||||
|
||||
LZ_Reader::LZ_Reader()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
LZ_Reader::~LZ_Reader()
|
||||
{ }
|
||||
|
||||
static blargg_err_t LZ_reader_read( void* file, void* out, int* count )
|
||||
{
|
||||
return STATIC_CAST(File_Reader*,file)->read_avail( out, count );
|
||||
}
|
||||
|
||||
enum { header_size = 6, trailer_size = 20 };
|
||||
|
||||
size_t LZ_Reader::get_uncompressed_size()
|
||||
{
|
||||
uint8_t *header_ptr = (uint8_t *)malloc(header_size);
|
||||
uint8_t *trailer_ptr = (uint8_t *)malloc(trailer_size);
|
||||
|
||||
if ((header_ptr == NULL) || (trailer_ptr == NULL)) {
|
||||
fprintf(stderr, "Error: Couldn't allocate data\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
in->seek(0);
|
||||
in->read((void *)header_ptr, header_size);
|
||||
|
||||
unsigned dict_size = 1 << (header_ptr[5] & 0x1F);
|
||||
dict_size -= (dict_size / 16) * ((header_ptr[5] >> 5 ) & 7);
|
||||
|
||||
if(dict_size < (1 << 12) || dict_size > (1 << 29))
|
||||
{
|
||||
fprintf(stderr, "Invalid dictionary size in member header.\n");
|
||||
free((void *)header_ptr);
|
||||
free((void *)trailer_ptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
in->seek(in->size() - trailer_size);
|
||||
in->read((void *)trailer_ptr, trailer_size);
|
||||
|
||||
unsigned long long data_size = 0;
|
||||
for (int i = 11; i >= 4; --i)
|
||||
data_size = ( data_size << 8 ) + trailer_ptr[i];
|
||||
|
||||
in->seek(0);
|
||||
|
||||
free((void *)header_ptr);
|
||||
free((void *)trailer_ptr);
|
||||
|
||||
return (size_t)data_size;
|
||||
}
|
||||
|
||||
blargg_err_t LZ_Reader::calc_size()
|
||||
{
|
||||
size_ = (int)get_uncompressed_size();
|
||||
fprintf(stderr, "LZ uncompressed size: %d\n", size_);
|
||||
|
||||
crc32_ = 0;
|
||||
return blargg_ok;
|
||||
}
|
||||
|
||||
blargg_err_t LZ_Reader::open( File_Reader* new_in )
|
||||
{
|
||||
close();
|
||||
|
||||
in = new_in;
|
||||
RETURN_ERR( in->seek( 0 ) );
|
||||
RETURN_ERR( inflater.begin( LZ_reader_read, new_in ) );
|
||||
RETURN_ERR( inflater.set_mode( inflater.mode_auto ) );
|
||||
RETURN_ERR( calc_size() );
|
||||
set_remain( size_ );
|
||||
|
||||
return blargg_ok;
|
||||
}
|
||||
|
||||
void LZ_Reader::close()
|
||||
{
|
||||
in = NULL;
|
||||
inflater.end();
|
||||
}
|
||||
|
||||
blargg_err_t LZ_Reader::read_v( void* out, int count )
|
||||
{
|
||||
assert( in );
|
||||
int actual = count;
|
||||
RETURN_ERR( inflater.read( out, &actual ) );
|
||||
|
||||
fprintf(stderr, "LZ: Actual read: %d, count: %d\n", actual, count);
|
||||
|
||||
if ( actual != count )
|
||||
return blargg_err_file_corrupt;
|
||||
|
||||
return blargg_ok;
|
||||
}
|
||||
|
||||
#endif
|
60
src/core/fex/fex/LZ_Reader.h
Normal file
60
src/core/fex/fex/LZ_Reader.h
Normal file
@@ -0,0 +1,60 @@
|
||||
// Transparently decompresses gzip files, as well as uncompressed
|
||||
|
||||
// File_Extractor 1.0.0
|
||||
#ifndef LZ_READER_H
|
||||
#define LZ_READER_H
|
||||
|
||||
#include "Data_Reader.h"
|
||||
#include "LZMA_Inflater.h"
|
||||
|
||||
class LZ_Reader : public Data_Reader
|
||||
{
|
||||
public:
|
||||
// Keeps pointer to reader until close(). If
|
||||
blargg_err_t open(File_Reader *);
|
||||
|
||||
// True if file is open
|
||||
bool opened() const
|
||||
{
|
||||
return in != NULL;
|
||||
}
|
||||
|
||||
// Frees memory
|
||||
void close();
|
||||
|
||||
// True if file is compressed
|
||||
bool deflated() const
|
||||
{
|
||||
return inflater.deflated();
|
||||
}
|
||||
|
||||
// CRC-32 of data, of 0 if unavailable
|
||||
unsigned int crc32() const
|
||||
{
|
||||
return crc32_;
|
||||
}
|
||||
|
||||
// Number of bytes read since opening
|
||||
int tell() const
|
||||
{
|
||||
return size_ - remain();
|
||||
}
|
||||
|
||||
public:
|
||||
LZ_Reader();
|
||||
virtual ~LZ_Reader();
|
||||
size_t get_uncompressed_size();
|
||||
|
||||
protected:
|
||||
virtual blargg_err_t read_v(void *, int);
|
||||
|
||||
private:
|
||||
File_Reader *in;
|
||||
unsigned crc32_;
|
||||
int size_;
|
||||
LZMA_Inflater inflater;
|
||||
|
||||
blargg_err_t calc_size();
|
||||
};
|
||||
|
||||
#endif
|
102
src/core/fex/fex/Tar_Extractor.cpp
Normal file
102
src/core/fex/fex/Tar_Extractor.cpp
Normal file
@@ -0,0 +1,102 @@
|
||||
// File_Extractor 1.0.0. http://www.slack.net/~ant/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "Tar_Extractor.h"
|
||||
|
||||
/* Copyright (C) 2025 Andy Vandijck. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
General Public License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version. This
|
||||
module is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
details. You should have received a copy of the GNU Lesser General Public
|
||||
License along with this module; if not, write to the Free Software Foundation,
|
||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||
|
||||
#include "blargg_source.h"
|
||||
|
||||
// TODO: could close file once data has been read into memory
|
||||
|
||||
static blargg_err_t init_tar_file()
|
||||
{
|
||||
get_crc_table(); // initialize zlib's CRC-32 tables
|
||||
return blargg_ok;
|
||||
}
|
||||
|
||||
static File_Extractor* new_tar()
|
||||
{
|
||||
return BLARGG_NEW Tar_Extractor;
|
||||
}
|
||||
|
||||
fex_type_t_ const fex_tar_type [1] = {{
|
||||
".tar",
|
||||
&new_tar,
|
||||
"tar file",
|
||||
&init_tar_file
|
||||
}};
|
||||
|
||||
Tar_Extractor::Tar_Extractor() :
|
||||
File_Extractor( fex_tar_type )
|
||||
{ }
|
||||
|
||||
Tar_Extractor::~Tar_Extractor()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
blargg_err_t Tar_Extractor::open_path_v()
|
||||
{
|
||||
RETURN_ERR( open_arc_file(true) );
|
||||
RETURN_ERR( arc().seek( 0 ) );
|
||||
return open_v();
|
||||
}
|
||||
|
||||
blargg_err_t Tar_Extractor::stat_v()
|
||||
{
|
||||
set_info( arc().remain(), 0, 0 );
|
||||
return blargg_ok;
|
||||
}
|
||||
|
||||
blargg_err_t Tar_Extractor::open_v()
|
||||
{
|
||||
arc().read(&header, BLOCKSIZE);
|
||||
set_name( header.name );
|
||||
#if __STDC_WANT_SECURE_LIB__
|
||||
sscanf_s(header.size, "%o", &tarsize);
|
||||
#else
|
||||
sscanf(header.size, "%o", &tarsize);
|
||||
#endif
|
||||
return blargg_ok;
|
||||
}
|
||||
|
||||
void Tar_Extractor::close_v()
|
||||
{
|
||||
name.clear();
|
||||
}
|
||||
|
||||
blargg_err_t Tar_Extractor::next_v()
|
||||
{
|
||||
arc().read(&header, BLOCKSIZE);
|
||||
set_name( header.name );
|
||||
#if __STDC_WANT_SECURE_LIB__
|
||||
sscanf_s(header.size, "%o", &tarsize);
|
||||
#else
|
||||
sscanf(header.size, "%o", &tarsize);
|
||||
#endif
|
||||
return blargg_ok;
|
||||
}
|
||||
|
||||
blargg_err_t Tar_Extractor::rewind_v()
|
||||
{
|
||||
arc().seek(0);
|
||||
return blargg_ok;
|
||||
}
|
||||
|
||||
blargg_err_t Tar_Extractor::extract_v( void* p, int n )
|
||||
{
|
||||
return arc().read( p, n );
|
||||
}
|
76
src/core/fex/fex/Tar_Extractor.h
Normal file
76
src/core/fex/fex/Tar_Extractor.h
Normal file
@@ -0,0 +1,76 @@
|
||||
// Presents a gzipped file as an "archive" of just that file.
|
||||
// Also handles non-gzipped files.
|
||||
|
||||
// File_Extractor 1.0.0
|
||||
#ifndef TAR_EXTRACTOR_H
|
||||
#define TAR_EXTRACTOR_H
|
||||
|
||||
#include "File_Extractor.h"
|
||||
|
||||
typedef struct posix_header
|
||||
{ /* byte offset */
|
||||
char name[100]; /* 0 */
|
||||
char mode[8]; /* 100 */
|
||||
char uid[8]; /* 108 */
|
||||
char gid[8]; /* 116 */
|
||||
char size[12]; /* 124 */
|
||||
char mtime[12]; /* 136 */
|
||||
char chksum[8]; /* 148 */
|
||||
char typeflag; /* 156 */
|
||||
char linkname[100]; /* 157 */
|
||||
char magic[6]; /* 257 */
|
||||
char version[2]; /* 263 */
|
||||
char uname[32]; /* 265 */
|
||||
char gname[32]; /* 297 */
|
||||
char devmajor[8]; /* 329 */
|
||||
char devminor[8]; /* 337 */
|
||||
char prefix[155]; /* 345 */
|
||||
char padding[12]; /* 500 */
|
||||
/* 512 */
|
||||
} posix_header;
|
||||
|
||||
#define BLOCKSIZE 512
|
||||
|
||||
#define TMAGIC "ustar" /* ustar and a null */
|
||||
#define TMAGLEN 6
|
||||
#define TVERSION "00" /* 00 and no null */
|
||||
#define TVERSLEN 2
|
||||
|
||||
/* Values used in typeflag field. */
|
||||
#define REGTYPE '0' /* regular file */
|
||||
#define AREGTYPE '\0' /* regular file */
|
||||
#define LNKTYPE '1' /* link */
|
||||
#define SYMTYPE '2' /* reserved */
|
||||
#define CHRTYPE '3' /* character special */
|
||||
#define BLKTYPE '4' /* block special */
|
||||
#define DIRTYPE '5' /* directory */
|
||||
#define FIFOTYPE '6' /* FIFO special */
|
||||
#define CONTTYPE '7' /* reserved */
|
||||
|
||||
class Tar_Extractor : public File_Extractor
|
||||
{
|
||||
public:
|
||||
Tar_Extractor();
|
||||
virtual ~Tar_Extractor();
|
||||
|
||||
protected:
|
||||
virtual blargg_err_t open_path_v();
|
||||
virtual blargg_err_t open_v();
|
||||
virtual void close_v();
|
||||
|
||||
virtual blargg_err_t next_v();
|
||||
virtual blargg_err_t rewind_v();
|
||||
|
||||
virtual blargg_err_t stat_v();
|
||||
virtual blargg_err_t extract_v(void *, int);
|
||||
|
||||
private:
|
||||
File_Reader *gr;
|
||||
posix_header header;
|
||||
unsigned int tarsize;
|
||||
blargg_vector<char> name;
|
||||
|
||||
void set_info_();
|
||||
};
|
||||
|
||||
#endif
|
103
src/core/fex/fex/XZ_Extractor.cpp
Normal file
103
src/core/fex/fex/XZ_Extractor.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
// File_Extractor 1.0.0. http://www.slack.net/~ant/
|
||||
|
||||
#if FEX_ENABLE_LZMA
|
||||
|
||||
#include "XZ_Extractor.h"
|
||||
#include <zlib.h>
|
||||
|
||||
/* Copyright (C) 2005-2009 Shay Green. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
General Public License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version. This
|
||||
module is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
details. You should have received a copy of the GNU Lesser General Public
|
||||
License along with this module; if not, write to the Free Software Foundation,
|
||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||
|
||||
#include "blargg_source.h"
|
||||
|
||||
// TODO: could close file once data has been read into memory
|
||||
|
||||
static blargg_err_t init_xz_file()
|
||||
{
|
||||
get_crc_table(); // initialize zlib's CRC-32 tables
|
||||
return blargg_ok;
|
||||
}
|
||||
|
||||
static File_Extractor* new_xz()
|
||||
{
|
||||
return BLARGG_NEW XZ_Extractor;
|
||||
}
|
||||
|
||||
fex_type_t_ const fex_xz_type [1] = {{
|
||||
".xz",
|
||||
&new_xz,
|
||||
"xz file",
|
||||
&init_xz_file
|
||||
}};
|
||||
|
||||
XZ_Extractor::XZ_Extractor() :
|
||||
File_Extractor( fex_xz_type )
|
||||
{ }
|
||||
|
||||
XZ_Extractor::~XZ_Extractor()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
blargg_err_t XZ_Extractor::open_path_v()
|
||||
{
|
||||
// skip opening file
|
||||
return open_v();
|
||||
}
|
||||
|
||||
blargg_err_t XZ_Extractor::stat_v()
|
||||
{
|
||||
RETURN_ERR( open_arc_file( true ) );
|
||||
if ( !gr.opened() || gr.tell() != 0 )
|
||||
RETURN_ERR( gr.open( &arc() ) );
|
||||
|
||||
set_info( gr.remain(), 0, gr.crc32() );
|
||||
return blargg_ok;
|
||||
}
|
||||
|
||||
blargg_err_t XZ_Extractor::open_v()
|
||||
{
|
||||
// Remove .gz suffix
|
||||
size_t len = strlen( arc_path() );
|
||||
if ( fex_has_extension( arc_path(), ".xz" ) )
|
||||
len -= 3;
|
||||
|
||||
RETURN_ERR( name.resize( len + 1 ) );
|
||||
memcpy( name.begin(), arc_path(), name.size() );
|
||||
name [name.size() - 1] = '\0';
|
||||
|
||||
set_name( name.begin() );
|
||||
return blargg_ok;
|
||||
}
|
||||
|
||||
void XZ_Extractor::close_v()
|
||||
{
|
||||
name.clear();
|
||||
gr.close();
|
||||
}
|
||||
|
||||
blargg_err_t XZ_Extractor::next_v()
|
||||
{
|
||||
return blargg_ok;
|
||||
}
|
||||
|
||||
blargg_err_t XZ_Extractor::rewind_v()
|
||||
{
|
||||
set_name( name.begin() );
|
||||
return blargg_ok;
|
||||
}
|
||||
|
||||
blargg_err_t XZ_Extractor::extract_v( void* p, int n )
|
||||
{
|
||||
return gr.read( p, n );
|
||||
}
|
||||
|
||||
#endif
|
35
src/core/fex/fex/XZ_Extractor.h
Normal file
35
src/core/fex/fex/XZ_Extractor.h
Normal file
@@ -0,0 +1,35 @@
|
||||
// Presents a gzipped file as an "archive" of just that file.
|
||||
// Also handles non-gzipped files.
|
||||
|
||||
// File_Extractor 1.0.0
|
||||
#ifndef XZ_EXTRACTOR_H
|
||||
#define XZ_EXTRACTOR_H
|
||||
|
||||
#include "File_Extractor.h"
|
||||
#include "XZ_Reader.h"
|
||||
|
||||
class XZ_Extractor : public File_Extractor
|
||||
{
|
||||
public:
|
||||
XZ_Extractor();
|
||||
virtual ~XZ_Extractor();
|
||||
|
||||
protected:
|
||||
virtual blargg_err_t open_path_v();
|
||||
virtual blargg_err_t open_v();
|
||||
virtual void close_v();
|
||||
|
||||
virtual blargg_err_t next_v();
|
||||
virtual blargg_err_t rewind_v();
|
||||
|
||||
virtual blargg_err_t stat_v();
|
||||
virtual blargg_err_t extract_v(void *, int);
|
||||
|
||||
private:
|
||||
XZ_Reader gr;
|
||||
blargg_vector<char> name;
|
||||
|
||||
void set_info_();
|
||||
};
|
||||
|
||||
#endif
|
129
src/core/fex/fex/XZ_Reader.cpp
Normal file
129
src/core/fex/fex/XZ_Reader.cpp
Normal file
@@ -0,0 +1,129 @@
|
||||
// File_Extractor 1.0.0. http://www.slack.net/~ant/
|
||||
|
||||
#if FEX_ENABLE_LZMA
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "XZ_Reader.h"
|
||||
|
||||
#include "blargg_endian.h"
|
||||
|
||||
/* Copyright (C) 2025 Andy Vandijck. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
General Public License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version. This
|
||||
module is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
details. You should have received a copy of the GNU Lesser General Public
|
||||
License along with this module; if not, write to the Free Software Foundation,
|
||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||
|
||||
#include "blargg_source.h"
|
||||
|
||||
XZ_Reader::XZ_Reader()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
XZ_Reader::~XZ_Reader()
|
||||
{ }
|
||||
|
||||
static blargg_err_t XZ_reader_read( void* file, void* out, int* count )
|
||||
{
|
||||
return STATIC_CAST(File_Reader*,file)->read_avail( out, count );
|
||||
}
|
||||
|
||||
size_t XZ_Reader::get_uncompressed_size()
|
||||
{
|
||||
lzma_stream_flags stream_flags;
|
||||
|
||||
const uint8_t *footer_ptr = NULL;
|
||||
const uint8_t *index_ptr = NULL;
|
||||
const uint8_t *data = (const uint8_t *)malloc(in->size());
|
||||
|
||||
if (data == NULL) {
|
||||
fprintf(stderr, "Error: Couldn't allocate data\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
in->seek(0);
|
||||
in->read((void *)data, in->size());
|
||||
|
||||
// 12 is the size of the footer per the file-spec...
|
||||
footer_ptr = data + (in->size() - 12);
|
||||
|
||||
// Decode the footer, so we have the backward_size pointing to the index
|
||||
(void)lzma_stream_footer_decode(&stream_flags, (const uint8_t *)footer_ptr);
|
||||
// This is the index pointer, where the size is ultimately stored...
|
||||
index_ptr = data + ((in->size() - 12) - stream_flags.backward_size);
|
||||
|
||||
// Allocate an index
|
||||
lzma_index *index = lzma_index_init(NULL);
|
||||
uint64_t memlimit;
|
||||
size_t in_pos = 0;
|
||||
// decode the index we calculated
|
||||
lzma_index_buffer_decode(&index, &memlimit, NULL, (const uint8_t *)index_ptr, &in_pos, footer_ptr - index_ptr);
|
||||
// Just make sure the whole index was decoded, otherwise, we might be
|
||||
// dealing with something utterly corrupt
|
||||
if (in_pos != stream_flags.backward_size) {
|
||||
lzma_index_end(index, NULL);
|
||||
free((void *)data);
|
||||
fprintf(stderr, "Error: input position %u is not equal to backward size %llu\n", in_pos, stream_flags.backward_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Finally get the size
|
||||
lzma_vli uSize = lzma_index_uncompressed_size(index);
|
||||
lzma_index_end(index, NULL);
|
||||
|
||||
free((void *)data);
|
||||
in->seek(0);
|
||||
|
||||
return (size_t) uSize;
|
||||
}
|
||||
|
||||
blargg_err_t XZ_Reader::calc_size()
|
||||
{
|
||||
size_ = (int)get_uncompressed_size();
|
||||
fprintf(stderr, "XZ uncompressed size: %d\n", size_);
|
||||
|
||||
crc32_ = 0;
|
||||
return blargg_ok;
|
||||
}
|
||||
|
||||
blargg_err_t XZ_Reader::open( File_Reader* new_in )
|
||||
{
|
||||
close();
|
||||
|
||||
in = new_in;
|
||||
RETURN_ERR( in->seek( 0 ) );
|
||||
RETURN_ERR( inflater.begin( XZ_reader_read, new_in ) );
|
||||
RETURN_ERR( inflater.set_mode( inflater.mode_auto ) );
|
||||
RETURN_ERR( calc_size() );
|
||||
set_remain( size_ );
|
||||
|
||||
return blargg_ok;
|
||||
}
|
||||
|
||||
void XZ_Reader::close()
|
||||
{
|
||||
in = NULL;
|
||||
inflater.end();
|
||||
}
|
||||
|
||||
blargg_err_t XZ_Reader::read_v( void* out, int count )
|
||||
{
|
||||
assert( in );
|
||||
int actual = count;
|
||||
RETURN_ERR( inflater.read( out, &actual ) );
|
||||
|
||||
fprintf(stderr, "XZ: Actual read: %d, count: %d\n", actual, count);
|
||||
|
||||
if ( actual != count )
|
||||
return blargg_err_file_corrupt;
|
||||
|
||||
return blargg_ok;
|
||||
}
|
||||
|
||||
#endif
|
60
src/core/fex/fex/XZ_Reader.h
Normal file
60
src/core/fex/fex/XZ_Reader.h
Normal file
@@ -0,0 +1,60 @@
|
||||
// Transparently decompresses gzip files, as well as uncompressed
|
||||
|
||||
// File_Extractor 1.0.0
|
||||
#ifndef XZ_READER_H
|
||||
#define XZ_READER_H
|
||||
|
||||
#include "Data_Reader.h"
|
||||
#include "LZMA_Inflater.h"
|
||||
|
||||
class XZ_Reader : public Data_Reader
|
||||
{
|
||||
public:
|
||||
// Keeps pointer to reader until close(). If
|
||||
blargg_err_t open(File_Reader *);
|
||||
|
||||
// True if file is open
|
||||
bool opened() const
|
||||
{
|
||||
return in != NULL;
|
||||
}
|
||||
|
||||
// Frees memory
|
||||
void close();
|
||||
|
||||
// True if file is compressed
|
||||
bool deflated() const
|
||||
{
|
||||
return inflater.deflated();
|
||||
}
|
||||
|
||||
// CRC-32 of data, of 0 if unavailable
|
||||
unsigned int crc32() const
|
||||
{
|
||||
return crc32_;
|
||||
}
|
||||
|
||||
// Number of bytes read since opening
|
||||
int tell() const
|
||||
{
|
||||
return size_ - remain();
|
||||
}
|
||||
|
||||
public:
|
||||
XZ_Reader();
|
||||
virtual ~XZ_Reader();
|
||||
size_t get_uncompressed_size();
|
||||
|
||||
protected:
|
||||
virtual blargg_err_t read_v(void *, int);
|
||||
|
||||
private:
|
||||
File_Reader *in;
|
||||
unsigned crc32_;
|
||||
int size_;
|
||||
LZMA_Inflater inflater;
|
||||
|
||||
blargg_err_t calc_size();
|
||||
};
|
||||
|
||||
#endif
|
@@ -17,8 +17,23 @@
|
||||
// Enable support for as building DLL on Windows.
|
||||
//#define BLARGG_BUILD_DLL 1
|
||||
|
||||
// Support only the listed archive types. Remove any you don't need.
|
||||
#define FEX_TYPE_LIST fex_7z_type, fex_gz_type, fex_zip_type,
|
||||
#ifndef FEX_ENABLE_RAR
|
||||
#define FEX_ENABLE_RAR 1
|
||||
#endif
|
||||
|
||||
#if FEX_ENABLE_BZ2
|
||||
#define FEX_TYPE_BZ2 fex_bz2_type,
|
||||
#else
|
||||
#define FEX_TYPE_BZ2
|
||||
#endif
|
||||
|
||||
#if FEX_ENABLE_LZMA
|
||||
#define FEX_TYPE_LZMA fex_xz_type, fex_lz_type,
|
||||
#else
|
||||
#define FEX_TYPE_LZMA
|
||||
#endif
|
||||
|
||||
#define FEX_TYPE_LIST fex_7z_type, fex_gz_type, fex_zip_type, fex_tar_type, fex_rar_type, FEX_TYPE_BZ2 FEX_TYPE_LZMA
|
||||
|
||||
// Use standard config.h if present
|
||||
#ifdef HAVE_CONFIG_H
|
||||
|
@@ -33,6 +33,14 @@ BLARGG_EXPORT const fex_type_t* fex_type_list( void )
|
||||
// Modify blargg_config.h to change type list, NOT this file
|
||||
fex_7z_type,
|
||||
fex_gz_type,
|
||||
fex_tar_type,
|
||||
#if FEX_ENABLE_LZMA
|
||||
fex_xz_type,
|
||||
fex_lz_type,
|
||||
#endif
|
||||
#if FEX_ENABLE_BZ2
|
||||
fex_bz2_type,
|
||||
#endif
|
||||
#if FEX_ENABLE_RAR
|
||||
fex_rar_type,
|
||||
#endif
|
||||
@@ -41,7 +49,7 @@ BLARGG_EXPORT const fex_type_t* fex_type_list( void )
|
||||
fex_bin_type,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
return fex_type_list_;
|
||||
}
|
||||
|
||||
@@ -60,8 +68,19 @@ BLARGG_EXPORT fex_err_t fex_init( void )
|
||||
return blargg_ok;
|
||||
}
|
||||
|
||||
BLARGG_EXPORT const char* fex_identify_header( void const* header )
|
||||
BLARGG_EXPORT const char* fex_identify_header(const void* header)
|
||||
{
|
||||
const unsigned char* data = static_cast<const unsigned char*>(header);
|
||||
|
||||
// Safely detect .xz (magic bytes at offset 0)
|
||||
if (memcmp(data, "\xFD\x37\x7A\x58\x5A\x00", 6) == 0)
|
||||
return ".xz";
|
||||
|
||||
// Safely detect .tar (magic string at offset 257)
|
||||
const char tar_magic[] = "ustar";
|
||||
if (memcmp(data + 257, tar_magic, sizeof(tar_magic)) == 0 && data[262] == 0x00)
|
||||
return ".tar";
|
||||
|
||||
unsigned four = get_be32( header );
|
||||
switch ( four )
|
||||
{
|
||||
@@ -77,6 +96,8 @@ BLARGG_EXPORT const char* fex_identify_header( void const* header )
|
||||
case 0x41724301: return ".arc";
|
||||
case 0x4D534346: return ".cab";
|
||||
case 0x5A4F4F20: return ".zoo";
|
||||
case 0x04224D18: return ".lz4";
|
||||
case 0x4C5A4950: return ".lz";
|
||||
}
|
||||
|
||||
unsigned three = four >> 8;
|
||||
@@ -130,6 +151,7 @@ static int is_archive_extension( const char str [] )
|
||||
".dmg",
|
||||
".gz",
|
||||
".lha",
|
||||
".lz4",
|
||||
".lz",
|
||||
".lzh",
|
||||
".lzma",
|
||||
@@ -139,8 +161,10 @@ static int is_archive_extension( const char str [] )
|
||||
".rar",
|
||||
".sit",
|
||||
".sitx",
|
||||
".tar",
|
||||
".tgz",
|
||||
".tlz",
|
||||
".xz",
|
||||
".z",
|
||||
".zip",
|
||||
".zoo",
|
||||
@@ -177,7 +201,7 @@ BLARGG_EXPORT fex_err_t fex_identify_file( fex_type_t* type_out, const char path
|
||||
*type_out = NULL;
|
||||
|
||||
fex_type_t type = fex_identify_extension( path );
|
||||
|
||||
|
||||
// Unsupported extension?
|
||||
if ( !type )
|
||||
return blargg_ok; // reject
|
||||
@@ -192,11 +216,11 @@ BLARGG_EXPORT fex_err_t fex_identify_file( fex_type_t* type_out, const char path
|
||||
{
|
||||
char h [fex_identify_header_size];
|
||||
RETURN_ERR( in.read( h, sizeof h ) );
|
||||
|
||||
|
||||
type = fex_identify_extension( fex_identify_header( h ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
*type_out = type;
|
||||
return blargg_ok;
|
||||
}
|
||||
|
92
src/core/fex/unrar/acknow.txt
Normal file
92
src/core/fex/unrar/acknow.txt
Normal file
@@ -0,0 +1,92 @@
|
||||
ACKNOWLEDGMENTS
|
||||
|
||||
* We used "Screaming Fast Galois Field Arithmetic Using Intel
|
||||
SIMD Instructions" paper by James S. Plank, Kevin M. Greenan
|
||||
and Ethan L. Miller to improve Reed-Solomon coding performance.
|
||||
Also we are grateful to Artem Drobanov and Bulat Ziganshin
|
||||
for samples and ideas allowed to make Reed-Solomon coding
|
||||
more efficient.
|
||||
|
||||
* RAR text compression algorithm is based on Dmitry Shkarin PPMII
|
||||
and Dmitry Subbotin carryless rangecoder public domain source code.
|
||||
You may find it in ftp.elf.stuba.sk/pub/pc/pack.
|
||||
|
||||
* RAR encryption includes parts of code from Szymon Stefanek
|
||||
and Brian Gladman AES implementations also as Steve Reid SHA-1 source.
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
Copyright (c) 2002, Dr Brian Gladman < >, Worcester, UK.
|
||||
All rights reserved.
|
||||
|
||||
LICENSE TERMS
|
||||
|
||||
The free distribution and use of this software in both source and binary
|
||||
form is allowed (with or without changes) provided that:
|
||||
|
||||
1. distributions of this source code include the above copyright
|
||||
notice, this list of conditions and the following disclaimer;
|
||||
|
||||
2. distributions in binary form include the above copyright
|
||||
notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other associated materials;
|
||||
|
||||
3. the copyright holder's name is not used to endorse products
|
||||
built using this software without specific written permission.
|
||||
|
||||
ALTERNATIVELY, provided that this notice is retained in full, this product
|
||||
may be distributed under the terms of the GNU General Public License (GPL),
|
||||
in which case the provisions of the GPL apply INSTEAD OF those given above.
|
||||
|
||||
DISCLAIMER
|
||||
|
||||
This software is provided 'as is' with no explicit or implied warranties
|
||||
in respect of its properties, including, but not limited to, correctness
|
||||
and/or fitness for purpose.
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
Source code of this package also as other cryptographic technology
|
||||
and computing project related links are available on Brian Gladman's
|
||||
web site: http://www.gladman.me.uk
|
||||
|
||||
* RAR uses CRC32 function based on Intel Slicing-by-8 algorithm.
|
||||
Original Intel Slicing-by-8 code is available here:
|
||||
|
||||
http://sourceforge.net/projects/slicing-by-8/
|
||||
|
||||
Original Intel Slicing-by-8 code is licensed under BSD License
|
||||
available at http://www.opensource.org/licenses/bsd-license.html
|
||||
|
||||
Copyright (c) 2004-2006 Intel Corporation.
|
||||
All Rights Reserved
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with
|
||||
the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
|
||||
* RAR archives may optionally include BLAKE2sp hash ( https://blake2.net ),
|
||||
designed by Jean-Philippe Aumasson, Samuel Neves, Zooko Wilcox-O'Hearn
|
||||
and Christian Winnerlein.
|
||||
|
||||
* Useful hints provided by Alexander Khoroshev and Bulat Ziganshin allowed
|
||||
to significantly improve RAR compression and speed.
|
100
src/core/fex/unrar/archive.cpp
Normal file
100
src/core/fex/unrar/archive.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
#include <stdio.h>
|
||||
#include "rar.hpp"
|
||||
|
||||
#include "unrar.h"
|
||||
|
||||
Archive::Archive() : Raw( this )
|
||||
{
|
||||
Format=RARFMT15;
|
||||
Solid=false;
|
||||
|
||||
CurBlockPos=0;
|
||||
NextBlockPos=0;
|
||||
|
||||
memset(&MainHead,0,sizeof(MainHead));
|
||||
memset(&EndArcHead,0,sizeof(EndArcHead));
|
||||
|
||||
HeaderCRC=0;
|
||||
}
|
||||
|
||||
RARFORMAT Archive::IsSignature(const byte *D,size_t Size)
|
||||
{
|
||||
RARFORMAT Type=RARFMT_NONE;
|
||||
if (Size>=1 && D[0]==0x52) {
|
||||
#ifndef SFX_MODULE
|
||||
if (Size>=4 && D[1]==0x45 && D[2]==0x7e && D[3]==0x5e)
|
||||
Type=RARFMT14;
|
||||
else {
|
||||
#endif
|
||||
if (Size>=7 && D[1]==0x61 && D[2]==0x72 && D[3]==0x21 && D[4]==0x1a && D[5]==0x07)
|
||||
{
|
||||
// We check for non-zero last signature byte, so we can return
|
||||
// a sensible warning in case we'll want to change the archive
|
||||
// format sometimes in the future.
|
||||
if (D[6]==0)
|
||||
Type=RARFMT15;
|
||||
else if (D[6]==1)
|
||||
Type=RARFMT50;
|
||||
else if (D[6]==2)
|
||||
Type=RARFMT_FUTURE;
|
||||
}
|
||||
#ifndef SFX_MODULE
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return Type;
|
||||
}
|
||||
|
||||
|
||||
unrar_err_t Archive::IsArchive()
|
||||
{
|
||||
if (Read(MarkHead.Mark,SIZEOF_MARKHEAD3)!=SIZEOF_MARKHEAD3)
|
||||
return unrar_err_not_arc;
|
||||
|
||||
RARFORMAT Type;
|
||||
if ((Type=IsSignature(MarkHead.Mark,SIZEOF_MARKHEAD3))!=RARFMT_NONE)
|
||||
{
|
||||
Format=Type;
|
||||
if (Format==RARFMT14)
|
||||
Seek(Tell()-SIZEOF_MARKHEAD3,SEEK_SET);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (SFXSize==0)
|
||||
return unrar_err_not_arc;
|
||||
}
|
||||
if (Format==RARFMT_FUTURE)
|
||||
return unrar_err_new_algo;
|
||||
if (Format==RARFMT50) // RAR 5.0 signature is one byte longer.
|
||||
{
|
||||
Read(MarkHead.Mark+SIZEOF_MARKHEAD3,1);
|
||||
if (MarkHead.Mark[SIZEOF_MARKHEAD3]!=0)
|
||||
return unrar_err_not_arc;
|
||||
MarkHead.HeadSize=SIZEOF_MARKHEAD5;
|
||||
}
|
||||
else
|
||||
MarkHead.HeadSize=SIZEOF_MARKHEAD3;
|
||||
|
||||
unrar_err_t error;
|
||||
size_t HeaderSize;
|
||||
while ((error=ReadHeader(&HeaderSize))==unrar_ok && HeaderSize!=0)
|
||||
{
|
||||
HEADER_TYPE Type=GetHeaderType();
|
||||
// In RAR 5.0 we need to quit after reading HEAD_CRYPT if we wish to
|
||||
// avoid the password prompt.
|
||||
if (Type==HEAD_MAIN)
|
||||
break;
|
||||
SeekToNext();
|
||||
}
|
||||
if ( error != unrar_ok )
|
||||
return error;
|
||||
|
||||
SeekToNext();
|
||||
|
||||
return unrar_ok;
|
||||
}
|
||||
|
||||
void Archive::SeekToNext()
|
||||
{
|
||||
Seek(NextBlockPos,SEEK_SET);
|
||||
}
|
55
src/core/fex/unrar/archive.hpp
Normal file
55
src/core/fex/unrar/archive.hpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#ifndef _RAR_ARCHIVE_
|
||||
#define _RAR_ARCHIVE_
|
||||
|
||||
typedef ComprDataIO File;
|
||||
#include "rawread.hpp"
|
||||
|
||||
enum RARFORMAT {RARFMT_NONE,RARFMT14,RARFMT15,RARFMT50,RARFMT_FUTURE};
|
||||
|
||||
class Archive:public File
|
||||
{
|
||||
private:
|
||||
void ConvertFileHeader(FileHeader *hd);
|
||||
void WriteBlock50(HEADER_TYPE HeaderType,BaseBlock *wb,bool OnlySetSize,bool NonFinalWrite);
|
||||
unrar_err_t ReadHeader14(size_t *ReadSize);
|
||||
unrar_err_t ReadHeader15(size_t *ReadSize);
|
||||
unrar_err_t ReadHeader50(size_t *ReadSize);
|
||||
unrar_err_t ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb);
|
||||
|
||||
RawRead Raw;
|
||||
|
||||
HEADER_TYPE CurHeaderType;
|
||||
|
||||
public:
|
||||
Archive();
|
||||
RARFORMAT IsSignature(const byte *D,size_t Size);
|
||||
unrar_err_t IsArchive();
|
||||
size_t SearchBlock(HEADER_TYPE HeaderType);
|
||||
size_t SearchSubBlock(const wchar *Type);
|
||||
size_t SearchRR();
|
||||
unrar_err_t ReadHeader(size_t *ReadSize);
|
||||
void SeekToNext();
|
||||
bool IsArcDir();
|
||||
bool IsArcLabel();
|
||||
int64 GetStartPos();
|
||||
HEADER_TYPE GetHeaderType() {return(CurHeaderType);};
|
||||
|
||||
BaseBlock ShortBlock;
|
||||
MarkHeader MarkHead;
|
||||
MainHeader MainHead;
|
||||
FileHeader FileHead;
|
||||
EndArcHeader EndArcHead;
|
||||
SubBlockHeader SubBlockHead;
|
||||
FileHeader SubHead;
|
||||
ProtectHeader ProtectHead;
|
||||
|
||||
int64 CurBlockPos;
|
||||
int64 NextBlockPos;
|
||||
|
||||
RARFORMAT Format;
|
||||
bool Solid;
|
||||
enum { SFXSize = 0 }; // self-extracting not supported
|
||||
ushort HeaderCRC;
|
||||
};
|
||||
|
||||
#endif
|
739
src/core/fex/unrar/arcread.cpp
Normal file
739
src/core/fex/unrar/arcread.cpp
Normal file
@@ -0,0 +1,739 @@
|
||||
#include "rar.hpp"
|
||||
|
||||
#include "unrar.h"
|
||||
#include "unicode.hpp"
|
||||
#include "encname.hpp"
|
||||
|
||||
// arcread.cpp
|
||||
unrar_err_t Archive::ReadHeader(size_t * ReadSize_)
|
||||
{
|
||||
CurBlockPos=Tell();
|
||||
|
||||
unrar_err_t Error;
|
||||
size_t ReadSize;
|
||||
switch(Format)
|
||||
{
|
||||
#ifndef SFX_MODULE
|
||||
case RARFMT14:
|
||||
Error=ReadHeader14(&ReadSize);
|
||||
break;
|
||||
#endif
|
||||
case RARFMT15:
|
||||
Error=ReadHeader15(&ReadSize);
|
||||
break;
|
||||
case RARFMT50:
|
||||
Error=ReadHeader50(&ReadSize);
|
||||
break;
|
||||
|
||||
default: // unreachable
|
||||
Error=unrar_err_corrupt;
|
||||
break;
|
||||
}
|
||||
|
||||
if (Error!=unrar_ok)
|
||||
return Error;
|
||||
|
||||
if (ReadSize>0 && NextBlockPos<=CurBlockPos)
|
||||
return unrar_err_corrupt;
|
||||
|
||||
*ReadSize_ = ReadSize;
|
||||
|
||||
return unrar_ok;
|
||||
}
|
||||
|
||||
|
||||
unrar_err_t Archive::ReadHeader15(size_t *ReadSize)
|
||||
{
|
||||
Raw.Reset();
|
||||
|
||||
Raw.Read(SIZEOF_SHORTBLOCKHEAD);
|
||||
if (Raw.Size()==0)
|
||||
return unrar_err_corrupt;
|
||||
|
||||
ShortBlock.HeadCRC=Raw.Get2();
|
||||
|
||||
ShortBlock.Reset();
|
||||
|
||||
uint HeaderType=Raw.Get1();
|
||||
ShortBlock.Flags=Raw.Get2();
|
||||
ShortBlock.SkipIfUnknown=(ShortBlock.Flags & SKIP_IF_UNKNOWN)!=0;
|
||||
ShortBlock.HeadSize=Raw.Get2();
|
||||
|
||||
ShortBlock.HeaderType=(HEADER_TYPE)HeaderType;
|
||||
if (ShortBlock.HeadSize<SIZEOF_SHORTBLOCKHEAD)
|
||||
return unrar_err_corrupt;
|
||||
|
||||
// For simpler further processing we map header types common
|
||||
// for RAR 1.5 and 5.0 formats to RAR 5.0 values. It does not include
|
||||
// header types specific for RAR 1.5 - 4.x only.
|
||||
switch(ShortBlock.HeaderType)
|
||||
{
|
||||
case HEAD3_MAIN: ShortBlock.HeaderType=HEAD_MAIN; break;
|
||||
case HEAD3_FILE: ShortBlock.HeaderType=HEAD_FILE; break;
|
||||
case HEAD3_SERVICE: ShortBlock.HeaderType=HEAD_SERVICE; break;
|
||||
case HEAD3_ENDARC: ShortBlock.HeaderType=HEAD_ENDARC; break;
|
||||
default: break;
|
||||
}
|
||||
CurHeaderType=ShortBlock.HeaderType;
|
||||
|
||||
if (ShortBlock.HeaderType==HEAD3_CMT)
|
||||
{
|
||||
// Old style (up to RAR 2.9) comment header embedded into main
|
||||
// or file header. We must not read the entire ShortBlock.HeadSize here
|
||||
// to not break the comment processing logic later.
|
||||
Raw.Read(SIZEOF_COMMHEAD-SIZEOF_SHORTBLOCKHEAD);
|
||||
}
|
||||
else
|
||||
if (ShortBlock.HeaderType==HEAD_MAIN && (ShortBlock.Flags & MHD_COMMENT)!=0)
|
||||
{
|
||||
// Old style (up to RAR 2.9) main archive comment embedded into
|
||||
// the main archive header found. While we can read the entire
|
||||
// ShortBlock.HeadSize here and remove this part of "if", it would be
|
||||
// waste of memory, because we'll read and process this comment data
|
||||
// in other function anyway and we do not need them here now.
|
||||
Raw.Read(SIZEOF_MAINHEAD3-SIZEOF_SHORTBLOCKHEAD);
|
||||
}
|
||||
else
|
||||
Raw.Read(ShortBlock.HeadSize-SIZEOF_SHORTBLOCKHEAD);
|
||||
|
||||
NextBlockPos=CurBlockPos+ShortBlock.HeadSize;
|
||||
|
||||
switch(ShortBlock.HeaderType)
|
||||
{
|
||||
case HEAD_MAIN:
|
||||
MainHead.Reset();
|
||||
*(BaseBlock *)&MainHead=ShortBlock;
|
||||
MainHead.HighPosAV=Raw.Get2();
|
||||
MainHead.PosAV=Raw.Get4();
|
||||
|
||||
Solid=(MainHead.Flags & MHD_SOLID)!=0;
|
||||
MainHead.CommentInHeader=(MainHead.Flags & MHD_COMMENT)!=0;
|
||||
break;
|
||||
case HEAD_FILE:
|
||||
case HEAD_SERVICE:
|
||||
{
|
||||
bool FileBlock=ShortBlock.HeaderType==HEAD_FILE;
|
||||
FileHeader *hd=FileBlock ? &FileHead:&SubHead;
|
||||
hd->Reset();
|
||||
|
||||
*(BaseBlock *)hd=ShortBlock;
|
||||
|
||||
hd->SplitBefore=(hd->Flags & LHD_SPLIT_BEFORE)!=0;
|
||||
hd->SplitAfter=(hd->Flags & LHD_SPLIT_AFTER)!=0;
|
||||
hd->Encrypted=(hd->Flags & LHD_PASSWORD)!=0;
|
||||
hd->Solid=FileBlock && (hd->Flags & LHD_SOLID)!=0;
|
||||
hd->SubBlock=!FileBlock && (hd->Flags & LHD_SOLID)!=0;
|
||||
hd->Dir=(hd->Flags & LHD_WINDOWMASK)==LHD_DIRECTORY;
|
||||
hd->WinSize=hd->Dir ? 0:0x10000<<((hd->Flags & LHD_WINDOWMASK)>>5);
|
||||
hd->CommentInHeader=(hd->Flags & LHD_COMMENT)!=0;
|
||||
hd->Version=(hd->Flags & LHD_VERSION)!=0;
|
||||
|
||||
hd->DataSize=Raw.Get4();
|
||||
uint LowUnpSize=Raw.Get4();
|
||||
hd->HostOS=Raw.Get1();
|
||||
|
||||
hd->FileHash.Type=HASH_CRC32;
|
||||
hd->FileHash.CRC32=Raw.Get4();
|
||||
|
||||
uint FileTime=Raw.Get4();
|
||||
hd->UnpVer=Raw.Get1();
|
||||
hd->Method=Raw.Get1()-0x30;
|
||||
size_t NameSize=Raw.Get2();
|
||||
hd->FileAttr=Raw.Get4();
|
||||
|
||||
if (hd->Encrypted)
|
||||
return unrar_err_encrypted;
|
||||
|
||||
hd->HSType=HSYS_UNKNOWN;
|
||||
if (hd->HostOS==HOST_UNIX || hd->HostOS==HOST_BEOS)
|
||||
hd->HSType=HSYS_UNIX;
|
||||
else
|
||||
if (hd->HostOS<HOST_MAX)
|
||||
hd->HSType=HSYS_WINDOWS;
|
||||
|
||||
hd->RedirType=FSREDIR_NONE;
|
||||
|
||||
// RAR 4.x Unix symlink.
|
||||
if (hd->HostOS==HOST_UNIX && (hd->FileAttr & 0xF000)==0xA000)
|
||||
{
|
||||
hd->RedirType=FSREDIR_UNIXSYMLINK;
|
||||
*hd->RedirName=0;
|
||||
}
|
||||
|
||||
hd->Inherited=!FileBlock && (hd->SubFlags & SUBHEAD_FLAGS_INHERITED)!=0;
|
||||
|
||||
hd->LargeFile=(hd->Flags & LHD_LARGE)!=0;
|
||||
|
||||
uint HighPackSize,HighUnpSize;
|
||||
if (hd->LargeFile)
|
||||
{
|
||||
HighPackSize=Raw.Get4();
|
||||
HighUnpSize=Raw.Get4();
|
||||
hd->UnknownUnpSize=(LowUnpSize==0xffffffff && HighUnpSize==0xffffffff);
|
||||
}
|
||||
else
|
||||
{
|
||||
HighPackSize=HighUnpSize=0;
|
||||
// UnpSize equal to 0xffffffff without LHD_LARGE flag indicates
|
||||
// that we do not know the unpacked file size and must unpack it
|
||||
// until we find the end of file marker in compressed data.
|
||||
hd->UnknownUnpSize=(LowUnpSize==0xffffffff);
|
||||
}
|
||||
hd->PackSize=int32to64(HighPackSize,hd->DataSize);
|
||||
hd->UnpSize=int32to64(HighUnpSize,LowUnpSize);
|
||||
if (hd->UnknownUnpSize)
|
||||
hd->UnpSize=INT64NDF;
|
||||
|
||||
char FileName[NM*4];
|
||||
size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1);
|
||||
Raw.GetB((byte *)FileName,ReadNameSize);
|
||||
FileName[ReadNameSize]=0;
|
||||
|
||||
if (FileBlock)
|
||||
{
|
||||
if ((hd->Flags & LHD_UNICODE)!=0)
|
||||
{
|
||||
EncodeFileName NameCoder;
|
||||
size_t Length=strlen(FileName);
|
||||
Length++;
|
||||
NameCoder.Decode(FileName,(byte *)FileName+Length,
|
||||
NameSize-Length,hd->FileName,
|
||||
ASIZE(hd->FileName));
|
||||
}
|
||||
else
|
||||
*hd->FileName=0;
|
||||
|
||||
char AnsiName[NM];
|
||||
IntToExt(FileName,AnsiName,ASIZE(AnsiName));
|
||||
GetWideName(AnsiName,hd->FileName,hd->FileName,ASIZE(hd->FileName));
|
||||
|
||||
ConvertFileHeader(hd);
|
||||
}
|
||||
else
|
||||
{
|
||||
CharToWide(FileName,hd->FileName,ASIZE(hd->FileName));
|
||||
|
||||
// Calculate the size of optional data.
|
||||
int DataSize=int(hd->HeadSize-NameSize-SIZEOF_FILEHEAD3);
|
||||
if ((hd->Flags & LHD_SALT)!=0)
|
||||
return unrar_err_encrypted;
|
||||
|
||||
if (DataSize>0)
|
||||
{
|
||||
// Here we read optional additional fields for subheaders.
|
||||
// They are stored after the file name and before salt.
|
||||
hd->SubData.Alloc(DataSize);
|
||||
Raw.GetB(&hd->SubData[0],DataSize);
|
||||
}
|
||||
}
|
||||
if ((hd->Flags & LHD_SALT)!=0)
|
||||
return unrar_err_encrypted;
|
||||
hd->mtime.SetDos(FileTime);
|
||||
if ((hd->Flags & LHD_EXTTIME)!=0)
|
||||
{
|
||||
ushort Flags=Raw.Get2();
|
||||
RarTime *tbl[4];
|
||||
tbl[0]=&FileHead.mtime;
|
||||
tbl[1]=&FileHead.ctime;
|
||||
tbl[2]=&FileHead.atime;
|
||||
tbl[3]=NULL; // Archive time is not used now.
|
||||
for (int I=0;I<4;I++)
|
||||
{
|
||||
RarTime *CurTime=tbl[I];
|
||||
uint rmode=Flags>>(3-I)*4;
|
||||
if ((rmode & 8)==0 || CurTime==NULL)
|
||||
continue;
|
||||
if (I!=0)
|
||||
{
|
||||
uint DosTime=Raw.Get4();
|
||||
CurTime->SetDos(DosTime);
|
||||
}
|
||||
RarLocalTime rlt;
|
||||
CurTime->GetLocal(&rlt);
|
||||
if (rmode & 4)
|
||||
rlt.Second++;
|
||||
rlt.Reminder=0;
|
||||
int count=rmode&3;
|
||||
for (int J=0;J<count;J++)
|
||||
{
|
||||
byte CurByte=Raw.Get1();
|
||||
rlt.Reminder|=(((uint)CurByte)<<((J+3-count)*8));
|
||||
}
|
||||
CurTime->SetLocal(&rlt);
|
||||
}
|
||||
}
|
||||
NextBlockPos+=hd->PackSize;
|
||||
bool CRCProcessedOnly=hd->CommentInHeader;
|
||||
ushort HeaderCRC=Raw.GetCRC15(CRCProcessedOnly);
|
||||
if (hd->HeadCRC!=HeaderCRC)
|
||||
return unrar_err_corrupt;
|
||||
}
|
||||
break;
|
||||
case HEAD_ENDARC:
|
||||
*(BaseBlock *)&EndArcHead=ShortBlock;
|
||||
EndArcHead.NextVolume=(EndArcHead.Flags & EARC_NEXT_VOLUME)!=0;
|
||||
EndArcHead.DataCRC=(EndArcHead.Flags & EARC_DATACRC)!=0;
|
||||
EndArcHead.RevSpace=(EndArcHead.Flags & EARC_REVSPACE)!=0;
|
||||
EndArcHead.StoreVolNumber=(EndArcHead.Flags & EARC_VOLNUMBER)!=0;
|
||||
if (EndArcHead.DataCRC)
|
||||
EndArcHead.ArcDataCRC=Raw.Get4();
|
||||
if (EndArcHead.StoreVolNumber)
|
||||
return unrar_err_segmented;
|
||||
break;
|
||||
default:
|
||||
if (ShortBlock.Flags & LONG_BLOCK)
|
||||
NextBlockPos+=Raw.Get4();
|
||||
break;
|
||||
}
|
||||
|
||||
ushort HeaderCRC=Raw.GetCRC15(false);
|
||||
|
||||
// Old AV header does not have header CRC properly set.
|
||||
if (ShortBlock.HeadCRC!=HeaderCRC && ShortBlock.HeaderType!=HEAD3_SIGN &&
|
||||
ShortBlock.HeaderType!=HEAD3_AV)
|
||||
return unrar_err_corrupt;
|
||||
|
||||
if (NextBlockPos<=CurBlockPos)
|
||||
return unrar_err_corrupt;
|
||||
|
||||
*ReadSize=Raw.Size();
|
||||
return unrar_ok;
|
||||
}
|
||||
|
||||
|
||||
unrar_err_t Archive::ReadHeader50(size_t *ReadSize)
|
||||
{
|
||||
Raw.Reset();
|
||||
|
||||
// Header size must not occupy more than 3 variable length integer bytes
|
||||
// resulting in 2 MB maximum header size, so here we read 4 byte CRC32
|
||||
// followed by 3 bytes or less of header size.
|
||||
const size_t FirstReadSize=7; // Smallest possible block size.
|
||||
if (Raw.Read(FirstReadSize)<FirstReadSize)
|
||||
return unrar_err_arc_eof;
|
||||
|
||||
ShortBlock.Reset();
|
||||
ShortBlock.HeadCRC=Raw.Get4();
|
||||
uint SizeBytes=Raw.GetVSize(4);
|
||||
uint64 BlockSize=Raw.GetV();
|
||||
|
||||
if (BlockSize==0 || SizeBytes==0)
|
||||
return unrar_err_corrupt;
|
||||
|
||||
int SizeToRead=int(BlockSize);
|
||||
SizeToRead-=FirstReadSize-SizeBytes-4; // Adjust overread size bytes if any.
|
||||
uint HeaderSize=4+SizeBytes+(uint)BlockSize;
|
||||
|
||||
if (SizeToRead<0 || HeaderSize<SIZEOF_SHORTBLOCKHEAD5)
|
||||
return unrar_err_corrupt;
|
||||
|
||||
Raw.Read(SizeToRead);
|
||||
|
||||
if (Raw.Size()<HeaderSize)
|
||||
return unrar_err_arc_eof;
|
||||
|
||||
uint HeaderCRC=Raw.GetCRC50();
|
||||
|
||||
ShortBlock.HeaderType=(HEADER_TYPE)Raw.GetV();
|
||||
ShortBlock.Flags=(uint)Raw.GetV();
|
||||
ShortBlock.SkipIfUnknown=(ShortBlock.Flags & HFL_SKIPIFUNKNOWN)!=0;
|
||||
ShortBlock.HeadSize=HeaderSize;
|
||||
|
||||
CurHeaderType=ShortBlock.HeaderType;
|
||||
|
||||
bool BadCRC=(ShortBlock.HeadCRC!=HeaderCRC);
|
||||
if (BadCRC)
|
||||
return unrar_err_corrupt;
|
||||
|
||||
uint64 ExtraSize=0;
|
||||
if ((ShortBlock.Flags & HFL_EXTRA)!=0)
|
||||
{
|
||||
ExtraSize=Raw.GetV();
|
||||
if (ExtraSize>=ShortBlock.HeadSize)
|
||||
return unrar_err_corrupt;
|
||||
}
|
||||
|
||||
uint64 DataSize=0;
|
||||
if ((ShortBlock.Flags & HFL_DATA)!=0)
|
||||
DataSize=Raw.GetV();
|
||||
|
||||
NextBlockPos=CurBlockPos+ShortBlock.HeadSize+DataSize;
|
||||
|
||||
switch(ShortBlock.HeaderType)
|
||||
{
|
||||
case HEAD_CRYPT:
|
||||
return unrar_err_encrypted;
|
||||
case HEAD_MAIN:
|
||||
{
|
||||
MainHead.Reset();
|
||||
*(BaseBlock *)&MainHead=ShortBlock;
|
||||
uint ArcFlags=(uint)Raw.GetV();
|
||||
|
||||
Solid=(ArcFlags & MHFL_SOLID)!=0;
|
||||
|
||||
if (ExtraSize!=0)
|
||||
{
|
||||
unrar_err_t Error;
|
||||
if ((Error=ProcessExtra50(&Raw,(size_t)ExtraSize,&MainHead))!=unrar_ok)
|
||||
return Error;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case HEAD_FILE:
|
||||
case HEAD_SERVICE:
|
||||
{
|
||||
FileHeader *hd=ShortBlock.HeaderType==HEAD_FILE ? &FileHead:&SubHead;
|
||||
hd->Reset();
|
||||
*(BaseBlock *)hd=ShortBlock;
|
||||
|
||||
bool FileBlock=ShortBlock.HeaderType==HEAD_FILE;
|
||||
|
||||
hd->LargeFile=true;
|
||||
|
||||
hd->PackSize=DataSize;
|
||||
hd->FileFlags=(uint)Raw.GetV();
|
||||
hd->UnpSize=Raw.GetV();
|
||||
|
||||
hd->UnknownUnpSize=(hd->FileFlags & FHFL_UNPUNKNOWN)!=0;
|
||||
if (hd->UnknownUnpSize)
|
||||
hd->UnpSize=INT64NDF;
|
||||
|
||||
hd->MaxSize=Max(hd->PackSize,hd->UnpSize);
|
||||
hd->FileAttr=(uint)Raw.GetV();
|
||||
if ((hd->FileFlags & FHFL_UTIME)!=0)
|
||||
hd->mtime=(time_t)Raw.Get4();
|
||||
|
||||
hd->FileHash.Type=HASH_NONE;
|
||||
if ((hd->FileFlags & FHFL_CRC32)!=0)
|
||||
{
|
||||
hd->FileHash.Type=HASH_CRC32;
|
||||
hd->FileHash.CRC32=Raw.Get4();
|
||||
}
|
||||
|
||||
hd->RedirType=FSREDIR_NONE;
|
||||
|
||||
uint CompInfo=(uint)Raw.GetV();
|
||||
hd->Method=(CompInfo>>7) & 7;
|
||||
hd->UnpVer=CompInfo & 0x3f;
|
||||
|
||||
hd->HostOS=(byte)Raw.GetV();
|
||||
size_t NameSize=(size_t)Raw.GetV();
|
||||
hd->Inherited=(ShortBlock.Flags & HFL_INHERITED)!=0;
|
||||
|
||||
hd->HSType=HSYS_UNKNOWN;
|
||||
if (hd->HostOS==HOST5_UNIX)
|
||||
hd->HSType=HSYS_UNIX;
|
||||
else
|
||||
if (hd->HostOS==HOST5_WINDOWS)
|
||||
hd->HSType=HSYS_WINDOWS;
|
||||
|
||||
hd->SplitBefore=(hd->Flags & HFL_SPLITBEFORE)!=0;
|
||||
hd->SplitAfter=(hd->Flags & HFL_SPLITAFTER)!=0;
|
||||
hd->SubBlock=(hd->Flags & HFL_CHILD)!=0;
|
||||
hd->Solid=FileBlock && (CompInfo & FCI_SOLID)!=0;
|
||||
hd->Dir=(hd->FileFlags & FHFL_DIRECTORY)!=0;
|
||||
hd->WinSize=hd->Dir ? 0:size_t(0x20000)<<((CompInfo>>10)&0xf);
|
||||
|
||||
if (hd->Encrypted)
|
||||
return unrar_err_encrypted;
|
||||
|
||||
char FileName[NM*4];
|
||||
size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1);
|
||||
Raw.GetB((byte *)FileName,ReadNameSize);
|
||||
FileName[ReadNameSize]=0;
|
||||
|
||||
UtfToWide(FileName,hd->FileName,ASIZE(hd->FileName)-1);
|
||||
|
||||
// Should do it before converting names, because extra fields can
|
||||
// affect name processing, like in case of NTFS streams.
|
||||
if (ExtraSize!=0)
|
||||
ProcessExtra50(&Raw,(size_t)ExtraSize,hd);
|
||||
|
||||
if (FileBlock)
|
||||
ConvertFileHeader(hd);
|
||||
|
||||
if (BadCRC) // Add the file name to broken header message displayed above.
|
||||
return unrar_err_corrupt;
|
||||
}
|
||||
break;
|
||||
case HEAD_ENDARC:
|
||||
{
|
||||
*(BaseBlock *)&EndArcHead=ShortBlock;
|
||||
uint ArcFlags=(uint)Raw.GetV();
|
||||
EndArcHead.NextVolume=(ArcFlags & EHFL_NEXTVOLUME)!=0;
|
||||
EndArcHead.StoreVolNumber=false;
|
||||
EndArcHead.DataCRC=false;
|
||||
EndArcHead.RevSpace=false;
|
||||
}
|
||||
break;
|
||||
case HEAD_MARK:
|
||||
case HEAD3_MARK:
|
||||
case HEAD3_MAIN:
|
||||
break;
|
||||
}
|
||||
|
||||
if (NextBlockPos<=CurBlockPos)
|
||||
return unrar_err_corrupt;
|
||||
|
||||
*ReadSize=Raw.Size();
|
||||
|
||||
return unrar_ok;
|
||||
}
|
||||
|
||||
|
||||
unrar_err_t Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb)
|
||||
{
|
||||
// Read extra data from the end of block skipping any fields before it.
|
||||
size_t ExtraStart=Raw->Size()-ExtraSize;
|
||||
if (ExtraStart<Raw->GetPos())
|
||||
return unrar_err_corrupt;
|
||||
Raw->SetPos(ExtraStart);
|
||||
while (Raw->DataLeft()>=2)
|
||||
{
|
||||
int64 FieldSize=Raw->GetV();
|
||||
if (FieldSize==0 || Raw->DataLeft()==0 || FieldSize>(int64)Raw->DataLeft())
|
||||
break;
|
||||
size_t NextPos=size_t(Raw->GetPos()+FieldSize);
|
||||
uint64 FieldType=Raw->GetV();
|
||||
|
||||
FieldSize=Raw->DataLeft(); // Field size without size and type fields.
|
||||
|
||||
if (bb->HeaderType==HEAD_MAIN)
|
||||
{
|
||||
MainHeader *hd=(MainHeader *)bb;
|
||||
if (FieldType==MHEXTRA_LOCATOR)
|
||||
{
|
||||
hd->Locator=true;
|
||||
uint Flags=(uint)Raw->GetV();
|
||||
if ((Flags & MHEXTRA_LOCATOR_QLIST)!=0)
|
||||
{
|
||||
uint64 Offset=Raw->GetV();
|
||||
if (Offset!=0) // 0 means that reserved space was not enough to write the offset.
|
||||
hd->QOpenOffset=Offset+CurBlockPos;
|
||||
}
|
||||
if ((Flags & MHEXTRA_LOCATOR_RR)!=0)
|
||||
{
|
||||
uint64 Offset=Raw->GetV();
|
||||
if (Offset!=0) // 0 means that reserved space was not enough to write the offset.
|
||||
hd->RROffset=Offset+CurBlockPos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bb->HeaderType==HEAD_FILE || bb->HeaderType==HEAD_SERVICE)
|
||||
{
|
||||
FileHeader *hd=(FileHeader *)bb;
|
||||
switch(FieldType)
|
||||
{
|
||||
case FHEXTRA_CRYPT:
|
||||
return unrar_err_encrypted;
|
||||
case FHEXTRA_HASH:
|
||||
{
|
||||
FileHeader *hd=(FileHeader *)bb;
|
||||
uint Type=(uint)Raw->GetV();
|
||||
if (Type==FHEXTRA_HASH_BLAKE2)
|
||||
{
|
||||
hd->FileHash.Type=HASH_BLAKE2;
|
||||
Raw->GetB(hd->FileHash.Digest,BLAKE2_DIGEST_SIZE);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FHEXTRA_HTIME:
|
||||
if (FieldSize>=9)
|
||||
{
|
||||
byte Flags=(byte)Raw->GetV();
|
||||
bool UnixTime=(Flags & FHEXTRA_HTIME_UNIXTIME)!=0;
|
||||
if ((Flags & FHEXTRA_HTIME_MTIME)!=0)
|
||||
{
|
||||
if (UnixTime)
|
||||
hd->mtime=(time_t)Raw->Get4();
|
||||
else
|
||||
hd->mtime.SetRaw(Raw->Get8());
|
||||
}
|
||||
if ((Flags & FHEXTRA_HTIME_CTIME)!=0)
|
||||
{
|
||||
if (UnixTime)
|
||||
hd->ctime=(time_t)Raw->Get4();
|
||||
else
|
||||
hd->ctime.SetRaw(Raw->Get8());
|
||||
}
|
||||
if ((Flags & FHEXTRA_HTIME_ATIME)!=0)
|
||||
{
|
||||
if (UnixTime)
|
||||
hd->atime=(time_t)Raw->Get4();
|
||||
else
|
||||
hd->atime.SetRaw(Raw->Get8());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FHEXTRA_REDIR:
|
||||
{
|
||||
hd->RedirType=(FILE_SYSTEM_REDIRECT)Raw->GetV();
|
||||
uint Flags=(uint)Raw->GetV();
|
||||
hd->DirTarget=(Flags & FHEXTRA_REDIR_DIR)!=0;
|
||||
size_t NameSize=(size_t)Raw->GetV();
|
||||
|
||||
char UtfName[NM*4];
|
||||
*UtfName=0;
|
||||
if (NameSize<ASIZE(UtfName)-1)
|
||||
{
|
||||
Raw->GetB(UtfName,NameSize);
|
||||
UtfName[NameSize]=0;
|
||||
}
|
||||
#ifdef _WIN_ALL
|
||||
UnixSlashToDos(UtfName,UtfName,ASIZE(UtfName));
|
||||
#endif
|
||||
UtfToWide(UtfName,hd->RedirName,ASIZE(hd->RedirName));
|
||||
}
|
||||
break;
|
||||
case FHEXTRA_UOWNER:
|
||||
{
|
||||
uint Flags=(uint)Raw->GetV();
|
||||
hd->UnixOwnerNumeric=(Flags & FHEXTRA_UOWNER_NUMUID)!=0;
|
||||
hd->UnixGroupNumeric=(Flags & FHEXTRA_UOWNER_NUMGID)!=0;
|
||||
*hd->UnixOwnerName=*hd->UnixGroupName=0;
|
||||
if ((Flags & FHEXTRA_UOWNER_UNAME)!=0)
|
||||
{
|
||||
size_t Length=(size_t)Raw->GetV();
|
||||
Length=Min(Length,ASIZE(hd->UnixOwnerName)-1);
|
||||
Raw->GetB(hd->UnixOwnerName,Length);
|
||||
hd->UnixOwnerName[Length]=0;
|
||||
}
|
||||
if ((Flags & FHEXTRA_UOWNER_GNAME)!=0)
|
||||
{
|
||||
size_t Length=(size_t)Raw->GetV();
|
||||
Length=Min(Length,ASIZE(hd->UnixGroupName)-1);
|
||||
Raw->GetB(hd->UnixGroupName,Length);
|
||||
hd->UnixGroupName[Length]=0;
|
||||
}
|
||||
#ifdef _UNIX
|
||||
if (hd->UnixOwnerNumeric)
|
||||
hd->UnixOwnerID=(uid_t)Raw->GetV();
|
||||
if (hd->UnixGroupNumeric)
|
||||
hd->UnixGroupID=(uid_t)Raw->GetV();
|
||||
#else
|
||||
// Need these fields in Windows too for 'list' command,
|
||||
// but uid_t and gid_t are not defined.
|
||||
if (hd->UnixOwnerNumeric)
|
||||
hd->UnixOwnerID=(uint)Raw->GetV();
|
||||
if (hd->UnixGroupNumeric)
|
||||
hd->UnixGroupID=(uint)Raw->GetV();
|
||||
#endif
|
||||
hd->UnixOwnerSet=true;
|
||||
}
|
||||
break;
|
||||
case FHEXTRA_SUBDATA:
|
||||
{
|
||||
hd->SubData.Alloc((size_t)FieldSize);
|
||||
Raw->GetB(hd->SubData.Addr(0),(size_t)FieldSize);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Raw->SetPos(NextPos);
|
||||
}
|
||||
|
||||
return unrar_ok;
|
||||
}
|
||||
|
||||
|
||||
#ifndef SFX_MODULE
|
||||
unrar_err_t Archive::ReadHeader14(size_t *ReadSize)
|
||||
{
|
||||
Raw.Reset();
|
||||
|
||||
if (CurBlockPos<=(int64)SFXSize)
|
||||
{
|
||||
Raw.Read(SIZEOF_MAINHEAD14);
|
||||
MainHead.Reset();
|
||||
byte Mark[4];
|
||||
Raw.GetB(Mark,4);
|
||||
uint HeadSize=Raw.Get2();
|
||||
byte Flags=Raw.Get1();
|
||||
NextBlockPos=CurBlockPos+HeadSize;
|
||||
CurHeaderType=HEAD_MAIN;
|
||||
|
||||
Solid=(Flags & MHD_SOLID)!=0;
|
||||
MainHead.CommentInHeader=(Flags & MHD_COMMENT)!=0;
|
||||
MainHead.PackComment=(Flags & MHD_PACK_COMMENT)!=0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Raw.Read(SIZEOF_FILEHEAD14);
|
||||
FileHead.Reset();
|
||||
|
||||
FileHead.HeaderType=HEAD_FILE;
|
||||
FileHead.DataSize=Raw.Get4();
|
||||
FileHead.UnpSize=Raw.Get4();
|
||||
FileHead.FileHash.Type=HASH_RAR14;
|
||||
FileHead.FileHash.CRC32=Raw.Get2();
|
||||
FileHead.HeadSize=Raw.Get2();
|
||||
uint FileTime=Raw.Get4();
|
||||
FileHead.FileAttr=Raw.Get1();
|
||||
FileHead.Flags=Raw.Get1()|LONG_BLOCK;
|
||||
FileHead.UnpVer=(Raw.Get1()==2) ? 13 : 10;
|
||||
size_t NameSize=Raw.Get1();
|
||||
FileHead.Method=Raw.Get1();
|
||||
|
||||
FileHead.SplitBefore=(FileHead.Flags & LHD_SPLIT_BEFORE)!=0;
|
||||
FileHead.SplitAfter=(FileHead.Flags & LHD_SPLIT_AFTER)!=0;
|
||||
FileHead.Encrypted=(FileHead.Flags & LHD_PASSWORD)!=0;
|
||||
if (FileHead.Encrypted)
|
||||
return unrar_err_encrypted;
|
||||
|
||||
FileHead.PackSize=FileHead.DataSize;
|
||||
FileHead.WinSize=0x10000;
|
||||
|
||||
FileHead.mtime.SetDos(FileTime);
|
||||
|
||||
Raw.Read(NameSize);
|
||||
|
||||
char FileName[NM];
|
||||
Raw.GetB((byte *)FileName,Min(NameSize,ASIZE(FileName)));
|
||||
FileName[NameSize]=0;
|
||||
IntToExt(FileName,FileName,ASIZE(FileName));
|
||||
CharToWide(FileName,FileHead.FileName,ASIZE(FileHead.FileName));
|
||||
|
||||
if (Raw.Size()!=0)
|
||||
NextBlockPos=CurBlockPos+FileHead.HeadSize+FileHead.PackSize;
|
||||
CurHeaderType=HEAD_FILE;
|
||||
}
|
||||
*ReadSize=(NextBlockPos>CurBlockPos ? Raw.Size():0);
|
||||
return unrar_ok;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// (removed name case and attribute conversion)
|
||||
|
||||
bool Archive::IsArcDir()
|
||||
{
|
||||
return FileHead.Dir;
|
||||
}
|
||||
|
||||
|
||||
bool Archive::IsArcLabel()
|
||||
{
|
||||
return(FileHead.HostOS<=HOST_WIN32 && (FileHead.FileAttr & 8));
|
||||
}
|
||||
|
||||
|
||||
void Archive::ConvertFileHeader(FileHeader *hd)
|
||||
{
|
||||
if (Format==RARFMT15 && hd->UnpVer<20 && (hd->FileAttr & 0x10))
|
||||
hd->Dir=true;
|
||||
if (hd->HSType==HSYS_UNKNOWN)
|
||||
if (hd->Dir)
|
||||
hd->FileAttr=0x10;
|
||||
else
|
||||
hd->FileAttr=0x20;
|
||||
}
|
||||
|
||||
|
||||
int64 Archive::GetStartPos()
|
||||
{
|
||||
int64 StartPos=SFXSize+MarkHead.HeadSize;
|
||||
StartPos+=MainHead.HeadSize;
|
||||
return StartPos;
|
||||
}
|
171
src/core/fex/unrar/array.hpp
Normal file
171
src/core/fex/unrar/array.hpp
Normal file
@@ -0,0 +1,171 @@
|
||||
#ifndef _RAR_ARRAY_
|
||||
#define _RAR_ARRAY_
|
||||
|
||||
#include <new>
|
||||
|
||||
template <class T> class Array
|
||||
{
|
||||
private:
|
||||
T *Buffer;
|
||||
size_t BufSize;
|
||||
size_t AllocSize;
|
||||
size_t MaxSize;
|
||||
public:
|
||||
Array();
|
||||
Array(size_t Size);
|
||||
Array(const Array &Src); // Copy constructor.
|
||||
~Array();
|
||||
inline void CleanData();
|
||||
inline T& operator [](size_t Item) const;
|
||||
inline T* operator + (size_t Pos);
|
||||
inline size_t Size();
|
||||
void Add(size_t Items);
|
||||
void Alloc(size_t Items);
|
||||
void Reset();
|
||||
void SoftReset();
|
||||
void operator = (Array<T> &Src);
|
||||
void Push(T Item);
|
||||
void Append(T *Item,size_t Count);
|
||||
T* Addr(size_t Item) {return Buffer+Item;}
|
||||
void SetMaxSize(size_t Size) {MaxSize=Size;}
|
||||
};
|
||||
|
||||
template <class T> void Array<T>::CleanData()
|
||||
{
|
||||
Buffer=NULL;
|
||||
BufSize=0;
|
||||
AllocSize=0;
|
||||
MaxSize=0;
|
||||
}
|
||||
|
||||
|
||||
template <class T> Array<T>::Array()
|
||||
{
|
||||
CleanData();
|
||||
}
|
||||
|
||||
|
||||
template <class T> Array<T>::Array(size_t Size)
|
||||
{
|
||||
CleanData();
|
||||
Add(Size);
|
||||
}
|
||||
|
||||
|
||||
// Copy constructor in case we need to pass an object as value.
|
||||
template <class T> Array<T>::Array(const Array &Src)
|
||||
{
|
||||
CleanData();
|
||||
Alloc(Src.BufSize);
|
||||
if (Src.BufSize!=0)
|
||||
memcpy((void *)Buffer,(void *)Src.Buffer,Src.BufSize*sizeof(T));
|
||||
}
|
||||
|
||||
|
||||
template <class T> Array<T>::~Array()
|
||||
{
|
||||
if (Buffer!=NULL)
|
||||
rarfree(Buffer);
|
||||
}
|
||||
|
||||
|
||||
template <class T> inline T& Array<T>::operator [](size_t Item) const
|
||||
{
|
||||
return Buffer[Item];
|
||||
}
|
||||
|
||||
|
||||
template <class T> inline T* Array<T>::operator +(size_t Pos)
|
||||
{
|
||||
return Buffer+Pos;
|
||||
}
|
||||
|
||||
|
||||
template <class T> inline size_t Array<T>::Size()
|
||||
{
|
||||
return BufSize;
|
||||
}
|
||||
|
||||
|
||||
template <class T> void Array<T>::Add(size_t Items)
|
||||
{
|
||||
size_t NewBufSize=BufSize+Items;
|
||||
if (NewBufSize>AllocSize)
|
||||
{
|
||||
if (MaxSize!=0 && NewBufSize>MaxSize)
|
||||
throw std::bad_alloc();
|
||||
|
||||
size_t Suggested=AllocSize+AllocSize/4+32;
|
||||
size_t NewSize=Max(NewBufSize,Suggested);
|
||||
|
||||
T *NewBuffer=(T *)rarrealloc(Buffer,NewSize*sizeof(T));
|
||||
if (NewBuffer==NULL)
|
||||
throw std::bad_alloc();
|
||||
Buffer=NewBuffer;
|
||||
AllocSize=NewSize;
|
||||
}
|
||||
BufSize=NewBufSize;
|
||||
}
|
||||
|
||||
|
||||
template <class T> void Array<T>::Alloc(size_t Items)
|
||||
{
|
||||
if (Items>AllocSize)
|
||||
Add(Items-BufSize);
|
||||
else
|
||||
BufSize=Items;
|
||||
}
|
||||
|
||||
|
||||
template <class T> void Array<T>::Reset()
|
||||
{
|
||||
// Keep memory allocated if it's small
|
||||
// Eliminates constant reallocation when scanning archive
|
||||
if ( AllocSize < 1024/sizeof(T) )
|
||||
{
|
||||
BufSize = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Buffer!=NULL)
|
||||
{
|
||||
rarfree(Buffer);
|
||||
Buffer=NULL;
|
||||
}
|
||||
BufSize=0;
|
||||
AllocSize=0;
|
||||
}
|
||||
|
||||
|
||||
// Reset buffer size, but preserve already allocated memory if any,
|
||||
// so we can reuse it without wasting time to allocation.
|
||||
template <class T> void Array<T>::SoftReset()
|
||||
{
|
||||
BufSize=0;
|
||||
}
|
||||
|
||||
|
||||
template <class T> void Array<T>::operator =(Array<T> &Src)
|
||||
{
|
||||
Reset();
|
||||
Alloc(Src.BufSize);
|
||||
if (Src.BufSize!=0)
|
||||
memcpy((void *)Buffer,(void *)Src.Buffer,Src.BufSize*sizeof(T));
|
||||
}
|
||||
|
||||
|
||||
template <class T> void Array<T>::Push(T Item)
|
||||
{
|
||||
Add(1);
|
||||
(*this)[Size()-1]=Item;
|
||||
}
|
||||
|
||||
|
||||
template <class T> void Array<T>::Append(T *Items,size_t Count)
|
||||
{
|
||||
size_t CurSize=Size();
|
||||
Add(Count);
|
||||
memcpy(Buffer+CurSize,Items,Count*sizeof(T));
|
||||
}
|
||||
|
||||
#endif
|
189
src/core/fex/unrar/blake2s.cpp
Normal file
189
src/core/fex/unrar/blake2s.cpp
Normal file
@@ -0,0 +1,189 @@
|
||||
// Based on public domain code written in 2012 by Samuel Neves
|
||||
|
||||
#include "rar.hpp"
|
||||
|
||||
#ifdef USE_SSE
|
||||
#include "blake2s_sse.cpp"
|
||||
#endif
|
||||
|
||||
static void blake2s_init_param( blake2s_state *S, uint32 node_offset, uint32 node_depth);
|
||||
static void blake2s_update( blake2s_state *S, const byte *in, size_t inlen );
|
||||
static void blake2s_final( blake2s_state *S, byte *digest );
|
||||
|
||||
#include "blake2sp.cpp"
|
||||
|
||||
static const uint32 blake2s_IV[8] =
|
||||
{
|
||||
0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL,
|
||||
0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL
|
||||
};
|
||||
|
||||
static const byte blake2s_sigma[10][16] =
|
||||
{
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } ,
|
||||
{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } ,
|
||||
{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } ,
|
||||
{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } ,
|
||||
{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } ,
|
||||
{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } ,
|
||||
{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } ,
|
||||
{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } ,
|
||||
{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } ,
|
||||
{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } ,
|
||||
};
|
||||
|
||||
static inline void blake2s_set_lastnode( blake2s_state *S )
|
||||
{
|
||||
S->f[1] = ~0U;
|
||||
}
|
||||
|
||||
|
||||
/* Some helper functions, not necessarily useful */
|
||||
static inline void blake2s_set_lastblock( blake2s_state *S )
|
||||
{
|
||||
if( S->last_node ) blake2s_set_lastnode( S );
|
||||
|
||||
S->f[0] = ~0U;
|
||||
}
|
||||
|
||||
|
||||
static inline void blake2s_increment_counter( blake2s_state *S, const uint32 inc )
|
||||
{
|
||||
S->t[0] += inc;
|
||||
S->t[1] += ( S->t[0] < inc );
|
||||
}
|
||||
|
||||
|
||||
/* init2 xors IV with input parameter block */
|
||||
void blake2s_init_param( blake2s_state *S, uint32 node_offset, uint32 node_depth)
|
||||
{
|
||||
#ifdef USE_SSE
|
||||
if (_SSE_Version>=SSE_SSE2)
|
||||
blake2s_init_sse();
|
||||
#endif
|
||||
|
||||
S->init(); // Clean data.
|
||||
for( int i = 0; i < 8; ++i )
|
||||
S->h[i] = blake2s_IV[i];
|
||||
|
||||
S->h[0] ^= 0x02080020; // We use BLAKE2sp parameters block.
|
||||
S->h[2] ^= node_offset;
|
||||
S->h[3] ^= (node_depth<<16)|0x20000000;
|
||||
}
|
||||
|
||||
|
||||
static inline uint32 rotr32( const uint32 w, const unsigned c )
|
||||
{
|
||||
return ( w >> c ) | ( w << ( 32 - c ) );
|
||||
}
|
||||
|
||||
|
||||
#define G(r,i,m,a,b,c,d) \
|
||||
a = a + b + m[blake2s_sigma[r][2*i+0]]; \
|
||||
d = rotr32(d ^ a, 16); \
|
||||
c = c + d; \
|
||||
b = rotr32(b ^ c, 12); \
|
||||
a = a + b + m[blake2s_sigma[r][2*i+1]]; \
|
||||
d = rotr32(d ^ a, 8); \
|
||||
c = c + d; \
|
||||
b = rotr32(b ^ c, 7);
|
||||
|
||||
|
||||
static void blake2s_compress( blake2s_state *S, const byte block[BLAKE2S_BLOCKBYTES] )
|
||||
{
|
||||
uint32 m[16];
|
||||
uint32 v[16];
|
||||
|
||||
for( size_t i = 0; i < 16; ++i )
|
||||
m[i] = RawGet4( block + i * 4 );
|
||||
|
||||
for( size_t i = 0; i < 8; ++i )
|
||||
v[i] = S->h[i];
|
||||
|
||||
v[ 8] = blake2s_IV[0];
|
||||
v[ 9] = blake2s_IV[1];
|
||||
v[10] = blake2s_IV[2];
|
||||
v[11] = blake2s_IV[3];
|
||||
v[12] = S->t[0] ^ blake2s_IV[4];
|
||||
v[13] = S->t[1] ^ blake2s_IV[5];
|
||||
v[14] = S->f[0] ^ blake2s_IV[6];
|
||||
v[15] = S->f[1] ^ blake2s_IV[7];
|
||||
|
||||
for ( uint r = 0; r <= 9; ++r ) // No gain on i7 if unrolled, but exe size grows.
|
||||
{
|
||||
G(r,0,m,v[ 0],v[ 4],v[ 8],v[12]);
|
||||
G(r,1,m,v[ 1],v[ 5],v[ 9],v[13]);
|
||||
G(r,2,m,v[ 2],v[ 6],v[10],v[14]);
|
||||
G(r,3,m,v[ 3],v[ 7],v[11],v[15]);
|
||||
G(r,4,m,v[ 0],v[ 5],v[10],v[15]);
|
||||
G(r,5,m,v[ 1],v[ 6],v[11],v[12]);
|
||||
G(r,6,m,v[ 2],v[ 7],v[ 8],v[13]);
|
||||
G(r,7,m,v[ 3],v[ 4],v[ 9],v[14]);
|
||||
}
|
||||
|
||||
for( size_t i = 0; i < 8; ++i )
|
||||
S->h[i] = S->h[i] ^ v[i] ^ v[i + 8];
|
||||
}
|
||||
|
||||
|
||||
void blake2s_update( blake2s_state *S, const byte *in, size_t inlen )
|
||||
{
|
||||
while( inlen > 0 )
|
||||
{
|
||||
size_t left = S->buflen;
|
||||
size_t fill = 2 * BLAKE2S_BLOCKBYTES - left;
|
||||
|
||||
if( inlen > fill )
|
||||
{
|
||||
memcpy( S->buf + left, in, fill ); // Fill buffer
|
||||
S->buflen += fill;
|
||||
blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES );
|
||||
|
||||
#ifdef USE_SSE
|
||||
#ifdef _WIN_32 // We use SSSE3 _mm_shuffle_epi8 only in x64 mode.
|
||||
if (_SSE_Version>=SSE_SSE2)
|
||||
#else
|
||||
if (_SSE_Version>=SSE_SSSE3)
|
||||
#endif
|
||||
blake2s_compress_sse( S, S->buf );
|
||||
else
|
||||
blake2s_compress( S, S->buf ); // Compress
|
||||
#else
|
||||
blake2s_compress( S, S->buf ); // Compress
|
||||
#endif
|
||||
|
||||
memcpy( S->buf, S->buf + BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES ); // Shift buffer left
|
||||
S->buflen -= BLAKE2S_BLOCKBYTES;
|
||||
in += fill;
|
||||
inlen -= fill;
|
||||
}
|
||||
else // inlen <= fill
|
||||
{
|
||||
memcpy( S->buf + left, in, (size_t)inlen );
|
||||
S->buflen += (size_t)inlen; // Be lazy, do not compress
|
||||
in += inlen;
|
||||
inlen -= inlen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void blake2s_final( blake2s_state *S, byte *digest )
|
||||
{
|
||||
if( S->buflen > BLAKE2S_BLOCKBYTES )
|
||||
{
|
||||
blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES );
|
||||
blake2s_compress( S, S->buf );
|
||||
S->buflen -= BLAKE2S_BLOCKBYTES;
|
||||
memcpy( S->buf, S->buf + BLAKE2S_BLOCKBYTES, S->buflen );
|
||||
}
|
||||
|
||||
blake2s_increment_counter( S, ( uint32 )S->buflen );
|
||||
blake2s_set_lastblock( S );
|
||||
memset( S->buf + S->buflen, 0, 2 * BLAKE2S_BLOCKBYTES - S->buflen ); /* Padding */
|
||||
blake2s_compress( S, S->buf );
|
||||
|
||||
for( int i = 0; i < 8; ++i ) /* Output full hash */
|
||||
RawPut4( S->h[i], digest + 4 * i );
|
||||
}
|
||||
|
101
src/core/fex/unrar/blake2s.hpp
Normal file
101
src/core/fex/unrar/blake2s.hpp
Normal file
@@ -0,0 +1,101 @@
|
||||
// Based on public domain code written in 2012 by Samuel Neves
|
||||
#ifndef _RAR_BLAKE2_
|
||||
#define _RAR_BLAKE2_
|
||||
|
||||
#define BLAKE2_DIGEST_SIZE 32
|
||||
|
||||
enum blake2s_constant
|
||||
{
|
||||
BLAKE2S_BLOCKBYTES = 64,
|
||||
BLAKE2S_OUTBYTES = 32
|
||||
};
|
||||
|
||||
|
||||
// Alignment to 64 improves performance of both SSE and non-SSE versions.
|
||||
// Alignment to n*16 is required for SSE version, so we selected 64.
|
||||
// We use the custom alignment scheme instead of __declspec(align(x)),
|
||||
// because it is less compiler dependent. Also the compiler directive
|
||||
// does not help if structure is a member of class allocated through
|
||||
// 'new' operator.
|
||||
struct blake2s_state
|
||||
{
|
||||
enum { BLAKE_ALIGNMENT = 64 };
|
||||
|
||||
// buffer and uint32 h[8], t[2], f[2];
|
||||
enum { BLAKE_DATA_SIZE = 48 + 2 * BLAKE2S_BLOCKBYTES };
|
||||
|
||||
byte ubuf[BLAKE_DATA_SIZE + BLAKE_ALIGNMENT];
|
||||
|
||||
byte *buf; // byte buf[2 * BLAKE2S_BLOCKBYTES].
|
||||
uint32 *h, *t, *f; // uint32 h[8], t[2], f[2].
|
||||
|
||||
size_t buflen;
|
||||
byte last_node;
|
||||
|
||||
blake2s_state()
|
||||
{
|
||||
set_pointers();
|
||||
}
|
||||
|
||||
// Required when we declare and assign in the same command.
|
||||
blake2s_state(blake2s_state &st)
|
||||
{
|
||||
set_pointers();
|
||||
*this=st;
|
||||
}
|
||||
|
||||
void set_pointers()
|
||||
{
|
||||
// Set aligned pointers. Must be done in constructor, not in Init(),
|
||||
// so assignments like 'blake2sp_state res=blake2ctx' work correctly
|
||||
// even if blake2sp_init is not called for 'res'.
|
||||
buf = (byte *) ALIGN_VALUE(ubuf, BLAKE_ALIGNMENT);
|
||||
h = (uint32 *) (buf + 2 * BLAKE2S_BLOCKBYTES);
|
||||
t = h + 8;
|
||||
f = t + 2;
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
memset( ubuf, 0, sizeof( ubuf ) );
|
||||
buflen = 0;
|
||||
last_node = 0;
|
||||
}
|
||||
|
||||
// Since we use pointers, the default = would work incorrectly.
|
||||
blake2s_state& operator = (blake2s_state &st)
|
||||
{
|
||||
if (this != &st)
|
||||
{
|
||||
memcpy(buf, st.buf, BLAKE_DATA_SIZE);
|
||||
buflen = st.buflen;
|
||||
last_node = st.last_node;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#ifdef RAR_SMP
|
||||
class ThreadPool;
|
||||
#endif
|
||||
|
||||
struct blake2sp_state
|
||||
{
|
||||
blake2s_state S[8];
|
||||
blake2s_state R;
|
||||
byte buf[8 * BLAKE2S_BLOCKBYTES];
|
||||
size_t buflen;
|
||||
|
||||
#ifdef RAR_SMP
|
||||
ThreadPool *ThPool;
|
||||
uint MaxThreads;
|
||||
#endif
|
||||
};
|
||||
|
||||
void blake2sp_init( blake2sp_state *S );
|
||||
void blake2sp_update( blake2sp_state *S, const byte *in, size_t inlen );
|
||||
void blake2sp_final( blake2sp_state *S, byte *digest );
|
||||
|
||||
#endif
|
||||
|
131
src/core/fex/unrar/blake2s_sse.cpp
Normal file
131
src/core/fex/unrar/blake2s_sse.cpp
Normal file
@@ -0,0 +1,131 @@
|
||||
// Based on public domain code written in 2012 by Samuel Neves
|
||||
|
||||
#ifdef RAR_COMMON_HPP
|
||||
|
||||
extern const byte blake2s_sigma[10][16];
|
||||
|
||||
// Initialization vector.
|
||||
static __m128i blake2s_IV_0_3, blake2s_IV_4_7;
|
||||
|
||||
#ifdef _WIN_64
|
||||
// Constants for cyclic rotation. Used in 64-bit mode in mm_rotr_epi32 macro.
|
||||
static __m128i crotr8, crotr16;
|
||||
#endif
|
||||
|
||||
static void blake2s_init_sse()
|
||||
{
|
||||
// We cannot initialize these 128 bit variables in place when declaring
|
||||
// them globally, because global scope initialization is performed before
|
||||
// our SSE check and it would make code incompatible with older non-SSE2
|
||||
// CPUs. Also we cannot initialize them as static inside of function
|
||||
// using these variables, because SSE static initialization is not thread
|
||||
// safe: first thread starts initialization and sets "init done" flag even
|
||||
// if it is not done yet, second thread can attempt to access half-init
|
||||
// SSE data. So we moved init code here.
|
||||
|
||||
blake2s_IV_0_3 = _mm_setr_epi32( 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A );
|
||||
blake2s_IV_4_7 = _mm_setr_epi32( 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 );
|
||||
|
||||
#ifdef _WIN_64
|
||||
crotr8 = _mm_set_epi8( 12, 15, 14, 13, 8, 11, 10, 9, 4, 7, 6, 5, 0, 3, 2, 1 );
|
||||
crotr16 = _mm_set_epi8( 13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6, 1, 0, 3, 2 );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#define LOAD(p) _mm_load_si128( (__m128i *)(p) )
|
||||
#define STORE(p,r) _mm_store_si128((__m128i *)(p), r)
|
||||
|
||||
#ifdef _WIN_32
|
||||
// 32-bit mode has less SSE2 registers and in MSVC2008 it is more efficient
|
||||
// to not use _mm_shuffle_epi8 here.
|
||||
#define mm_rotr_epi32(r, c) ( \
|
||||
_mm_xor_si128(_mm_srli_epi32( (r), c ),_mm_slli_epi32( (r), 32-c )) )
|
||||
#else
|
||||
#define mm_rotr_epi32(r, c) ( \
|
||||
c==8 ? _mm_shuffle_epi8(r,crotr8) \
|
||||
: c==16 ? _mm_shuffle_epi8(r,crotr16) \
|
||||
: _mm_xor_si128(_mm_srli_epi32( (r), c ),_mm_slli_epi32( (r), 32-c )) )
|
||||
#endif
|
||||
|
||||
|
||||
#define G1(row1,row2,row3,row4,buf) \
|
||||
row1 = _mm_add_epi32( _mm_add_epi32( row1, buf), row2 ); \
|
||||
row4 = _mm_xor_si128( row4, row1 ); \
|
||||
row4 = mm_rotr_epi32(row4, 16); \
|
||||
row3 = _mm_add_epi32( row3, row4 ); \
|
||||
row2 = _mm_xor_si128( row2, row3 ); \
|
||||
row2 = mm_rotr_epi32(row2, 12);
|
||||
|
||||
#define G2(row1,row2,row3,row4,buf) \
|
||||
row1 = _mm_add_epi32( _mm_add_epi32( row1, buf), row2 ); \
|
||||
row4 = _mm_xor_si128( row4, row1 ); \
|
||||
row4 = mm_rotr_epi32(row4, 8); \
|
||||
row3 = _mm_add_epi32( row3, row4 ); \
|
||||
row2 = _mm_xor_si128( row2, row3 ); \
|
||||
row2 = mm_rotr_epi32(row2, 7);
|
||||
|
||||
#define DIAGONALIZE(row1,row2,row3,row4) \
|
||||
row4 = _mm_shuffle_epi32( row4, _MM_SHUFFLE(2,1,0,3) ); \
|
||||
row3 = _mm_shuffle_epi32( row3, _MM_SHUFFLE(1,0,3,2) ); \
|
||||
row2 = _mm_shuffle_epi32( row2, _MM_SHUFFLE(0,3,2,1) );
|
||||
|
||||
#define UNDIAGONALIZE(row1,row2,row3,row4) \
|
||||
row4 = _mm_shuffle_epi32( row4, _MM_SHUFFLE(0,3,2,1) ); \
|
||||
row3 = _mm_shuffle_epi32( row3, _MM_SHUFFLE(1,0,3,2) ); \
|
||||
row2 = _mm_shuffle_epi32( row2, _MM_SHUFFLE(2,1,0,3) );
|
||||
|
||||
#ifdef _WIN_64
|
||||
// MSVC 2008 in x64 mode expands _mm_set_epi32 to store to stack and load
|
||||
// from stack operations, which are slower than this code.
|
||||
#define _mm_set_epi32(i3,i2,i1,i0) \
|
||||
_mm_unpacklo_epi32(_mm_unpacklo_epi32(_mm_cvtsi32_si128(i0),_mm_cvtsi32_si128(i2)), \
|
||||
_mm_unpacklo_epi32(_mm_cvtsi32_si128(i1),_mm_cvtsi32_si128(i3)))
|
||||
#endif
|
||||
|
||||
// Original BLAKE2 SSE4.1 message loading code was a little slower in x86 mode
|
||||
// and about the same in x64 mode in our test. Perhaps depends on compiler.
|
||||
#define SSE_ROUND(m,row,r) \
|
||||
{ \
|
||||
__m128i buf; \
|
||||
buf=_mm_set_epi32(m[blake2s_sigma[r][6]],m[blake2s_sigma[r][4]],m[blake2s_sigma[r][2]],m[blake2s_sigma[r][0]]); \
|
||||
G1(row[0],row[1],row[2],row[3],buf); \
|
||||
buf=_mm_set_epi32(m[blake2s_sigma[r][7]],m[blake2s_sigma[r][5]],m[blake2s_sigma[r][3]],m[blake2s_sigma[r][1]]); \
|
||||
G2(row[0],row[1],row[2],row[3],buf); \
|
||||
DIAGONALIZE(row[0],row[1],row[2],row[3]); \
|
||||
buf=_mm_set_epi32(m[blake2s_sigma[r][14]],m[blake2s_sigma[r][12]],m[blake2s_sigma[r][10]],m[blake2s_sigma[r][8]]); \
|
||||
G1(row[0],row[1],row[2],row[3],buf); \
|
||||
buf=_mm_set_epi32(m[blake2s_sigma[r][15]],m[blake2s_sigma[r][13]],m[blake2s_sigma[r][11]],m[blake2s_sigma[r][9]]); \
|
||||
G2(row[0],row[1],row[2],row[3],buf); \
|
||||
UNDIAGONALIZE(row[0],row[1],row[2],row[3]); \
|
||||
}
|
||||
|
||||
|
||||
static int blake2s_compress_sse( blake2s_state *S, const byte block[BLAKE2S_BLOCKBYTES] )
|
||||
{
|
||||
__m128i row[4];
|
||||
__m128i ff0, ff1;
|
||||
|
||||
const uint32 *m = ( uint32 * )block;
|
||||
|
||||
row[0] = ff0 = LOAD( &S->h[0] );
|
||||
row[1] = ff1 = LOAD( &S->h[4] );
|
||||
|
||||
row[2] = blake2s_IV_0_3;
|
||||
row[3] = _mm_xor_si128( blake2s_IV_4_7, LOAD( &S->t[0] ) );
|
||||
SSE_ROUND( m, row, 0 );
|
||||
SSE_ROUND( m, row, 1 );
|
||||
SSE_ROUND( m, row, 2 );
|
||||
SSE_ROUND( m, row, 3 );
|
||||
SSE_ROUND( m, row, 4 );
|
||||
SSE_ROUND( m, row, 5 );
|
||||
SSE_ROUND( m, row, 6 );
|
||||
SSE_ROUND( m, row, 7 );
|
||||
SSE_ROUND( m, row, 8 );
|
||||
SSE_ROUND( m, row, 9 );
|
||||
STORE( &S->h[0], _mm_xor_si128( ff0, _mm_xor_si128( row[0], row[2] ) ) );
|
||||
STORE( &S->h[4], _mm_xor_si128( ff1, _mm_xor_si128( row[1], row[3] ) ) );
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
157
src/core/fex/unrar/blake2sp.cpp
Normal file
157
src/core/fex/unrar/blake2sp.cpp
Normal file
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
BLAKE2 reference source code package - reference C implementations
|
||||
|
||||
Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
|
||||
|
||||
To the extent possible under law, the author(s) have dedicated all copyright
|
||||
and related and neighboring rights to this software to the public domain
|
||||
worldwide. This software is distributed without any warranty.
|
||||
|
||||
You should have received a copy of the CC0 Public Domain Dedication along with
|
||||
this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
*/
|
||||
|
||||
#ifdef RAR_COMMON_HPP
|
||||
|
||||
#define PARALLELISM_DEGREE 8
|
||||
|
||||
void blake2sp_init( blake2sp_state *S )
|
||||
{
|
||||
memset( S->buf, 0, sizeof( S->buf ) );
|
||||
S->buflen = 0;
|
||||
|
||||
blake2s_init_param( &S->R, 0, 1 ); // Init root.
|
||||
|
||||
for( uint i = 0; i < PARALLELISM_DEGREE; ++i )
|
||||
blake2s_init_param( &S->S[i], i, 0 ); // Init leaf.
|
||||
|
||||
S->R.last_node = 1;
|
||||
S->S[PARALLELISM_DEGREE - 1].last_node = 1;
|
||||
}
|
||||
|
||||
|
||||
struct Blake2ThreadData
|
||||
{
|
||||
void Update();
|
||||
blake2s_state *S;
|
||||
const byte *in;
|
||||
size_t inlen;
|
||||
};
|
||||
|
||||
|
||||
void Blake2ThreadData::Update()
|
||||
{
|
||||
size_t inlen__ = inlen;
|
||||
const byte *in__ = ( const byte * )in;
|
||||
|
||||
while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES )
|
||||
{
|
||||
#ifdef USE_SSE
|
||||
// We gain 5% in i7 SSE mode by prefetching next data block.
|
||||
if (_SSE_Version>=SSE_SSE && inlen__ >= 2 * PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES)
|
||||
_mm_prefetch((char*)(in__ + PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES), _MM_HINT_T0);
|
||||
#endif
|
||||
blake2s_update( S, in__, BLAKE2S_BLOCKBYTES );
|
||||
in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
|
||||
inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef RAR_SMP
|
||||
THREAD_PROC(Blake2Thread)
|
||||
{
|
||||
Blake2ThreadData *td=(Blake2ThreadData *)Data;
|
||||
td->Update();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void blake2sp_update( blake2sp_state *S, const byte *in, size_t inlen )
|
||||
{
|
||||
size_t left = S->buflen;
|
||||
size_t fill = sizeof( S->buf ) - left;
|
||||
|
||||
if( left && inlen >= fill )
|
||||
{
|
||||
memcpy( S->buf + left, in, fill );
|
||||
|
||||
for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
|
||||
blake2s_update( &S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES );
|
||||
|
||||
in += fill;
|
||||
inlen -= fill;
|
||||
left = 0;
|
||||
}
|
||||
|
||||
Blake2ThreadData btd_array[PARALLELISM_DEGREE];
|
||||
|
||||
#ifdef RAR_SMP
|
||||
uint ThreadNumber = inlen < 0x1000 ? 1 : S->MaxThreads;
|
||||
|
||||
if (ThreadNumber==6 || ThreadNumber==7) // 6 and 7 threads work slower than 4 here.
|
||||
ThreadNumber=4;
|
||||
#else
|
||||
uint ThreadNumber=1;
|
||||
#endif
|
||||
|
||||
for (size_t id__=0;id__<PARALLELISM_DEGREE;)
|
||||
{
|
||||
for (uint Thread=0;Thread<ThreadNumber && id__<PARALLELISM_DEGREE;Thread++)
|
||||
{
|
||||
Blake2ThreadData *btd=btd_array+Thread;
|
||||
|
||||
btd->inlen = inlen;
|
||||
btd->in = in + id__ * BLAKE2S_BLOCKBYTES;
|
||||
btd->S = &S->S[id__];
|
||||
|
||||
#ifdef RAR_SMP
|
||||
if (ThreadNumber>1)
|
||||
S->ThPool->AddTask(Blake2Thread,(void*)btd);
|
||||
else
|
||||
btd->Update();
|
||||
#else
|
||||
btd->Update();
|
||||
#endif
|
||||
id__++;
|
||||
}
|
||||
#ifdef RAR_SMP
|
||||
if (S->ThPool!=NULL) // Can be NULL in -mt1 mode.
|
||||
S->ThPool->WaitDone();
|
||||
#endif // RAR_SMP
|
||||
}
|
||||
|
||||
in += inlen - inlen % ( PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES );
|
||||
inlen %= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
|
||||
|
||||
if( inlen > 0 )
|
||||
memcpy( S->buf + left, in, (size_t)inlen );
|
||||
|
||||
S->buflen = left + (size_t)inlen;
|
||||
}
|
||||
|
||||
|
||||
void blake2sp_final( blake2sp_state *S, byte *digest )
|
||||
{
|
||||
byte hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES];
|
||||
|
||||
for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
|
||||
{
|
||||
if( S->buflen > i * BLAKE2S_BLOCKBYTES )
|
||||
{
|
||||
size_t left = S->buflen - i * BLAKE2S_BLOCKBYTES;
|
||||
|
||||
if( left > BLAKE2S_BLOCKBYTES ) left = BLAKE2S_BLOCKBYTES;
|
||||
|
||||
blake2s_update( &S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, left );
|
||||
}
|
||||
|
||||
blake2s_final( &S->S[i], hash[i] );
|
||||
}
|
||||
|
||||
for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
|
||||
blake2s_update( &S->R, hash[i], BLAKE2S_OUTBYTES );
|
||||
|
||||
blake2s_final( &S->R, digest );
|
||||
}
|
||||
|
||||
#endif
|
141
src/core/fex/unrar/changes.txt
Normal file
141
src/core/fex/unrar/changes.txt
Normal file
@@ -0,0 +1,141 @@
|
||||
unrar_core source code changes
|
||||
------------------------------
|
||||
Unrar_core is based on UnRAR (unrarsrc-3.8.5.tar.gz) by Alexander L.
|
||||
Roshal. The original sources have been HEAVILY modified, trimmed down,
|
||||
and purged of all OS-specific calls for file access and other
|
||||
unnecessary operations. Support for encryption, recovery records, and
|
||||
segmentation has been REMOVED. See license.txt for licensing. In
|
||||
particular, this code cannot be used to re-create the RAR compression
|
||||
algorithm, which is proprietary.
|
||||
|
||||
If you obtained this code as a part of my File_Extractor library and
|
||||
want to use it on its own, get my unrar_core library, which includes
|
||||
examples and documentation.
|
||||
|
||||
The source is as close as possible to the original, to make it simple to
|
||||
update when a new version of UnRAR comes out. In many places the
|
||||
original names and object nesting are kept, even though it's a bit
|
||||
harder to follow. See rar.hpp for the main "glue".
|
||||
|
||||
Website: http://www.slack.net/~ant/
|
||||
E-mail : Shay Green <gblargg@gmail.com>
|
||||
|
||||
|
||||
Contents
|
||||
--------
|
||||
* Diff-friendly changes
|
||||
* Removal of features
|
||||
* Error reporting changes
|
||||
* Minor tweaks
|
||||
* Unrar findings
|
||||
|
||||
|
||||
Diff-friendly changes
|
||||
---------------------
|
||||
To make my source code changes more easily visible with a line-based
|
||||
file diff, I've tried to make changes by inserting or deleting lines,
|
||||
rather than modifying them. So if the original declared a static array
|
||||
|
||||
static int array [4] = { 1, 2, 3, 4 };
|
||||
|
||||
and I want to make it const, I add the const on a line before
|
||||
|
||||
const // added
|
||||
static int array [4] = { 1, 2, 3, 4 };
|
||||
|
||||
rather than on the same line
|
||||
|
||||
static const int array [4] = { 1, 2, 3, 4 };
|
||||
|
||||
This way a diff will simply show an added line, making it clear what was
|
||||
added. If I simply inserted const on the same line, it wouldn't be as
|
||||
clear what all I had changed.
|
||||
|
||||
I've also made use of several macros rather than changing the source
|
||||
text. For example, since a class name like Unpack might easily conflict,
|
||||
I've renamed it to Rar_Unpack by using #define Unpack Rar_Unpack rather
|
||||
than changing the source text. These macros are only defined when
|
||||
compiling the library sources; the user-visible unrar.h is very clean.
|
||||
|
||||
|
||||
Removal of features
|
||||
-------------------
|
||||
This library is meant for simple access to common archives without
|
||||
having to extract them first. Encryption, segmentation, huge files, and
|
||||
self-extracting archives aren't common for things that need to be
|
||||
accessed in this manner, so I've removed support for them. Also,
|
||||
encryption adds complexity to the code that must be maintained.
|
||||
Segmentation would require a way to specify the other segments.
|
||||
|
||||
|
||||
Error reporting changes
|
||||
-----------------------
|
||||
The original used C++ exceptions to report errors. I've eliminated use
|
||||
of these through a combination of error codes and longjmp. This allows
|
||||
use of the library from C or some other language which doesn't easily
|
||||
support exceptions.
|
||||
|
||||
I tried to make as few changes as possible in the conversion. Due to the
|
||||
number of places file reads occur, propagating an error code via return
|
||||
statements would have required too many code changes. Instead, I perform
|
||||
the read, save the error code, and return 0 bytes read in case of an
|
||||
error. I also ensure that the calling code interprets this zero in an
|
||||
acceptable way. I then check this saved error code after the operation
|
||||
completes, and have it take priority over the error the RAR code
|
||||
returned. I do a similar thing for write errors.
|
||||
|
||||
|
||||
Minor tweaks
|
||||
------------
|
||||
- Eliminated as many GCC warnings as reasonably possible.
|
||||
|
||||
- Non-class array allocations now use malloc(), allowing the code to be
|
||||
linked without the standard C++ library (particularly, operator new).
|
||||
Class object allocations use a class-specific allocator that just calls
|
||||
malloc(), also avoiding calls to operator new.
|
||||
|
||||
- Made all unchanging static data const. Several pieces of static data
|
||||
in the original code weren't marked const where they could be.
|
||||
|
||||
- Initialization of some static tables was done on an as-needed basis,
|
||||
creating a problem when extracting from archives in multiple threads.
|
||||
This initialization can now be done by the user before any archives are
|
||||
opened.
|
||||
|
||||
- Tweaked CopyString, the major bottleneck during compression. I inlined
|
||||
it, cached some variables in locals in case the compiler couldn't easily
|
||||
see that the memory accesses don't modify them, and made them use
|
||||
memcpy() where possible. This improved performance by at least 20% on
|
||||
x86. Perhaps it won't work as well on files with lots of smaller string
|
||||
matches.
|
||||
|
||||
- Some .cpp files are #included by others. I've added guards to these so
|
||||
that you can simply compile all .cpp files and not get any redefinition
|
||||
errors.
|
||||
|
||||
- The current solid extraction position is kept track of, allowing the
|
||||
user to randomly extract files without regard to proper extraction
|
||||
order. The library optimizes solid extraction and only restarts it if
|
||||
the user is extracting a file earlier in the archive than the last
|
||||
solid-extracted one.
|
||||
|
||||
- Most of the time a solid file's data is already contiguously in the
|
||||
internal Unpack::Window, which unrar_extract_mem() takes advantage of.
|
||||
This avoids extra allocation in many cases.
|
||||
|
||||
- Allocation of Unpack is delayed until the first extraction, rather
|
||||
than being allocated immediately on opening the archive. This allows
|
||||
scanning with minimal memory usage.
|
||||
|
||||
|
||||
Unrar findings
|
||||
--------------
|
||||
- Apparently the LHD_SOLID flag indicates that file depends on previous
|
||||
files, rather than that later files depend on the current file's
|
||||
contents. Thus this flag can't be used to intelligently decide which
|
||||
files need to be internally extracted when skipping them, making it
|
||||
necessary to internally extract every file before the one to be
|
||||
extracted, if the archive is solid.
|
||||
|
||||
--
|
||||
Shay Green <gblargg@gmail.com>
|
50
src/core/fex/unrar/coder.cpp
Normal file
50
src/core/fex/unrar/coder.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
// #included by unpack.cpp
|
||||
#ifdef RAR_COMMON_HPP
|
||||
|
||||
inline unsigned int RangeCoder::GetChar()
|
||||
{
|
||||
return(UnpackRead->GetChar());
|
||||
}
|
||||
|
||||
|
||||
void RangeCoder::InitDecoder(Unpack *UnpackRead)
|
||||
{
|
||||
RangeCoder::UnpackRead=UnpackRead;
|
||||
|
||||
low=code=0;
|
||||
range=uint(-1);
|
||||
for (int i=0;i < 4;i++)
|
||||
code=(code << 8) | GetChar();
|
||||
}
|
||||
|
||||
|
||||
// (int) cast before "low" added only to suppress compiler warnings.
|
||||
#define ARI_DEC_NORMALIZE(code,low,range,read) \
|
||||
{ \
|
||||
while ((low^(low+range))<TOP || range<BOT && ((range=-(int)low&(BOT-1)),1)) \
|
||||
{ \
|
||||
code=(code << 8) | read->GetChar(); \
|
||||
range <<= 8; \
|
||||
low <<= 8; \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
inline int RangeCoder::GetCurrentCount()
|
||||
{
|
||||
return (code-low)/(range /= SubRange.scale);
|
||||
}
|
||||
|
||||
|
||||
inline uint RangeCoder::GetCurrentShiftCount(uint SHIFT)
|
||||
{
|
||||
return (code-low)/(range >>= SHIFT);
|
||||
}
|
||||
|
||||
|
||||
inline void RangeCoder::Decode()
|
||||
{
|
||||
low += range*SubRange.LowCount;
|
||||
range *= SubRange.HighCount-SubRange.LowCount;
|
||||
}
|
||||
#endif
|
23
src/core/fex/unrar/coder.hpp
Normal file
23
src/core/fex/unrar/coder.hpp
Normal file
@@ -0,0 +1,23 @@
|
||||
/****************************************************************************
|
||||
* Contents: 'Carryless rangecoder' by Dmitry Subbotin *
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
class RangeCoder
|
||||
{
|
||||
public:
|
||||
void InitDecoder(Unpack *UnpackRead);
|
||||
inline int GetCurrentCount();
|
||||
inline uint GetCurrentShiftCount(uint SHIFT);
|
||||
inline void Decode();
|
||||
inline void PutChar(unsigned int c);
|
||||
inline unsigned int GetChar();
|
||||
|
||||
uint low, code, range;
|
||||
struct SUBRANGE
|
||||
{
|
||||
uint LowCount, HighCount, scale;
|
||||
} SubRange;
|
||||
|
||||
Unpack *UnpackRead;
|
||||
};
|
50
src/core/fex/unrar/compress.hpp
Normal file
50
src/core/fex/unrar/compress.hpp
Normal file
@@ -0,0 +1,50 @@
|
||||
#ifndef _RAR_COMPRESS_
|
||||
#define _RAR_COMPRESS_
|
||||
|
||||
// Combine pack and unpack constants to class to avoid polluting global
|
||||
// namespace with numerous short names.
|
||||
class PackDef
|
||||
{
|
||||
public:
|
||||
static const uint MAX_LZ_MATCH = 0x1001;
|
||||
static const uint MAX3_LZ_MATCH = 0x101; // Maximum match length for RAR v3.
|
||||
static const uint LOW_DIST_REP_COUNT = 16;
|
||||
|
||||
static const uint NC = 306; /* alphabet = {0, 1, 2, ..., NC - 1} */
|
||||
static const uint DC = 64;
|
||||
static const uint LDC = 16;
|
||||
static const uint RC = 44;
|
||||
static const uint HUFF_TABLE_SIZE = NC + DC + RC + LDC;
|
||||
static const uint BC = 20;
|
||||
|
||||
static const uint NC30 = 299; /* alphabet = {0, 1, 2, ..., NC - 1} */
|
||||
static const uint DC30 = 60;
|
||||
static const uint LDC30 = 17;
|
||||
static const uint RC30 = 28;
|
||||
static const uint BC30 = 20;
|
||||
static const uint HUFF_TABLE_SIZE30 = NC30 + DC30 + RC30 + LDC30;
|
||||
|
||||
static const uint NC20 = 298; /* alphabet = {0, 1, 2, ..., NC - 1} */
|
||||
static const uint DC20 = 48;
|
||||
static const uint RC20 = 28;
|
||||
static const uint BC20 = 19;
|
||||
static const uint MC20 = 257;
|
||||
|
||||
// Largest alphabet size among all values listed above.
|
||||
static const uint LARGEST_TABLE_SIZE = 306;
|
||||
|
||||
enum {
|
||||
CODE_HUFFMAN, CODE_LZ, CODE_REPEATLZ, CODE_CACHELZ, CODE_STARTFILE,
|
||||
CODE_ENDFILE, CODE_FILTER, CODE_FILTERDATA
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
enum FilterType {
|
||||
// These values must not be changed, because we use them directly
|
||||
// in RAR5 compression and decompression code.
|
||||
FILTER_DELTA=0, FILTER_E8, FILTER_E8E9, FILTER_ARM,
|
||||
FILTER_AUDIO, FILTER_RGB, FILTER_ITANIUM, FILTER_PPM, FILTER_NONE
|
||||
};
|
||||
|
||||
#endif
|
97
src/core/fex/unrar/crc.cpp
Normal file
97
src/core/fex/unrar/crc.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
// This CRC function is based on Intel Slicing-by-8 algorithm.
|
||||
//
|
||||
// Original Intel Slicing-by-8 code is available here:
|
||||
//
|
||||
// http://sourceforge.net/projects/slicing-by-8/
|
||||
//
|
||||
// Original Intel Slicing-by-8 code is licensed as:
|
||||
//
|
||||
// Copyright (c) 2004-2006 Intel Corporation - All Rights Reserved
|
||||
//
|
||||
// This software program is licensed subject to the BSD License,
|
||||
// available at http://www.opensource.org/licenses/bsd-license.html
|
||||
|
||||
|
||||
#include "rar.hpp"
|
||||
|
||||
uint crc_tables[8][256]; // Tables for Slicing-by-8.
|
||||
|
||||
|
||||
// Build the classic CRC32 lookup table.
|
||||
// We also provide this function to legacy RAR and ZIP decryption code.
|
||||
void InitCRC32(uint *CRCTab)
|
||||
{
|
||||
if (CRCTab[1]!=0)
|
||||
return;
|
||||
for (uint I=0;I<256;I++)
|
||||
{
|
||||
uint C=I;
|
||||
for (uint J=0;J<8;J++)
|
||||
C=(C & 1) ? (C>>1)^0xEDB88320 : (C>>1);
|
||||
CRCTab[I]=C;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void InitCRCTables()
|
||||
{
|
||||
InitCRC32(crc_tables[0]);
|
||||
|
||||
for (uint I=0;I<256;I++) // Build additional lookup tables.
|
||||
{
|
||||
uint C=crc_tables[0][I];
|
||||
for (uint J=1;J<8;J++)
|
||||
{
|
||||
C=crc_tables[0][(byte)C]^(C>>8);
|
||||
crc_tables[J][I]=C;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint CRC32(uint StartCRC,const void *Addr,size_t Size)
|
||||
{
|
||||
byte *Data=(byte *)Addr;
|
||||
|
||||
// Align Data to 8 for better performance.
|
||||
for (;Size>0 && ((size_t)Data & 7);Size--,Data++)
|
||||
StartCRC=crc_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8);
|
||||
|
||||
for (;Size>=8;Size-=8,Data+=8)
|
||||
{
|
||||
#ifdef BIG_ENDIAN
|
||||
StartCRC ^= Data[0]|(Data[1] << 8)|(Data[2] << 16)|(Data[3] << 24);
|
||||
uint NextData = Data[4]|(Data[5] << 8)|(Data[6] << 16)|(Data[7] << 24);
|
||||
#else
|
||||
StartCRC ^= *(uint32 *) Data;
|
||||
uint NextData = *(uint32 *) (Data +4);
|
||||
#endif
|
||||
StartCRC = crc_tables[7][(byte) StartCRC ] ^
|
||||
crc_tables[6][(byte)(StartCRC >> 8) ] ^
|
||||
crc_tables[5][(byte)(StartCRC >> 16)] ^
|
||||
crc_tables[4][(byte)(StartCRC >> 24)] ^
|
||||
crc_tables[3][(byte) NextData ] ^
|
||||
crc_tables[2][(byte)(NextData >>8 ) ] ^
|
||||
crc_tables[1][(byte)(NextData >> 16)] ^
|
||||
crc_tables[0][(byte)(NextData >> 24)];
|
||||
}
|
||||
|
||||
for (;Size>0;Size--,Data++) // Process left data.
|
||||
StartCRC=crc_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8);
|
||||
|
||||
return StartCRC;
|
||||
}
|
||||
|
||||
|
||||
#ifndef SFX_MODULE
|
||||
// For RAR 1.4 archives in case somebody still has them.
|
||||
ushort Checksum14(ushort StartCRC,const void *Addr,size_t Size)
|
||||
{
|
||||
byte *Data=(byte *)Addr;
|
||||
for (size_t I=0;I<Size;I++)
|
||||
{
|
||||
StartCRC=(StartCRC+Data[I])&0xffff;
|
||||
StartCRC=((StartCRC<<1)|(StartCRC>>15))&0xffff;
|
||||
}
|
||||
return StartCRC;
|
||||
}
|
||||
#endif
|
57
src/core/fex/unrar/encname.cpp
Normal file
57
src/core/fex/unrar/encname.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
#include "rar.hpp"
|
||||
|
||||
EncodeFileName::EncodeFileName()
|
||||
{
|
||||
Flags=0;
|
||||
FlagBits=0;
|
||||
FlagsPos=0;
|
||||
DestSize=0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void EncodeFileName::Decode(char *Name,byte *EncName,size_t EncSize,wchar *NameW,
|
||||
size_t MaxDecSize)
|
||||
{
|
||||
size_t EncPos=0,DecPos=0;
|
||||
byte HighByte=EncName[EncPos++];
|
||||
while (EncPos<EncSize && DecPos<MaxDecSize)
|
||||
{
|
||||
if (FlagBits==0)
|
||||
{
|
||||
Flags=EncName[EncPos++];
|
||||
FlagBits=8;
|
||||
}
|
||||
switch(Flags>>6)
|
||||
{
|
||||
case 0:
|
||||
NameW[DecPos++]=EncName[EncPos++];
|
||||
break;
|
||||
case 1:
|
||||
NameW[DecPos++]=EncName[EncPos++]+(HighByte<<8);
|
||||
break;
|
||||
case 2:
|
||||
NameW[DecPos++]=EncName[EncPos]+(EncName[EncPos+1]<<8);
|
||||
EncPos+=2;
|
||||
break;
|
||||
case 3:
|
||||
{
|
||||
int Length=EncName[EncPos++];
|
||||
if (Length & 0x80)
|
||||
{
|
||||
byte Correction=EncName[EncPos++];
|
||||
for (Length=(Length&0x7f)+2;Length>0 && DecPos<MaxDecSize;Length--,DecPos++)
|
||||
NameW[DecPos]=((Name[DecPos]+Correction)&0xff)+(HighByte<<8);
|
||||
}
|
||||
else
|
||||
for (Length+=2;Length>0 && DecPos<MaxDecSize;Length--,DecPos++)
|
||||
NameW[DecPos]=Name[DecPos];
|
||||
}
|
||||
break;
|
||||
}
|
||||
Flags<<=2;
|
||||
FlagBits-=2;
|
||||
}
|
||||
NameW[DecPos<MaxDecSize ? DecPos:MaxDecSize-1]=0;
|
||||
}
|
20
src/core/fex/unrar/encname.hpp
Normal file
20
src/core/fex/unrar/encname.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef _RAR_ENCNAME_
|
||||
#define _RAR_ENCNAME_
|
||||
|
||||
class EncodeFileName
|
||||
{
|
||||
private:
|
||||
void AddFlags(int Value);
|
||||
|
||||
byte *EncName;
|
||||
byte Flags;
|
||||
size_t FlagBits;
|
||||
size_t FlagsPos;
|
||||
size_t DestSize;
|
||||
public:
|
||||
EncodeFileName();
|
||||
size_t Encode(char *Name,wchar *NameW,byte *EncName);
|
||||
void Decode(char *Name,byte *EncName,size_t EncSize,wchar *NameW,size_t MaxDecSize);
|
||||
};
|
||||
|
||||
#endif
|
114
src/core/fex/unrar/extract.cpp
Normal file
114
src/core/fex/unrar/extract.cpp
Normal file
@@ -0,0 +1,114 @@
|
||||
#include <stdio.h>
|
||||
#include "rar.hpp"
|
||||
|
||||
#include "unrar.h"
|
||||
|
||||
#define DataIO Arc
|
||||
|
||||
unrar_err_t CmdExtract::ExtractCurrentFile( bool SkipSolid, bool check_compatibility_only )
|
||||
{
|
||||
check( Arc.GetHeaderType() == FILE_HEAD );
|
||||
|
||||
if ( Arc.FileHead.SplitBefore || Arc.FileHead.SplitAfter )
|
||||
return unrar_err_segmented;
|
||||
|
||||
if ( Arc.FileHead.Encrypted )
|
||||
return unrar_err_encrypted;
|
||||
|
||||
if ( !check_compatibility_only )
|
||||
{
|
||||
check( Arc.NextBlockPos-Arc.FileHead.PackSize == Arc.Tell() );
|
||||
Arc.Seek(Arc.NextBlockPos-Arc.FileHead.PackSize,SEEK_SET);
|
||||
}
|
||||
|
||||
// (removed lots of command-line handling)
|
||||
|
||||
#ifdef SFX_MODULE
|
||||
if ((Arc.FileHead.UnpVer!=UNP_VER && Arc.FileHead.UnpVer!=29) &&
|
||||
Arc.FileHead.Method!=0x30)
|
||||
#else
|
||||
if (Arc.FileHead.UnpVer!=VER_UNPACK5 &&
|
||||
(Arc.FileHead.UnpVer<13 || Arc.FileHead.UnpVer>VER_UNPACK))
|
||||
#endif
|
||||
{
|
||||
if (Arc.FileHead.UnpVer>VER_UNPACK)
|
||||
return unrar_err_new_algo;
|
||||
return unrar_err_old_algo;
|
||||
}
|
||||
|
||||
if ( check_compatibility_only )
|
||||
return unrar_ok;
|
||||
|
||||
// (removed lots of command-line/encryption/volume handling)
|
||||
|
||||
update_first_file_pos();
|
||||
FileCount++;
|
||||
DataIO.UnpFileCRC=Arc.OldFormat ? 0 : 0xffffffff;
|
||||
// (removed decryption)
|
||||
DataIO.UnpHash.Init(Arc.FileHead.FileHash.Type,1);
|
||||
DataIO.PackedDataHash.Init(Arc.FileHead.FileHash.Type,1);
|
||||
DataIO.SetPackedSizeToRead(Arc.FileHead.PackSize);
|
||||
DataIO.SetSkipUnpCRC(SkipSolid);
|
||||
// (removed command-line handling)
|
||||
DataIO.SetSkipUnpCRC(SkipSolid);
|
||||
|
||||
if (Arc.FileHead.Method==0)
|
||||
UnstoreFile(Arc.FileHead.UnpSize);
|
||||
else
|
||||
{
|
||||
// Defer creation of Unpack until first extraction
|
||||
if ( !Unp )
|
||||
{
|
||||
Unp = new Unpack( &Arc );
|
||||
if ( !Unp )
|
||||
return unrar_err_memory;
|
||||
}
|
||||
|
||||
Unp->Init(Arc.FileHead.WinSize,Arc.FileHead.Solid);
|
||||
Unp->SetDestSize(Arc.FileHead.UnpSize);
|
||||
#ifndef SFX_MODULE
|
||||
if (Arc.Format!=RARFMT50 && Arc.FileHead.UnpVer<=15)
|
||||
Unp->DoUnpack(15,FileCount>1 && Arc.Solid);
|
||||
else
|
||||
#endif
|
||||
Unp->DoUnpack(Arc.FileHead.UnpVer,Arc.FileHead.Solid);
|
||||
}
|
||||
|
||||
// (no need to seek to next file)
|
||||
|
||||
if (!SkipSolid)
|
||||
{
|
||||
HashValue UnpHash;
|
||||
DataIO.UnpHash.Result(&UnpHash);
|
||||
if (UnpHash==Arc.FileHead.FileHash)
|
||||
{
|
||||
// CRC is correct
|
||||
}
|
||||
else
|
||||
{
|
||||
return unrar_err_corrupt;
|
||||
}
|
||||
}
|
||||
|
||||
// (removed broken file handling)
|
||||
// (removed command-line handling)
|
||||
|
||||
return unrar_ok;
|
||||
}
|
||||
|
||||
|
||||
void CmdExtract::UnstoreFile(int64 DestUnpSize)
|
||||
{
|
||||
Buffer.Alloc((int)Min(DestUnpSize,0x10000));
|
||||
while (1)
|
||||
{
|
||||
unsigned int Code=DataIO.UnpRead(&Buffer[0],(uint)Buffer.Size());
|
||||
if (Code==0 || (int)Code==-1)
|
||||
break;
|
||||
Code=Code<DestUnpSize ? Code:int64to32(DestUnpSize);
|
||||
DataIO.UnpWrite(&Buffer[0],Code);
|
||||
if (DestUnpSize>=0)
|
||||
DestUnpSize-=Code;
|
||||
}
|
||||
Buffer.Reset();
|
||||
}
|
56
src/core/fex/unrar/getbits.cpp
Normal file
56
src/core/fex/unrar/getbits.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#include "rar.hpp"
|
||||
|
||||
BitInput::BitInput(bool AllocBuffer)
|
||||
{
|
||||
ExternalBuffer=false;
|
||||
if (AllocBuffer)
|
||||
{
|
||||
// getbits32 attempts to read data from InAddr, ... InAddr+3 positions.
|
||||
// So let's allocate 3 additional bytes for situation, when we need to
|
||||
// read only 1 byte from the last position of buffer and avoid a crash
|
||||
// from access to next 3 bytes, which contents we do not need.
|
||||
size_t BufSize=MAX_SIZE+3;
|
||||
InBuf=new byte[BufSize];
|
||||
|
||||
// Ensure that we get predictable results when accessing bytes in area
|
||||
// not filled with read data.
|
||||
memset(InBuf,0,BufSize);
|
||||
}
|
||||
else
|
||||
InBuf=NULL;
|
||||
}
|
||||
|
||||
BitInput::~BitInput()
|
||||
{
|
||||
if (!ExternalBuffer)
|
||||
delete[] InBuf;
|
||||
}
|
||||
|
||||
void BitInput::handle_mem_error( Rar_Error_Handler& ErrHandler )
|
||||
{
|
||||
if ( !InBuf )
|
||||
ErrHandler.MemoryError();
|
||||
}
|
||||
|
||||
void BitInput::faddbits(uint Bits)
|
||||
{
|
||||
// Function wrapped version of inline addbits to save code size.
|
||||
addbits(Bits);
|
||||
}
|
||||
|
||||
|
||||
uint BitInput::fgetbits()
|
||||
{
|
||||
// Function wrapped version of inline getbits to save code size.
|
||||
return(getbits());
|
||||
}
|
||||
|
||||
|
||||
void BitInput::SetExternalBuffer(byte *Buf)
|
||||
{
|
||||
if (InBuf!=NULL && !ExternalBuffer)
|
||||
delete[] InBuf;
|
||||
InBuf=Buf;
|
||||
ExternalBuffer=true;
|
||||
}
|
||||
|
70
src/core/fex/unrar/getbits.hpp
Normal file
70
src/core/fex/unrar/getbits.hpp
Normal file
@@ -0,0 +1,70 @@
|
||||
#ifndef _RAR_GETBITS_
|
||||
#define _RAR_GETBITS_
|
||||
|
||||
class BitInput
|
||||
: public Rar_Allocator
|
||||
{
|
||||
public:
|
||||
enum BufferSize {MAX_SIZE=0x8000}; // Size of input buffer.
|
||||
|
||||
int InAddr; // Curent byte position in the buffer.
|
||||
int InBit; // Current bit position in the current byte.
|
||||
|
||||
bool ExternalBuffer;
|
||||
public:
|
||||
BitInput(bool AllocBuffer);
|
||||
~BitInput();
|
||||
void handle_mem_error( Rar_Error_Handler& );
|
||||
|
||||
byte *InBuf; // Dynamically allocated input buffer.
|
||||
|
||||
void InitBitInput()
|
||||
{
|
||||
InAddr=InBit=0;
|
||||
}
|
||||
|
||||
// Move forward by 'Bits' bits.
|
||||
void addbits(uint Bits)
|
||||
{
|
||||
Bits+=InBit;
|
||||
InAddr+=Bits>>3;
|
||||
InBit=Bits&7;
|
||||
}
|
||||
|
||||
// Return 16 bits from current position in the buffer.
|
||||
// Bit at (InAddr,InBit) has the highest position in returning data.
|
||||
uint getbits()
|
||||
{
|
||||
uint BitField=(uint)InBuf[InAddr] << 16;
|
||||
BitField|=(uint)InBuf[InAddr+1] << 8;
|
||||
BitField|=(uint)InBuf[InAddr+2];
|
||||
BitField >>= (8-InBit);
|
||||
return(BitField & 0xffff);
|
||||
}
|
||||
|
||||
// Return 32 bits from current position in the buffer.
|
||||
// Bit at (InAddr,InBit) has the highest position in returning data.
|
||||
uint getbits32()
|
||||
{
|
||||
uint BitField=(uint)InBuf[InAddr] << 24;
|
||||
BitField|=(uint)InBuf[InAddr+1] << 16;
|
||||
BitField|=(uint)InBuf[InAddr+2] << 8;
|
||||
BitField|=(uint)InBuf[InAddr+3];
|
||||
BitField <<= InBit;
|
||||
BitField|=(uint)InBuf[InAddr+4] >> (8-InBit);
|
||||
return(BitField & 0xffffffff);
|
||||
}
|
||||
|
||||
void faddbits(uint Bits);
|
||||
uint fgetbits();
|
||||
|
||||
// Check if buffer has enough space for IncPtr bytes. Returns 'true'
|
||||
// if buffer will be overflown.
|
||||
bool Overflow(uint IncPtr)
|
||||
{
|
||||
return(InAddr+IncPtr>=MAX_SIZE);
|
||||
}
|
||||
|
||||
void SetExternalBuffer(byte *Buf);
|
||||
};
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user