summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--unrar/unrar/UnRAR.vcxproj279
-rw-r--r--unrar/unrar/UnRARDll.vcxproj420
-rw-r--r--unrar/unrar/acknow.txt59
-rw-r--r--unrar/unrar/arccmt.cpp252
-rw-r--r--unrar/unrar/archive.cpp339
-rw-r--r--unrar/unrar/archive.hpp160
-rw-r--r--unrar/unrar/arcread.cpp1726
-rw-r--r--unrar/unrar/array.hpp101
-rw-r--r--unrar/unrar/beosea.cpp113
-rw-r--r--unrar/unrar/blake2s.cpp183
-rw-r--r--unrar/unrar/blake2s.hpp107
-rw-r--r--unrar/unrar/blake2s_sse.cpp129
-rw-r--r--unrar/unrar/blake2sp.cpp153
-rw-r--r--unrar/unrar/cmddata.cpp1607
-rw-r--r--unrar/unrar/cmddata.hpp77
-rw-r--r--unrar/unrar/cmdfilter.cpp354
-rw-r--r--unrar/unrar/cmdmix.cpp125
-rw-r--r--unrar/unrar/coder.cpp3
-rw-r--r--unrar/unrar/coder.hpp1
-rw-r--r--unrar/unrar/compress.hpp78
-rw-r--r--unrar/unrar/consio.cpp464
-rw-r--r--unrar/unrar/consio.hpp48
-rw-r--r--unrar/unrar/crc.cpp280
-rw-r--r--unrar/unrar/crc.hpp17
-rw-r--r--unrar/unrar/crypt.cpp419
-rw-r--r--unrar/unrar/crypt.hpp191
-rw-r--r--unrar/unrar/crypt1.cpp79
-rw-r--r--unrar/unrar/crypt2.cpp133
-rw-r--r--unrar/unrar/crypt3.cpp69
-rw-r--r--unrar/unrar/crypt5.cpp241
-rw-r--r--unrar/unrar/dll.cpp495
-rw-r--r--unrar/unrar/dll.def1
-rw-r--r--unrar/unrar/dll.hpp75
-rw-r--r--unrar/unrar/dll.rc28
-rw-r--r--unrar/unrar/dll_nocrypt.def13
-rw-r--r--unrar/unrar/encname.cpp24
-rw-r--r--unrar/unrar/encname.hpp13
-rw-r--r--unrar/unrar/errhnd.cpp392
-rw-r--r--unrar/unrar/errhnd.hpp98
-rw-r--r--unrar/unrar/extinfo.cpp204
-rw-r--r--unrar/unrar/extinfo.hpp19
-rw-r--r--unrar/unrar/extract.cpp1926
-rw-r--r--unrar/unrar/extract.hpp92
-rw-r--r--unrar/unrar/filcreat.cpp306
-rw-r--r--unrar/unrar/filcreat.hpp13
-rw-r--r--unrar/unrar/file.cpp813
-rw-r--r--unrar/unrar/file.hpp171
-rw-r--r--unrar/unrar/filefn.cpp785
-rw-r--r--unrar/unrar/filefn.hpp57
-rw-r--r--unrar/unrar/filestr.cpp268
-rw-r--r--unrar/unrar/filestr.hpp16
-rw-r--r--unrar/unrar/find.cpp279
-rw-r--r--unrar/unrar/find.hpp28
-rw-r--r--unrar/unrar/getbits.cpp40
-rw-r--r--unrar/unrar/getbits.hpp66
-rw-r--r--unrar/unrar/global.cpp3
-rw-r--r--unrar/unrar/hardlinks.cpp37
-rw-r--r--unrar/unrar/hash.cpp135
-rw-r--r--unrar/unrar/hash.hpp58
-rw-r--r--unrar/unrar/headers.cpp53
-rw-r--r--unrar/unrar/headers.hpp405
-rw-r--r--unrar/unrar/headers5.hpp107
-rw-r--r--unrar/unrar/int64.cpp274
-rw-r--r--unrar/unrar/int64.hpp86
-rw-r--r--unrar/unrar/isnt.cpp107
-rw-r--r--unrar/unrar/isnt.hpp12
-rw-r--r--unrar/unrar/license.txt18
-rw-r--r--unrar/unrar/list.cpp614
-rw-r--r--unrar/unrar/loclang.hpp753
-rw-r--r--unrar/unrar/log.cpp33
-rw-r--r--unrar/unrar/log.hpp12
-rw-r--r--unrar/unrar/makefile (renamed from unrar/unrar/makefile.unix)76
-rw-r--r--unrar/unrar/makefile.bcc501
-rw-r--r--unrar/unrar/makefile.cygmin54
-rw-r--r--unrar/unrar/makefile.dj50
-rw-r--r--unrar/unrar/makefile.dmc54
-rw-r--r--unrar/unrar/makefile.msc564
-rw-r--r--unrar/unrar/match.cpp227
-rw-r--r--unrar/unrar/match.hpp33
-rw-r--r--unrar/unrar/model.cpp117
-rw-r--r--unrar/unrar/model.hpp76
-rw-r--r--unrar/unrar/msc.dep2532
-rw-r--r--unrar/unrar/options.cpp23
-rw-r--r--unrar/unrar/options.hpp194
-rw-r--r--unrar/unrar/os.hpp235
-rw-r--r--unrar/unrar/os2ea.cpp94
-rw-r--r--unrar/unrar/pathfn.cpp1158
-rw-r--r--unrar/unrar/pathfn.hpp99
-rw-r--r--unrar/unrar/qopen.cpp300
-rw-r--r--unrar/unrar/qopen.hpp61
-rw-r--r--unrar/unrar/rar.cpp135
-rw-r--r--unrar/unrar/rar.hpp46
-rw-r--r--unrar/unrar/rardefs.hpp33
-rw-r--r--unrar/unrar/rarfn.hpp7
-rw-r--r--unrar/unrar/raros.hpp19
-rw-r--r--unrar/unrar/rarpch.cpp2
-rw-r--r--unrar/unrar/rartypes.hpp37
-rw-r--r--unrar/unrar/rarvm.cpp969
-rw-r--r--unrar/unrar/rarvm.hpp93
-rw-r--r--unrar/unrar/rarvmtbl.cpp53
-rw-r--r--unrar/unrar/rawint.hpp122
-rw-r--r--unrar/unrar/rawread.cpp163
-rw-r--r--unrar/unrar/rawread.hpp43
-rw-r--r--unrar/unrar/rdwrfn.cpp232
-rw-r--r--unrar/unrar/rdwrfn.hpp67
-rw-r--r--unrar/unrar/readme.txt35
-rw-r--r--unrar/unrar/recvol.cpp417
-rw-r--r--unrar/unrar/recvol.hpp82
-rw-r--r--unrar/unrar/recvol3.cpp551
-rw-r--r--unrar/unrar/recvol5.cpp538
-rw-r--r--unrar/unrar/resource.cpp18
-rw-r--r--unrar/unrar/resource.hpp11
-rw-r--r--unrar/unrar/rijndael.cpp529
-rw-r--r--unrar/unrar/rijndael.hpp43
-rw-r--r--unrar/unrar/rs.cpp93
-rw-r--r--unrar/unrar/rs.hpp18
-rw-r--r--unrar/unrar/rs16.cpp421
-rw-r--r--unrar/unrar/rs16.hpp44
-rw-r--r--unrar/unrar/savepos.cpp15
-rw-r--r--unrar/unrar/savepos.hpp15
-rw-r--r--unrar/unrar/scantree.cpp551
-rw-r--r--unrar/unrar/scantree.hpp67
-rw-r--r--unrar/unrar/secpassword.cpp218
-rw-r--r--unrar/unrar/secpassword.hpp28
-rw-r--r--unrar/unrar/sha1.cpp341
-rw-r--r--unrar/unrar/sha1.hpp14
-rw-r--r--unrar/unrar/sha256.cpp148
-rw-r--r--unrar/unrar/sha256.hpp17
-rw-r--r--unrar/unrar/smallfn.cpp20
-rw-r--r--unrar/unrar/smallfn.hpp4
-rw-r--r--unrar/unrar/strfn.cpp465
-rw-r--r--unrar/unrar/strfn.hpp56
-rw-r--r--unrar/unrar/strlist.cpp134
-rw-r--r--unrar/unrar/strlist.hpp36
-rw-r--r--unrar/unrar/suballoc.cpp97
-rw-r--r--unrar/unrar/suballoc.hpp39
-rw-r--r--unrar/unrar/system.cpp147
-rw-r--r--unrar/unrar/system.hpp20
-rw-r--r--unrar/unrar/threadmisc.cpp153
-rw-r--r--unrar/unrar/threadpool.cpp212
-rw-r--r--unrar/unrar/threadpool.hpp107
-rw-r--r--unrar/unrar/timefn.cpp366
-rw-r--r--unrar/unrar/timefn.hpp71
-rw-r--r--unrar/unrar/ui.cpp14
-rw-r--r--unrar/unrar/ui.hpp176
-rw-r--r--unrar/unrar/uicommon.cpp65
-rw-r--r--unrar/unrar/uiconsole.cpp484
-rw-r--r--unrar/unrar/uisilent.cpp75
-rw-r--r--unrar/unrar/ulinks.cpp150
-rw-r--r--unrar/unrar/ulinks.hpp9
-rw-r--r--unrar/unrar/unicode.cpp694
-rw-r--r--unrar/unrar/unicode.hpp75
-rw-r--r--unrar/unrar/unios2.cpp128
-rw-r--r--unrar/unrar/unpack.cpp1160
-rw-r--r--unrar/unrar/unpack.hpp407
-rw-r--r--unrar/unrar/unpack15.cpp142
-rw-r--r--unrar/unrar/unpack20.cpp199
-rw-r--r--unrar/unrar/unpack30.cpp765
-rw-r--r--unrar/unrar/unpack50.cpp687
-rw-r--r--unrar/unrar/unpack50frag.cpp103
-rw-r--r--unrar/unrar/unpack50mt.cpp655
-rw-r--r--unrar/unrar/unpackinline.cpp147
-rw-r--r--unrar/unrar/uowners.cpp105
-rw-r--r--unrar/unrar/version.hpp12
-rw-r--r--unrar/unrar/volume.cpp330
-rw-r--r--unrar/unrar/volume.hpp6
-rw-r--r--unrar/unrar/win32acl.cpp112
-rw-r--r--unrar/unrar/win32lnk.cpp194
-rw-r--r--unrar/unrar/win32stm.cpp190
169 files changed, 23138 insertions, 15692 deletions
diff --git a/unrar/unrar/UnRAR.vcxproj b/unrar/unrar/UnRAR.vcxproj
new file mode 100644
index 0000000..512bcf1
--- /dev/null
+++ b/unrar/unrar/UnRAR.vcxproj
@@ -0,0 +1,279 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{95CC809B-03FC-4EDB-BB20-FD07A698C05F}</ProjectGuid>
+ <RootNamespace>UnRAR</RootNamespace>
+ <Keyword>Win32Proj</Keyword>
+ <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <PlatformToolset>v140_xp</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <PlatformToolset>v140_xp</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <PlatformToolset>v140_xp</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <PlatformToolset>v140_xp</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>14.0.24720.0</_ProjectFileVersion>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <OutDir>build\unrar32\$(Configuration)\</OutDir>
+ <IntDir>build\unrar32\$(Configuration)\obj\</IntDir>
+ <LinkIncremental>true</LinkIncremental>
+ <GenerateManifest>false</GenerateManifest>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <OutDir>build\unrar64\$(Configuration)\</OutDir>
+ <IntDir>build\unrar64\$(Configuration)\obj\</IntDir>
+ <LinkIncremental>true</LinkIncremental>
+ <GenerateManifest>false</GenerateManifest>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <OutDir>build\unrar32\$(Configuration)\</OutDir>
+ <IntDir>build\unrar32\$(Configuration)\obj\</IntDir>
+ <LinkIncremental>false</LinkIncremental>
+ <GenerateManifest>false</GenerateManifest>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <OutDir>build\unrar64\$(Configuration)\</OutDir>
+ <IntDir>build\unrar64\$(Configuration)\obj\</IntDir>
+ <LinkIncremental>false</LinkIncremental>
+ <GenerateManifest>false</GenerateManifest>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>UNRAR;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <RuntimeTypeInfo>false</RuntimeTypeInfo>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CallingConvention>StdCall</CallingConvention>
+ <DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ <TargetMachine>MachineX86</TargetMachine>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Midl>
+ <TargetEnvironment>X64</TargetEnvironment>
+ </Midl>
+ <ClCompile>
+ <AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>UNRAR;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <RuntimeTypeInfo>false</RuntimeTypeInfo>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CallingConvention>StdCall</CallingConvention>
+ <DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ <TargetMachine>MachineX64</TargetMachine>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
+ <Optimization>MaxSpeed</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Neither</FavorSizeOrSpeed>
+ <OmitFramePointers>true</OmitFramePointers>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ <PreprocessorDefinitions>UNRAR;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <StructMemberAlignment>Default</StructMemberAlignment>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Precise</FloatingPointModel>
+ <RuntimeTypeInfo>false</RuntimeTypeInfo>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CallingConvention>StdCall</CallingConvention>
+ <DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <LinkTimeCodeGeneration />
+ <TargetMachine>MachineX86</TargetMachine>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Midl>
+ <TargetEnvironment>X64</TargetEnvironment>
+ </Midl>
+ <ClCompile>
+ <AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
+ <Optimization>MinSpace</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Neither</FavorSizeOrSpeed>
+ <OmitFramePointers>true</OmitFramePointers>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ <PreprocessorDefinitions>UNRAR;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>false</StringPooling>
+ <MinimalRebuild>false</MinimalRebuild>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <RuntimeTypeInfo>false</RuntimeTypeInfo>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CallingConvention>StdCall</CallingConvention>
+ <DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <LinkTimeCodeGeneration />
+ <TargetMachine>MachineX64</TargetMachine>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="archive.cpp" />
+ <ClCompile Include="arcread.cpp" />
+ <ClCompile Include="blake2s.cpp" />
+ <ClCompile Include="cmddata.cpp" />
+ <ClCompile Include="consio.cpp" />
+ <ClCompile Include="crc.cpp" />
+ <ClCompile Include="crypt.cpp" />
+ <ClCompile Include="encname.cpp" />
+ <ClCompile Include="errhnd.cpp" />
+ <ClCompile Include="extinfo.cpp" />
+ <ClCompile Include="extract.cpp" />
+ <ClCompile Include="filcreat.cpp" />
+ <ClCompile Include="file.cpp" />
+ <ClCompile Include="filefn.cpp" />
+ <ClCompile Include="filestr.cpp" />
+ <ClCompile Include="find.cpp" />
+ <ClCompile Include="getbits.cpp" />
+ <ClCompile Include="global.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="hash.cpp" />
+ <ClCompile Include="headers.cpp" />
+ <ClCompile Include="isnt.cpp" />
+ <ClCompile Include="list.cpp" />
+ <ClCompile Include="match.cpp" />
+ <ClCompile Include="options.cpp" />
+ <ClCompile Include="pathfn.cpp" />
+ <ClCompile Include="qopen.cpp" />
+ <ClCompile Include="rar.cpp" />
+ <ClCompile Include="rarpch.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="rarvm.cpp" />
+ <ClCompile Include="rawread.cpp" />
+ <ClCompile Include="rdwrfn.cpp" />
+ <ClCompile Include="recvol.cpp" />
+ <ClCompile Include="resource.cpp" />
+ <ClCompile Include="rijndael.cpp" />
+ <ClCompile Include="rs.cpp" />
+ <ClCompile Include="rs16.cpp" />
+ <ClCompile Include="scantree.cpp" />
+ <ClCompile Include="secpassword.cpp" />
+ <ClCompile Include="sha1.cpp" />
+ <ClCompile Include="sha256.cpp" />
+ <ClCompile Include="smallfn.cpp" />
+ <ClCompile Include="strfn.cpp" />
+ <ClCompile Include="strlist.cpp" />
+ <ClCompile Include="system.cpp" />
+ <ClCompile Include="threadpool.cpp" />
+ <ClCompile Include="timefn.cpp" />
+ <ClCompile Include="ui.cpp" />
+ <ClCompile Include="unicode.cpp" />
+ <ClCompile Include="unpack.cpp" />
+ <ClCompile Include="volume.cpp" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/unrar/unrar/UnRARDll.vcxproj b/unrar/unrar/UnRARDll.vcxproj
new file mode 100644
index 0000000..72cecd8
--- /dev/null
+++ b/unrar/unrar/UnRARDll.vcxproj
@@ -0,0 +1,420 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="release_nocrypt|Win32">
+ <Configuration>release_nocrypt</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="release_nocrypt|x64">
+ <Configuration>release_nocrypt</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectName>UnRAR</ProjectName>
+ <ProjectGuid>{E815C46C-36C4-499F-BBC2-E772C6B17971}</ProjectGuid>
+ <RootNamespace>UnRAR</RootNamespace>
+ <Keyword>Win32Proj</Keyword>
+ <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v140_xp</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v140_xp</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v140_xp</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v140_xp</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v140_xp</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v140_xp</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>14.0.24720.0</_ProjectFileVersion>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <OutDir>build\unrardll32\$(Configuration)\</OutDir>
+ <IntDir>build\unrardll32\$(Configuration)\obj\</IntDir>
+ <LinkIncremental>true</LinkIncremental>
+ <GenerateManifest>true</GenerateManifest>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <OutDir>build\unrardll64\$(Configuration)\</OutDir>
+ <IntDir>build\unrardll64\$(Configuration)\obj\</IntDir>
+ <LinkIncremental>true</LinkIncremental>
+ <GenerateManifest>true</GenerateManifest>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <OutDir>build\unrardll32\$(Configuration)\</OutDir>
+ <IntDir>build\unrardll32\$(Configuration)\obj\</IntDir>
+ <LinkIncremental>false</LinkIncremental>
+ <GenerateManifest>true</GenerateManifest>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <OutDir>build\unrardll64\$(Configuration)\</OutDir>
+ <IntDir>build\unrardll64\$(Configuration)\obj\</IntDir>
+ <LinkIncremental>false</LinkIncremental>
+ <GenerateManifest>true</GenerateManifest>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|Win32'">
+ <OutDir>build\unrardll32\$(Configuration)\</OutDir>
+ <IntDir>build\unrardll32\$(Configuration)\obj\</IntDir>
+ <LinkIncremental>false</LinkIncremental>
+ <GenerateManifest>true</GenerateManifest>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|x64'">
+ <OutDir>build\unrardll64\$(Configuration)\</OutDir>
+ <IntDir>build\unrardll64\$(Configuration)\obj\</IntDir>
+ <LinkIncremental>false</LinkIncremental>
+ <GenerateManifest>true</GenerateManifest>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>RARDLL;UNRAR;SILENT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <ExceptionHandling>Sync</ExceptionHandling>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <StructMemberAlignment>Default</StructMemberAlignment>
+ <RuntimeTypeInfo>false</RuntimeTypeInfo>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CallingConvention>Cdecl</CallingConvention>
+ <DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
+ </ClCompile>
+ <Link>
+ <OutputFile>$(OutDir)unrar.dll</OutputFile>
+ <ModuleDefinitionFile>dll.def</ModuleDefinitionFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ <TargetMachine>MachineX86</TargetMachine>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Midl>
+ <TargetEnvironment>X64</TargetEnvironment>
+ </Midl>
+ <ClCompile>
+ <AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>RARDLL;UNRAR;SILENT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <ExceptionHandling>Sync</ExceptionHandling>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <StructMemberAlignment>Default</StructMemberAlignment>
+ <RuntimeTypeInfo>false</RuntimeTypeInfo>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CallingConvention>Cdecl</CallingConvention>
+ <DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
+ </ClCompile>
+ <Link>
+ <OutputFile>$(OutDir)unrar.dll</OutputFile>
+ <ModuleDefinitionFile>dll.def</ModuleDefinitionFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ <TargetMachine>MachineX64</TargetMachine>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
+ <Optimization>MaxSpeed</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Neither</FavorSizeOrSpeed>
+ <OmitFramePointers>true</OmitFramePointers>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ <PreprocessorDefinitions>RARDLL;UNRAR;SILENT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <ExceptionHandling>Sync</ExceptionHandling>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <StructMemberAlignment>Default</StructMemberAlignment>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Precise</FloatingPointModel>
+ <RuntimeTypeInfo>false</RuntimeTypeInfo>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CallingConvention>Cdecl</CallingConvention>
+ <DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalOptions>/SAFESEH %(AdditionalOptions)</AdditionalOptions>
+ <OutputFile>$(OutDir)unrar.dll</OutputFile>
+ <ModuleDefinitionFile>dll.def</ModuleDefinitionFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <LinkTimeCodeGeneration />
+ <TargetMachine>MachineX86</TargetMachine>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Midl>
+ <TargetEnvironment>X64</TargetEnvironment>
+ </Midl>
+ <ClCompile>
+ <AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
+ <Optimization>MaxSpeed</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Neither</FavorSizeOrSpeed>
+ <OmitFramePointers>true</OmitFramePointers>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ <PreprocessorDefinitions>RARDLL;UNRAR;SILENT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>false</StringPooling>
+ <MinimalRebuild>false</MinimalRebuild>
+ <ExceptionHandling>Sync</ExceptionHandling>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <StructMemberAlignment>Default</StructMemberAlignment>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <RuntimeTypeInfo>false</RuntimeTypeInfo>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CallingConvention>Cdecl</CallingConvention>
+ <DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
+ </ClCompile>
+ <Link>
+ <OutputFile>$(OutDir)unrar.dll</OutputFile>
+ <ModuleDefinitionFile>dll.def</ModuleDefinitionFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <LinkTimeCodeGeneration />
+ <TargetMachine>MachineX64</TargetMachine>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|Win32'">
+ <ClCompile>
+ <AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
+ <Optimization>MaxSpeed</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Neither</FavorSizeOrSpeed>
+ <OmitFramePointers>true</OmitFramePointers>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ <PreprocessorDefinitions>RARDLL;UNRAR;SILENT;RAR_NOCRYPT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <ExceptionHandling>Sync</ExceptionHandling>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <StructMemberAlignment>Default</StructMemberAlignment>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Precise</FloatingPointModel>
+ <RuntimeTypeInfo>false</RuntimeTypeInfo>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CallingConvention>Cdecl</CallingConvention>
+ <DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalOptions>/SAFESEH %(AdditionalOptions)</AdditionalOptions>
+ <OutputFile>$(OutDir)unrar.dll</OutputFile>
+ <ModuleDefinitionFile>dll_nocrypt.def</ModuleDefinitionFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <LinkTimeCodeGeneration />
+ <TargetMachine>MachineX86</TargetMachine>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|x64'">
+ <Midl>
+ <TargetEnvironment>X64</TargetEnvironment>
+ </Midl>
+ <ClCompile>
+ <AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
+ <Optimization>MaxSpeed</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Neither</FavorSizeOrSpeed>
+ <OmitFramePointers>true</OmitFramePointers>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ <PreprocessorDefinitions>RARDLL;UNRAR;SILENT;RAR_NOCRYPT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>false</StringPooling>
+ <MinimalRebuild>false</MinimalRebuild>
+ <ExceptionHandling>Sync</ExceptionHandling>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <StructMemberAlignment>Default</StructMemberAlignment>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <RuntimeTypeInfo>false</RuntimeTypeInfo>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CallingConvention>StdCall</CallingConvention>
+ <DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
+ </ClCompile>
+ <Link>
+ <OutputFile>$(OutDir)unrar.dll</OutputFile>
+ <ModuleDefinitionFile>dll_nocrypt.def</ModuleDefinitionFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <LinkTimeCodeGeneration />
+ <TargetMachine>MachineX64</TargetMachine>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="archive.cpp" />
+ <ClCompile Include="arcread.cpp" />
+ <ClCompile Include="blake2s.cpp" />
+ <ClCompile Include="cmddata.cpp" />
+ <ClCompile Include="consio.cpp" />
+ <ClCompile Include="crc.cpp" />
+ <ClCompile Include="crypt.cpp" />
+ <ClCompile Include="dll.cpp" />
+ <ClCompile Include="encname.cpp" />
+ <ClCompile Include="errhnd.cpp" />
+ <ClCompile Include="extinfo.cpp" />
+ <ClCompile Include="extract.cpp" />
+ <ClCompile Include="filcreat.cpp" />
+ <ClCompile Include="file.cpp" />
+ <ClCompile Include="filefn.cpp" />
+ <ClCompile Include="filestr.cpp" />
+ <ClCompile Include="find.cpp" />
+ <ClCompile Include="getbits.cpp" />
+ <ClCompile Include="global.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|x64'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="hash.cpp" />
+ <ClCompile Include="headers.cpp" />
+ <ClCompile Include="isnt.cpp" />
+ <ClCompile Include="match.cpp" />
+ <ClCompile Include="options.cpp" />
+ <ClCompile Include="pathfn.cpp" />
+ <ClCompile Include="qopen.cpp" />
+ <ClCompile Include="rar.cpp" />
+ <ClCompile Include="rarpch.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|x64'">Create</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="rarvm.cpp" />
+ <ClCompile Include="rawread.cpp" />
+ <ClCompile Include="rdwrfn.cpp" />
+ <ClCompile Include="rijndael.cpp" />
+ <ClCompile Include="rs.cpp" />
+ <ClCompile Include="rs16.cpp" />
+ <ClCompile Include="scantree.cpp" />
+ <ClCompile Include="secpassword.cpp" />
+ <ClCompile Include="sha1.cpp" />
+ <ClCompile Include="sha256.cpp" />
+ <ClCompile Include="smallfn.cpp" />
+ <ClCompile Include="strfn.cpp" />
+ <ClCompile Include="strlist.cpp" />
+ <ClCompile Include="system.cpp" />
+ <ClCompile Include="threadpool.cpp" />
+ <ClCompile Include="timefn.cpp" />
+ <ClCompile Include="ui.cpp" />
+ <ClCompile Include="unicode.cpp" />
+ <ClCompile Include="unpack.cpp" />
+ <ClCompile Include="volume.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="rar.hpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="dll.rc" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/unrar/unrar/acknow.txt b/unrar/unrar/acknow.txt
new file mode 100644
index 0000000..ec2c2c7
--- /dev/null
+++ b/unrar/unrar/acknow.txt
@@ -0,0 +1,59 @@
+ 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.
+
+* RAR4 text compression algorithm is based on Dmitry Shkarin PPMII
+ and Dmitry Subbotin carryless rangecoder public domain source code.
+ You can find it in ftp.elf.stuba.sk/pub/pc/pack.
+
+* RAR encryption includes parts of public domain code
+ from Szymon Stefanek AES and Steve Reid SHA-1 implementations.
+
+* With exception of SFX modules, RAR uses CRC32 function based
+ on Intel Slicing-by-8 algorithm. Original Intel Slicing-by-8 code
+ is available here:
+
+ https://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.
diff --git a/unrar/unrar/arccmt.cpp b/unrar/unrar/arccmt.cpp
index 6ada2f0..8b7e498 100644
--- a/unrar/unrar/arccmt.cpp
+++ b/unrar/unrar/arccmt.cpp
@@ -1,57 +1,74 @@
-bool Archive::GetComment(Array<byte> *CmtData,Array<wchar> *CmtDataW)
+static bool IsAnsiEscComment(const wchar *Data,size_t Size);
+
+bool Archive::GetComment(Array<wchar> *CmtData)
{
if (!MainComment)
- return(false);
- SaveFilePos SavePos(*this);
+ return false;
+ int64 SavePos=Tell();
+ bool Success=DoGetComment(CmtData);
+ Seek(SavePos,SEEK_SET);
+ return Success;
+}
+
- ushort CmtLength;
+bool Archive::DoGetComment(Array<wchar> *CmtData)
+{
#ifndef SFX_MODULE
- if (OldFormat)
+ uint CmtLength;
+ if (Format==RARFMT14)
{
- Seek(SFXSize+SIZEOF_OLDMHD,SEEK_SET);
- CmtLength=GetByte()+(GetByte()<<8);
+ Seek(SFXSize+SIZEOF_MAINHEAD14,SEEK_SET);
+ CmtLength=GetByte();
+ CmtLength+=(GetByte()<<8);
}
else
#endif
{
- if (NewMhd.Flags & MHD_COMMENT)
+ if (MainHead.CommentInHeader)
{
- Seek(SFXSize+SIZEOF_MARKHEAD+SIZEOF_NEWMHD,SEEK_SET);
- ReadHeader();
+ // Old style (RAR 2.9) archive comment embedded into the main
+ // archive header.
+ Seek(SFXSize+SIZEOF_MARKHEAD3+SIZEOF_MAINHEAD3,SEEK_SET);
+ if (!ReadHeader() || GetHeaderType()!=HEAD3_CMT)
+ return false;
}
else
{
- Seek(SFXSize+SIZEOF_MARKHEAD+NewMhd.HeadSize,SEEK_SET);
- return(SearchSubBlock(SUBHEAD_TYPE_CMT)!=0 && ReadCommentData(CmtData,CmtDataW)!=0);
+ // Current (RAR 3.0+) version of archive comment.
+ Seek(GetStartPos(),SEEK_SET);
+ return SearchSubBlock(SUBHEAD_TYPE_CMT)!=0 && ReadCommentData(CmtData);
}
#ifndef SFX_MODULE
- if (CommHead.HeadCRC!=HeaderCRC)
+ // Old style (RAR 2.9) comment header embedded into the main
+ // archive header.
+ if (BrokenHeader || CommHead.HeadSize<SIZEOF_COMMHEAD)
{
- Log(FileName,St(MLogCommHead));
- Alarm();
- return(false);
+ uiMsg(UIERROR_CMTBROKEN,FileName);
+ return false;
}
CmtLength=CommHead.HeadSize-SIZEOF_COMMHEAD;
#endif
}
#ifndef SFX_MODULE
- if (OldFormat && (OldMhd.Flags & MHD_PACK_COMMENT) || !OldFormat && CommHead.Method!=0x30)
+ if (Format==RARFMT14 && MainHead.PackComment || Format!=RARFMT14 && CommHead.Method!=0x30)
{
- if (!OldFormat && (CommHead.UnpVer < 15 || CommHead.UnpVer > UNP_VER || CommHead.Method > 0x35))
- return(false);
+ if (Format!=RARFMT14 && (CommHead.UnpVer < 15 || CommHead.UnpVer > VER_UNPACK || CommHead.Method > 0x35))
+ return false;
ComprDataIO DataIO;
- Unpack Unpack(&DataIO);
- Unpack.Init();
DataIO.SetTestMode(true);
uint UnpCmtLength;
- if (OldFormat)
+ if (Format==RARFMT14)
{
-#ifdef NOCRYPT
- return(false);
+#ifdef RAR_NOCRYPT
+ return false;
#else
- UnpCmtLength=GetByte()+(GetByte()<<8);
+ UnpCmtLength=GetByte();
+ UnpCmtLength+=(GetByte()<<8);
+ if (CmtLength<2)
+ return false;
CmtLength-=2;
DataIO.SetCmt13Encryption();
+ CommHead.UnpVer=15;
#endif
}
else
@@ -59,157 +76,110 @@ bool Archive::GetComment(Array<byte> *CmtData,Array<wchar> *CmtDataW)
DataIO.SetFiles(this,NULL);
DataIO.EnableShowProgress(false);
DataIO.SetPackedSizeToRead(CmtLength);
- Unpack.SetDestSize(UnpCmtLength);
- Unpack.DoUnpack(CommHead.UnpVer,false);
+ DataIO.UnpHash.Init(HASH_CRC32,1);
+ DataIO.SetNoFileHeader(true); // this->FileHead is not filled yet.
+
+ Unpack CmtUnpack(&DataIO);
+ CmtUnpack.Init(0x10000,false);
+ CmtUnpack.SetDestSize(UnpCmtLength);
+ CmtUnpack.DoUnpack(CommHead.UnpVer,false);
- if (!OldFormat && ((~DataIO.UnpFileCRC)&0xffff)!=CommHead.CommCRC)
+ if (Format!=RARFMT14 && (DataIO.UnpHash.GetCRC32()&0xffff)!=CommHead.CommCRC)
{
- Log(FileName,St(MLogCommBrk));
- Alarm();
- return(false);
+ uiMsg(UIERROR_CMTBROKEN,FileName);
+ return false;
}
else
{
- unsigned char *UnpData;
- uint UnpDataSize;
+ byte *UnpData;
+ size_t UnpDataSize;
DataIO.GetUnpackedData(&UnpData,&UnpDataSize);
- CmtData->Alloc(UnpDataSize);
- memcpy(&((*CmtData)[0]),UnpData,UnpDataSize);
+ if (UnpDataSize>0)
+ {
+#ifdef _WIN_ALL
+ // If we ever decide to extend it to Android, we'll need to alloc
+ // 4x memory for OEM to UTF-8 output here.
+ OemToCharBuffA((char *)UnpData,(char *)UnpData,(DWORD)UnpDataSize);
+#endif
+ CmtData->Alloc(UnpDataSize+1);
+ memset(CmtData->Addr(0),0,CmtData->Size()*sizeof(wchar));
+ CharToWide((char *)UnpData,CmtData->Addr(0),CmtData->Size());
+ CmtData->Alloc(wcslen(CmtData->Addr(0)));
+ }
}
}
else
{
- CmtData->Alloc(CmtLength);
-
- Read(&((*CmtData)[0]),CmtLength);
- if (!OldFormat && CommHead.CommCRC!=(~CRC(0xffffffff,&((*CmtData)[0]),CmtLength)&0xffff))
+ if (CmtLength==0)
+ return false;
+ Array<byte> CmtRaw(CmtLength);
+ int ReadSize=Read(&CmtRaw[0],CmtLength);
+ if (ReadSize>=0 && (uint)ReadSize<CmtLength) // Comment is shorter than declared.
{
- Log(FileName,St(MLogCommBrk));
- Alarm();
- CmtData->Reset();
- return(false);
+ CmtLength=ReadSize;
+ CmtRaw.Alloc(CmtLength);
}
- }
-#endif
-#if defined(_WIN_32) && !defined(_WIN_CE)
- if (CmtData->Size()>0)
- {
- int CmtSize=CmtData->Size();
- OemToCharBuff((char *)CmtData->Addr(),(char *)CmtData->Addr(),CmtSize);
- if (CmtDataW!=NULL)
+ if (Format!=RARFMT14 && CommHead.CommCRC!=(~CRC32(0xffffffff,&CmtRaw[0],CmtLength)&0xffff))
{
- CmtDataW->Alloc(CmtSize+1);
- CmtData->Push(0);
- CharToWide((char *)CmtData->Addr(),CmtDataW->Addr(),CmtSize+1);
- CmtData->Alloc(CmtSize);
- CmtDataW->Alloc(strlenw(CmtDataW->Addr()));
+ uiMsg(UIERROR_CMTBROKEN,FileName);
+ return false;
}
+ CmtData->Alloc(CmtLength+1);
+ CmtRaw.Push(0);
+#ifdef _WIN_ALL
+ // If we ever decide to extend it to Android, we'll need to alloc
+ // 4x memory for OEM to UTF-8 output here.
+ OemToCharA((char *)&CmtRaw[0],(char *)&CmtRaw[0]);
+#endif
+ CharToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtData->Size());
+ CmtData->Alloc(wcslen(CmtData->Addr(0)));
}
#endif
- return(CmtData->Size()>0);
+ return CmtData->Size() > 0;
}
-int Archive::ReadCommentData(Array<byte> *CmtData,Array<wchar> *CmtDataW)
+bool Archive::ReadCommentData(Array<wchar> *CmtData)
{
- bool Unicode=SubHead.SubFlags & SUBHEAD_FLAGS_CMT_UNICODE;
- if (!ReadSubData(CmtData,NULL))
- return(0);
- int CmtSize=CmtData->Size();
- if (Unicode)
- {
- CmtSize/=2;
- Array<wchar> DataW(CmtSize+1);
- RawToWide(CmtData->Addr(),DataW.Addr(),CmtSize);
- DataW[CmtSize]=0;
- int DestSize=CmtSize*4;
- CmtData->Alloc(DestSize+1);
- WideToChar(DataW.Addr(),(char *)CmtData->Addr(),DestSize);
- (*CmtData)[DestSize]=0;
- CmtSize=strlen((char *)CmtData->Addr());
- CmtData->Alloc(CmtSize);
- if (CmtDataW!=NULL)
+ Array<byte> CmtRaw;
+ if (!ReadSubData(&CmtRaw,NULL,false))
+ return false;
+ size_t CmtSize=CmtRaw.Size();
+ CmtRaw.Push(0);
+ CmtData->Alloc(CmtSize+1);
+ if (Format==RARFMT50)
+ UtfToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtData->Size());
+ else
+ if ((SubHead.SubFlags & SUBHEAD_FLAGS_CMT_UNICODE)!=0)
{
- *CmtDataW=DataW;
- CmtDataW->Alloc(CmtSize);
+ RawToWide(&CmtRaw[0],CmtData->Addr(0),CmtSize/2);
+ (*CmtData)[CmtSize/2]=0;
+
}
- }
- else
- if (CmtDataW!=NULL)
+ else
{
- CmtData->Push(0);
- CmtDataW->Alloc(CmtSize+1);
- CharToWide((char *)CmtData->Addr(),CmtDataW->Addr(),CmtSize+1);
- CmtData->Alloc(CmtSize);
- CmtDataW->Alloc(strlenw(CmtDataW->Addr()));
+ CharToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtData->Size());
}
- return(CmtSize);
+ CmtData->Alloc(wcslen(CmtData->Addr(0))); // Set buffer size to actual comment length.
+ return true;
}
void Archive::ViewComment()
{
-#ifndef GUI
if (Cmd->DisableComment)
return;
- Array<byte> CmtBuf;
- if (GetComment(&CmtBuf,NULL))
+ Array<wchar> CmtBuf;
+ if (GetComment(&CmtBuf)) // In GUI too, so "Test" command detects broken comments.
{
- int CmtSize=CmtBuf.Size();
- char *ChPtr=(char *)memchr(&CmtBuf[0],0x1A,CmtSize);
+ size_t CmtSize=CmtBuf.Size();
+ wchar *ChPtr=wcschr(&CmtBuf[0],0x1A);
if (ChPtr!=NULL)
- CmtSize=ChPtr-(char *)&CmtBuf[0];
- mprintf("\n");
- OutComment((char *)&CmtBuf[0],CmtSize);
+ CmtSize=ChPtr-&CmtBuf[0];
+ mprintf(L"\n");
+ OutComment(&CmtBuf[0],CmtSize);
}
-#endif
}
-#ifndef SFX_MODULE
-void Archive::ViewFileComment()
-{
- if (!(NewLhd.Flags & LHD_COMMENT) || Cmd->DisableComment || OldFormat)
- return;
-#ifndef GUI
- mprintf(St(MFileComment));
-#endif
- const int MaxSize=0x8000;
- Array<char> CmtBuf(MaxSize);
- SaveFilePos SavePos(*this);
- Seek(CurBlockPos+SIZEOF_NEWLHD+NewLhd.NameSize,SEEK_SET);
- Int64 SaveCurBlockPos=CurBlockPos;
- Int64 SaveNextBlockPos=NextBlockPos;
-
- int Size=ReadHeader();
-
- CurBlockPos=SaveCurBlockPos;
- NextBlockPos=SaveNextBlockPos;
-
- if (Size<7 || CommHead.HeadType!=COMM_HEAD)
- return;
- if (CommHead.HeadCRC!=HeaderCRC)
- {
-#ifndef GUI
- Log(FileName,St(MLogCommHead));
-#endif
- return;
- }
- if (CommHead.UnpVer < 15 || CommHead.UnpVer > UNP_VER ||
- CommHead.Method > 0x30 || CommHead.UnpSize > MaxSize)
- return;
- Read(&CmtBuf[0],CommHead.UnpSize);
- if (CommHead.CommCRC!=((~CRC(0xffffffff,&CmtBuf[0],CommHead.UnpSize)&0xffff)))
- {
- Log(FileName,St(MLogBrokFCmt));
- }
- else
- {
- OutComment(&CmtBuf[0],CommHead.UnpSize);
-#ifndef GUI
- mprintf("\n");
-#endif
- }
-}
-#endif
diff --git a/unrar/unrar/archive.cpp b/unrar/unrar/archive.cpp
index 3460f99..25f0c3b 100644
--- a/unrar/unrar/archive.cpp
+++ b/unrar/unrar/archive.cpp
@@ -1,143 +1,161 @@
#include "rar.hpp"
-#ifndef SHELL_EXT
#include "arccmt.cpp"
-#endif
-Archive::Archive(RAROptions *InitCmd)
+Archive::Archive(CommandData *InitCmd)
{
- Cmd=InitCmd==NULL ? &DummyCmd:InitCmd;
+ Cmd=NULL; // Just in case we'll have an exception in 'new' below.
+
+ DummyCmd=(InitCmd==NULL);
+ Cmd=DummyCmd ? (new CommandData):InitCmd;
+
OpenShared=Cmd->OpenShared;
- OldFormat=false;
+ Format=RARFMT_NONE;
Solid=false;
Volume=false;
MainComment=false;
Locked=false;
Signed=false;
- NotFirstVolume=false;
+ FirstVolume=false;
+ NewNumbering=false;
SFXSize=0;
LatestTime.Reset();
Protected=false;
Encrypted=false;
- BrokenFileHeader=false;
+ FailedHeaderDecryption=false;
+ BrokenHeader=false;
LastReadBlock=0;
CurBlockPos=0;
NextBlockPos=0;
- RecoveryPos=SIZEOF_MARKHEAD;
- RecoverySectors=-1;
- memset(&NewMhd,0,sizeof(NewMhd));
- NewMhd.HeadType=MAIN_HEAD;
- NewMhd.HeadSize=SIZEOF_NEWMHD;
- HeaderCRC=0;
+ MainHead.Reset();
+ CryptHead={};
+ EndArcHead.Reset();
+
+ VolNumber=0;
VolWrite=0;
AddingFilesSize=0;
AddingHeadersSize=0;
-#if !defined(SHELL_EXT) && !defined(NOCRYPT)
- *HeadersSalt=0;
- *SubDataSalt=0;
-#endif
*FirstVolumeName=0;
- *FirstVolumeNameW=0;
Splitting=false;
NewArchive=false;
SilentOpen=false;
+#ifdef USE_QOPEN
+ ProhibitQOpen=false;
+#endif
+
+}
+
+
+Archive::~Archive()
+{
+ if (DummyCmd)
+ delete Cmd;
}
-#ifndef SHELL_EXT
void Archive::CheckArc(bool EnableBroken)
{
if (!IsArchive(EnableBroken))
{
- Log(FileName,St(MBadArc),FileName);
- ErrHandler.Exit(FATAL_ERROR);
+ // If FailedHeaderDecryption is set, we already reported that archive
+ // password is incorrect.
+ if (!FailedHeaderDecryption)
+ uiMsg(UIERROR_BADARCHIVE,FileName);
+ ErrHandler.Exit(RARX_FATAL);
}
}
-#endif
-#if !defined(SHELL_EXT) && !defined(SFX_MODULE)
-void Archive::CheckOpen(char *Name,wchar *NameW)
+#if !defined(SFX_MODULE)
+void Archive::CheckOpen(const wchar *Name)
{
- TOpen(Name,NameW);
+ TOpen(Name);
CheckArc(false);
}
#endif
-bool Archive::WCheckOpen(char *Name,wchar *NameW)
+bool Archive::WCheckOpen(const wchar *Name)
{
- if (!WOpen(Name,NameW))
- return(false);
+ if (!WOpen(Name))
+ return false;
if (!IsArchive(false))
{
-#ifndef SHELL_EXT
- Log(FileName,St(MNotRAR),FileName);
-#endif
+ uiMsg(UIERROR_BADARCHIVE,FileName);
Close();
- return(false);
+ return false;
}
- return(true);
+ return true;
}
-bool Archive::IsSignature(byte *D)
+RARFORMAT Archive::IsSignature(const byte *D,size_t Size)
{
- bool Valid=false;
- if (D[0]==0x52)
+ RARFORMAT Type=RARFMT_NONE;
+ if (Size>=1 && D[0]==0x52)
#ifndef SFX_MODULE
- if (D[1]==0x45 && D[2]==0x7e && D[3]==0x5e)
- {
- OldFormat=true;
- Valid=true;
- }
+ if (Size>=4 && D[1]==0x45 && D[2]==0x7e && D[3]==0x5e)
+ Type=RARFMT14;
else
#endif
- if (D[1]==0x61 && D[2]==0x72 && D[3]==0x21 && D[4]==0x1a && D[5]==0x07 && D[6]==0x00)
+ if (Size>=7 && D[1]==0x61 && D[2]==0x72 && D[3]==0x21 && D[4]==0x1a && D[5]==0x07)
{
- OldFormat=false;
- Valid=true;
+ // We check the 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]>1 && D[6]<5)
+ Type=RARFMT_FUTURE;
}
- return(Valid);
+ return Type;
}
bool Archive::IsArchive(bool EnableBroken)
{
Encrypted=false;
+ BrokenHeader=false; // Might be left from previous volume.
+
#ifndef SFX_MODULE
if (IsDevice())
{
-#ifndef SHELL_EXT
- Log(FileName,St(MInvalidName),FileName);
-#endif
- return(false);
+ uiMsg(UIERROR_INVALIDNAME,FileName,FileName);
+ return false;
}
#endif
- if (Read(MarkHead.Mark,SIZEOF_MARKHEAD)!=SIZEOF_MARKHEAD)
- return(false);
+ if (Read(MarkHead.Mark,SIZEOF_MARKHEAD3)!=SIZEOF_MARKHEAD3)
+ return false;
SFXSize=0;
- if (IsSignature(MarkHead.Mark))
+
+ RARFORMAT Type;
+ if ((Type=IsSignature(MarkHead.Mark,SIZEOF_MARKHEAD3))!=RARFMT_NONE)
{
- if (OldFormat)
- Seek(0,SEEK_SET);
+ Format=Type;
+ if (Format==RARFMT14)
+ Seek(Tell()-SIZEOF_MARKHEAD3,SEEK_SET);
}
else
{
Array<char> Buffer(MAXSFXSIZE);
- long CurPos=int64to32(Tell());
+ long CurPos=(long)Tell();
int ReadSize=Read(&Buffer[0],Buffer.Size()-16);
for (int I=0;I<ReadSize;I++)
- if (Buffer[I]==0x52 && IsSignature((byte *)&Buffer[I]))
+ if (Buffer[I]==0x52 && (Type=IsSignature((byte *)&Buffer[I],ReadSize-I))!=RARFMT_NONE)
{
- if (OldFormat && I>0 && CurPos<28 && ReadSize>31)
+ Format=Type;
+ if (Format==RARFMT14 && I>0 && CurPos<28 && ReadSize>31)
{
char *D=&Buffer[28-CurPos];
if (D[0]!=0x52 || D[1]!=0x53 || D[2]!=0x46 || D[3]!=0x58)
@@ -145,98 +163,111 @@ bool Archive::IsArchive(bool EnableBroken)
}
SFXSize=CurPos+I;
Seek(SFXSize,SEEK_SET);
- if (!OldFormat)
- Read(MarkHead.Mark,SIZEOF_MARKHEAD);
+ if (Format==RARFMT15 || Format==RARFMT50)
+ Read(MarkHead.Mark,SIZEOF_MARKHEAD3);
break;
}
if (SFXSize==0)
- return(false);
+ return false;
}
- ReadHeader();
- SeekToNext();
-#ifndef SFX_MODULE
- if (OldFormat)
+ if (Format==RARFMT_FUTURE)
+ {
+ uiMsg(UIERROR_NEWRARFORMAT,FileName);
+ return false;
+ }
+ if (Format==RARFMT50) // RAR 5.0 signature is by one byte longer.
{
- NewMhd.Flags=OldMhd.Flags & 0x3f;
- NewMhd.HeadSize=OldMhd.HeadSize;
+ if (Read(MarkHead.Mark+SIZEOF_MARKHEAD3,1)!=1 || MarkHead.Mark[SIZEOF_MARKHEAD3]!=0)
+ return false;
+ MarkHead.HeadSize=SIZEOF_MARKHEAD5;
}
else
+ MarkHead.HeadSize=SIZEOF_MARKHEAD3;
+
+#ifdef RARDLL
+ // If callback function is not set, we cannot get the password,
+ // so we skip the initial header processing for encrypted header archive.
+ // It leads to skipped archive comment, but the rest of archive data
+ // is processed correctly.
+ if (Cmd->Callback==NULL)
+ SilentOpen=true;
#endif
+
+ bool HeadersLeft; // Any headers left to read.
+ bool StartFound=false; // Main or encryption headers found.
+ // Skip the archive encryption header if any and read the main header.
+ while ((HeadersLeft=(ReadHeader()!=0))==true) // Additional parentheses to silence Clang.
{
- if (HeaderCRC!=NewMhd.HeadCRC)
- {
-#ifndef SHELL_EXT
- Log(FileName,St(MLogMainHead));
-#endif
- Alarm();
- if (!EnableBroken)
- return(false);
- }
+ SeekToNext();
+
+ HEADER_TYPE Type=GetHeaderType();
+ // In RAR 5.0 we need to quit after reading HEAD_CRYPT if we wish to
+ // avoid the password prompt.
+ StartFound=Type==HEAD_MAIN || SilentOpen && Type==HEAD_CRYPT;
+ if (StartFound)
+ break;
}
- Volume=(NewMhd.Flags & MHD_VOLUME);
- Solid=(NewMhd.Flags & MHD_SOLID)!=0;
- MainComment=(NewMhd.Flags & MHD_COMMENT)!=0;
- Locked=(NewMhd.Flags & MHD_LOCK)!=0;
- Signed=(NewMhd.PosAV!=0);
- Protected=(NewMhd.Flags & MHD_PROTECT)!=0;
- Encrypted=(NewMhd.Flags & MHD_PASSWORD)!=0;
-
- if (NewMhd.EncryptVer>UNP_VER)
+
+
+ // We should not do it for EnableBroken or we'll get 'not RAR archive'
+ // messages when extracting encrypted archives with wrong password.
+ if (FailedHeaderDecryption && !EnableBroken)
+ return false;
+
+ if (BrokenHeader || !StartFound) // Main archive header is corrupt or missing.
{
-#ifdef RARDLL
- Cmd->DllError=ERAR_UNKNOWN_FORMAT;
-#else
- ErrHandler.SetErrorCode(WARNING);
- #if !defined(SILENT) && !defined(SFX_MODULE)
- Log(FileName,St(MUnknownMeth),FileName);
- Log(FileName,St(MVerRequired),NewMhd.EncryptVer/10,NewMhd.EncryptVer%10);
- #endif
-#endif
- return(false);
+ if (!FailedHeaderDecryption) // If not reported a wrong password already.
+ uiMsg(UIERROR_MHEADERBROKEN,FileName);
+ if (!EnableBroken)
+ return false;
}
-#ifdef RARDLL
- SilentOpen=true;
-#endif
- //if not encrypted, we'll check it below
- NotFirstVolume=Encrypted && (NewMhd.Flags & MHD_FIRSTVOLUME)==0;
+ MainComment=MainHead.CommentInHeader;
- if (!SilentOpen || !Encrypted)
+ // If we process non-encrypted archive or can request a password,
+ // we set 'first volume' flag based on file attributes below.
+ // It is necessary for RAR 2.x archives, which did not have 'first volume'
+ // flag in main header. Also for all RAR formats we need to scan until
+ // first file header to set "comment" flag when reading service header.
+ // Unless we are in silent mode, we need to know about presence of comment
+ // immediately after IsArchive call.
+ if (HeadersLeft && (!SilentOpen || !Encrypted) && IsSeekable())
{
- SaveFilePos SavePos(*this);
- Int64 SaveCurBlockPos=CurBlockPos,SaveNextBlockPos=NextBlockPos;
+ int64 SavePos=Tell();
+ int64 SaveCurBlockPos=CurBlockPos,SaveNextBlockPos=NextBlockPos;
+ HEADER_TYPE SaveCurHeaderType=CurHeaderType;
- NotFirstVolume=false;
- while (ReadHeader())
+ while (ReadHeader()!=0)
{
- int HeaderType=GetHeaderType();
- if (HeaderType==NEWSUB_HEAD)
+ HEADER_TYPE HeaderType=GetHeaderType();
+ if (HeaderType==HEAD_SERVICE)
{
- if (SubHead.CmpName(SUBHEAD_TYPE_CMT))
- MainComment=true;
- if ((SubHead.Flags & LHD_SPLIT_BEFORE) ||
- Volume && (NewMhd.Flags & MHD_FIRSTVOLUME)==0)
- NotFirstVolume=true;
+ // If we have a split service headers, it surely indicates non-first
+ // volume. But not split service header does not guarantee the first
+ // volume, because we can have split file after non-split archive
+ // comment. So we do not quit from loop here.
+ FirstVolume=Volume && !SubHead.SplitBefore;
}
else
- {
- if (HeaderType==FILE_HEAD && ((NewLhd.Flags & LHD_SPLIT_BEFORE)!=0 ||
- Volume && NewLhd.UnpVer>=29 && (NewMhd.Flags & MHD_FIRSTVOLUME)==0))
- NotFirstVolume=true;
- break;
- }
+ if (HeaderType==HEAD_FILE)
+ {
+ FirstVolume=Volume && !FileHead.SplitBefore;
+ break;
+ }
+ else
+ if (HeaderType==HEAD_ENDARC) // Might happen if archive contains only a split service header.
+ break;
SeekToNext();
}
CurBlockPos=SaveCurBlockPos;
NextBlockPos=SaveNextBlockPos;
+ CurHeaderType=SaveCurHeaderType;
+ Seek(SavePos,SEEK_SET);
}
- if (!Volume || !NotFirstVolume)
- {
- strcpy(FirstVolumeName,FileName);
- strcpyw(FirstVolumeNameW,FileNameW);
- }
+ if (!Volume || FirstVolume)
+ wcsncpyz(FirstVolumeName,FileName,ASIZE(FirstVolumeName));
- return(true);
+ return true;
}
@@ -248,20 +279,60 @@ void Archive::SeekToNext()
}
-#ifndef SFX_MODULE
-int Archive::GetRecoverySize(bool Required)
+
+
+
+
+// Calculate the block size including encryption fields and padding if any.
+uint Archive::FullHeaderSize(size_t Size)
{
- if (!Protected)
- return(0);
- if (RecoverySectors!=-1 || !Required)
- return(RecoverySectors);
- SaveFilePos SavePos(*this);
- Seek(SFXSize,SEEK_SET);
- SearchSubBlock(SUBHEAD_TYPE_RR);
- return(RecoverySectors);
+ if (Encrypted)
+ {
+ Size = ALIGN_VALUE(Size, CRYPT_BLOCK_SIZE); // Align to encryption block size.
+ if (Format == RARFMT50)
+ Size += SIZE_INITV;
+ else
+ Size += SIZE_SALT30;
+ }
+ return uint(Size);
}
-#endif
+#ifdef USE_QOPEN
+bool Archive::Open(const wchar *Name,uint Mode)
+{
+ // Important if we reuse Archive object and it has virtual QOpen
+ // file position not matching real. For example, for 'l -v volname'.
+ QOpen.Unload();
+
+ return File::Open(Name,Mode);
+}
+
+
+int Archive::Read(void *Data,size_t Size)
+{
+ size_t Result;
+ if (QOpen.Read(Data,Size,Result))
+ return (int)Result;
+ return File::Read(Data,Size);
+}
+
+
+void Archive::Seek(int64 Offset,int Method)
+{
+ if (!QOpen.Seek(Offset,Method))
+ File::Seek(Offset,Method);
+}
+
+
+int64 Archive::Tell()
+{
+ int64 QPos;
+ if (QOpen.Tell(&QPos))
+ return QPos;
+ return File::Tell();
+}
+#endif
+
diff --git a/unrar/unrar/archive.hpp b/unrar/unrar/archive.hpp
index a278aff..c0019ae 100644
--- a/unrar/unrar/archive.hpp
+++ b/unrar/unrar/archive.hpp
@@ -1,125 +1,147 @@
#ifndef _RAR_ARCHIVE_
#define _RAR_ARCHIVE_
-class Pack;
+class PPack;
+class RawRead;
+class RawWrite;
-enum {EN_LOCK=1,EN_VOL=2,EN_FIRSTVOL=4};
+enum NOMODIFY_FLAGS
+{
+ NMDF_ALLOWLOCK=1,NMDF_ALLOWANYVOLUME=2,NMDF_ALLOWFIRSTVOLUME=4
+};
+
+enum RARFORMAT {RARFMT_NONE,RARFMT14,RARFMT15,RARFMT50,RARFMT_FUTURE};
+
+enum ADDSUBDATA_FLAGS
+{
+ ASDF_SPLIT = 1, // Allow to split archive just before header if necessary.
+ ASDF_COMPRESS = 2, // Allow to compress data following subheader.
+ ASDF_CRYPT = 4, // Encrypt data after subheader if password is set.
+ ASDF_CRYPTIFHEADERS = 8 // Encrypt data after subheader only in -hp mode.
+};
+
+// RAR5 headers must not exceed 2 MB.
+#define MAX_HEADER_SIZE_RAR5 0x200000
class Archive:public File
{
private:
- bool IsSignature(byte *D);
void UpdateLatestTime(FileHeader *CurBlock);
- void ConvertNameCase(char *Name);
void ConvertNameCase(wchar *Name);
- void ConvertUnknownHeader();
- int ReadOldHeader();
-
-#if !defined(SHELL_EXT) && !defined(NOCRYPT)
+ void ConvertFileHeader(FileHeader *hd);
+ size_t ReadHeader14();
+ size_t ReadHeader15();
+ size_t ReadHeader50();
+ void ProcessExtra50(RawRead *Raw,size_t ExtraSize,const BaseBlock *bb);
+ void RequestArcPassword(RarCheckPassword *SelPwd);
+ void UnexpEndArcMsg();
+ void BrokenHeaderMsg();
+ void UnkEncVerMsg(const wchar *Name,const wchar *Info);
+ bool DoGetComment(Array<wchar> *CmtData);
+ bool ReadCommentData(Array<wchar> *CmtData);
+
+#if !defined(RAR_NOCRYPT)
CryptData HeadersCrypt;
- byte HeadersSalt[SALT_SIZE];
#endif
-#ifndef SHELL_EXT
ComprDataIO SubDataIO;
- byte SubDataSalt[SALT_SIZE];
-#endif
- RAROptions *Cmd,DummyCmd;
+ bool DummyCmd;
+ CommandData *Cmd;
- MarkHeader MarkHead;
- OldMainHeader OldMhd;
-
- int RecoverySectors;
- Int64 RecoveryPos;
RarTime LatestTime;
int LastReadBlock;
- int CurHeaderType;
+ HEADER_TYPE CurHeaderType;
bool SilentOpen;
+#ifdef USE_QOPEN
+ QuickOpen QOpen;
+ bool ProhibitQOpen;
+#endif
public:
- Archive(RAROptions *InitCmd=NULL);
+ Archive(CommandData *InitCmd=NULL);
+ ~Archive();
+ static RARFORMAT IsSignature(const byte *D,size_t Size);
bool IsArchive(bool EnableBroken);
- int SearchBlock(int BlockType);
- int SearchSubBlock(const char *Type);
- int ReadBlock(int BlockType);
- void WriteBlock(int BlockType,BaseBlock *wb=NULL);
- int PrepareNamesToWrite(char *Name,wchar *NameW,char *DestName,byte *DestNameW);
- void SetLhdSize();
- int ReadHeader();
+ size_t SearchBlock(HEADER_TYPE HeaderType);
+ size_t SearchSubBlock(const wchar *Type);
+ size_t SearchRR();
+ size_t ReadHeader();
void CheckArc(bool EnableBroken);
- void CheckOpen(char *Name,wchar *NameW=NULL);
- bool WCheckOpen(char *Name,wchar *NameW=NULL);
- bool TestLock(int Mode);
- void MakeTemp();
- void CopyMainHeader(Archive &Src,bool CopySFX=true,char *NameToDisplay=NULL);
- bool ProcessToFileHead(Archive &Src,bool LastBlockAdded,
- Pack *Pack=NULL,const char *SkipName=NULL);
- void TmpToArc(Archive &Src);
- void CloseNew(int AdjustRecovery,bool CloseVolume);
- void WriteEndBlock(bool CloseVolume);
- void CopyFileRecord(Archive &Src);
- void CopyArchiveData(Archive &Src);
- bool GetComment(Array<byte> *CmtData,Array<wchar> *CmtDataW);
+ void CheckOpen(const wchar *Name);
+ bool WCheckOpen(const wchar *Name);
+ bool GetComment(Array<wchar> *CmtData);
void ViewComment();
- void ViewFileComment();
void SetLatestTime(RarTime *NewTime);
void SeekToNext();
bool CheckAccess();
bool IsArcDir();
- bool IsArcLabel();
void ConvertAttributes();
- int GetRecoverySize(bool Required);
- void VolSubtractHeaderSize(int SubSize);
- void AddSubData(byte *SrcData,int DataSize,File *SrcFile,char *Name,bool AllowSplit);
- bool ReadSubData(Array<byte> *UnpData,File *DestFile);
- int GetHeaderType() {return(CurHeaderType);};
- int ReadCommentData(Array<byte> *CmtData,Array<wchar> *CmtDataW);
- void WriteCommentData(byte *Data,int DataSize,bool FileComment);
- RAROptions* GetRAROptions() {return(Cmd);}
+ void VolSubtractHeaderSize(size_t SubSize);
+ uint FullHeaderSize(size_t Size);
+ int64 GetStartPos();
+ void AddSubData(byte *SrcData,uint64 DataSize,File *SrcFile,
+ const wchar *Name,uint Flags);
+ bool ReadSubData(Array<byte> *UnpData,File *DestFile,bool TestMode);
+ HEADER_TYPE GetHeaderType() {return CurHeaderType;}
+ CommandData* GetCommandData() {return Cmd;}
void SetSilentOpen(bool Mode) {SilentOpen=Mode;}
+#if 0
+ void GetRecoveryInfo(bool Required,int64 *Size,int *Percent);
+#endif
+#ifdef USE_QOPEN
+ bool Open(const wchar *Name,uint Mode=FMF_READ);
+ int Read(void *Data,size_t Size);
+ void Seek(int64 Offset,int Method);
+ int64 Tell();
+ void QOpenUnload() {QOpen.Unload();}
+ void SetProhibitQOpen(bool Mode) {ProhibitQOpen=Mode;}
+#endif
BaseBlock ShortBlock;
- MainHeader NewMhd;
- FileHeader NewLhd;
+ MarkHeader MarkHead;
+ MainHeader MainHead;
+ CryptHeader CryptHead;
+ FileHeader FileHead;
EndArcHeader EndArcHead;
SubBlockHeader SubBlockHead;
FileHeader SubHead;
CommentHeader CommHead;
ProtectHeader ProtectHead;
- AVHeader AVHead;
- SignHeader SignHead;
- UnixOwnersHeader UOHead;
- MacFInfoHeader MACHead;
EAHeader EAHead;
StreamHeader StreamHead;
- Int64 CurBlockPos;
- Int64 NextBlockPos;
+ int64 CurBlockPos;
+ int64 NextBlockPos;
- bool OldFormat;
+ RARFORMAT Format;
bool Solid;
bool Volume;
bool MainComment;
bool Locked;
bool Signed;
- bool NotFirstVolume;
+ bool FirstVolume;
+ bool NewNumbering;
bool Protected;
bool Encrypted;
- uint SFXSize;
- bool BrokenFileHeader;
+ size_t SFXSize;
+ bool BrokenHeader;
+ bool FailedHeaderDecryption;
- bool Splitting;
+#if !defined(RAR_NOCRYPT)
+ byte ArcSalt[SIZE_SALT50];
+#endif
- ushort HeaderCRC;
+ bool Splitting;
- Int64 VolWrite;
- Int64 AddingFilesSize;
- uint AddingHeadersSize;
+ uint VolNumber;
+ int64 VolWrite;
+ uint64 AddingFilesSize;
+ uint64 AddingHeadersSize;
bool NewArchive;
- char FirstVolumeName[NM];
- wchar FirstVolumeNameW[NM];
+ wchar FirstVolumeName[NM];
};
+
#endif
diff --git a/unrar/unrar/arcread.cpp b/unrar/unrar/arcread.cpp
index 8dd03fa..6b7dd98 100644
--- a/unrar/unrar/arcread.cpp
+++ b/unrar/unrar/arcread.cpp
@@ -1,71 +1,162 @@
#include "rar.hpp"
-int Archive::SearchBlock(int BlockType)
+size_t Archive::ReadHeader()
{
- int Size,Count=0;
+ // Once we failed to decrypt an encrypted block, there is no reason to
+ // attempt to do it further. We'll never be successful and only generate
+ // endless errors.
+ if (FailedHeaderDecryption)
+ return 0;
+
+ CurBlockPos=Tell();
+
+ // Other developers asked us to initialize it to suppress "may be used
+ // uninitialized" warning in code below in some compilers.
+ size_t ReadSize=0;
+
+ switch(Format)
+ {
+#ifndef SFX_MODULE
+ case RARFMT14:
+ ReadSize=ReadHeader14();
+ break;
+#endif
+ case RARFMT15:
+ ReadSize=ReadHeader15();
+ break;
+ case RARFMT50:
+ ReadSize=ReadHeader50();
+ break;
+ }
+
+ // It is important to check ReadSize>0 here, because it is normal
+ // for RAR2 and RAR3 archives without end of archive block to have
+ // NextBlockPos==CurBlockPos after the end of archive has reached.
+ if (ReadSize>0 && NextBlockPos<=CurBlockPos)
+ {
+ BrokenHeaderMsg();
+ ReadSize=0;
+ }
+
+ if (ReadSize==0)
+ CurHeaderType=HEAD_UNKNOWN;
+
+ return ReadSize;
+}
+
+
+size_t Archive::SearchBlock(HEADER_TYPE HeaderType)
+{
+ size_t Size,Count=0;
while ((Size=ReadHeader())!=0 &&
- (BlockType==ENDARC_HEAD || GetHeaderType()!=ENDARC_HEAD))
+ (HeaderType==HEAD_ENDARC || GetHeaderType()!=HEAD_ENDARC))
{
if ((++Count & 127)==0)
Wait();
- if (GetHeaderType()==BlockType)
- return(Size);
+ if (GetHeaderType()==HeaderType)
+ return Size;
SeekToNext();
}
- return(0);
+ return 0;
}
-int Archive::SearchSubBlock(const char *Type)
+size_t Archive::SearchSubBlock(const wchar *Type)
{
- int Size;
- while ((Size=ReadHeader())!=0 && GetHeaderType()!=ENDARC_HEAD)
+ size_t Size,Count=0;
+ while ((Size=ReadHeader())!=0 && GetHeaderType()!=HEAD_ENDARC)
{
- if (GetHeaderType()==NEWSUB_HEAD && SubHead.CmpName(Type))
- return(Size);
+ if ((++Count & 127)==0)
+ Wait();
+ if (GetHeaderType()==HEAD_SERVICE && SubHead.CmpName(Type))
+ return Size;
SeekToNext();
}
- return(0);
+ return 0;
}
-int Archive::ReadHeader()
+size_t Archive::SearchRR()
{
- CurBlockPos=Tell();
+ // If locator extra field is available for recovery record, let's utilize it.
+ if (MainHead.Locator && MainHead.RROffset!=0)
+ {
+ uint64 CurPos=Tell();
+ Seek(MainHead.RROffset,SEEK_SET);
+ size_t Size=ReadHeader();
+ if (Size!=0 && !BrokenHeader && GetHeaderType()==HEAD_SERVICE && SubHead.CmpName(SUBHEAD_TYPE_RR))
+ return Size;
+ Seek(CurPos,SEEK_SET);
+ }
+ // Otherwise scan the entire archive to find the recovery record.
+ return SearchSubBlock(SUBHEAD_TYPE_RR);
+}
-#ifndef SFX_MODULE
- if (OldFormat)
- return(ReadOldHeader());
-#endif
+void Archive::UnexpEndArcMsg()
+{
+ int64 ArcSize=FileLength();
+
+ // If block positions are equal to file size, this is not an error.
+ // It can happen when we reached the end of older RAR 1.5 archive,
+ // which did not have the end of archive block.
+ // We can't replace this check by checking that read size is exactly 0
+ // in the beginning of file header, because in this case the read position
+ // still can be beyond the end of archive.
+ if (CurBlockPos!=ArcSize || NextBlockPos!=ArcSize)
+ {
+ uiMsg(UIERROR_UNEXPEOF,FileName);
+ ErrHandler.SetErrorCode(RARX_WARNING);
+ }
+}
+
+
+void Archive::BrokenHeaderMsg()
+{
+ uiMsg(UIERROR_HEADERBROKEN,FileName);
+ BrokenHeader=true;
+ ErrHandler.SetErrorCode(RARX_CRC);
+}
+
+
+void Archive::UnkEncVerMsg(const wchar *Name,const wchar *Info)
+{
+ uiMsg(UIERROR_UNKNOWNENCMETHOD,FileName,Name,Info);
+ ErrHandler.SetErrorCode(RARX_WARNING);
+}
+
+
+// Return f in case of signed integer overflow or negative parameters
+// or v1+v2 otherwise. We use it for file offsets, which are signed
+// for compatibility with off_t in POSIX file functions and third party code.
+// Signed integer overflow is the undefined behavior according to
+// C++ standard and it causes fuzzers to complain.
+inline int64 SafeAdd(int64 v1,int64 v2,int64 f)
+{
+ return v1>=0 && v2>=0 && v1<=MAX_INT64-v2 ? v1+v2 : f;
+}
+
+
+size_t Archive::ReadHeader15()
+{
RawRead Raw(this);
- bool Decrypt=Encrypted && CurBlockPos>=SFXSize+SIZEOF_MARKHEAD+SIZEOF_NEWMHD;
+ bool Decrypt=Encrypted && CurBlockPos>(int64)SFXSize+SIZEOF_MARKHEAD3;
if (Decrypt)
{
-#if defined(SHELL_EXT) || defined(NOCRYPT)
- return(0);
+#ifdef RAR_NOCRYPT // For rarext.dll and unrar_nocrypt.dll.
+ return 0;
#else
- if (Read(HeadersSalt,SALT_SIZE)!=SALT_SIZE)
- return(0);
- if (*Cmd->Password==0)
-#ifdef RARDLL
- if (Cmd->Callback==NULL ||
- Cmd->Callback(UCM_NEEDPASSWORD,Cmd->UserData,(LPARAM)Cmd->Password,sizeof(Cmd->Password))==-1)
- {
- Close();
- ErrHandler.Exit(USER_BREAK);
- }
+ RequestArcPassword(NULL);
-#else
- if (!GetPassword(PASSWORD_ARCHIVE,FileName,Cmd->Password,sizeof(Cmd->Password)))
- {
- Close();
- ErrHandler.Exit(USER_BREAK);
- }
-#endif
- HeadersCrypt.SetCryptKeys(Cmd->Password,HeadersSalt,false,false,NewMhd.EncryptVer>=36);
+ byte Salt[SIZE_SALT30];
+ if (Read(Salt,SIZE_SALT30)!=SIZE_SALT30)
+ {
+ UnexpEndArcMsg();
+ return 0;
+ }
+ HeadersCrypt.SetCryptKeys(false,CRYPT_RAR30,&Cmd->Password,Salt,NULL,0,NULL,NULL);
Raw.SetCrypt(&HeadersCrypt);
#endif
}
@@ -73,169 +164,244 @@ int Archive::ReadHeader()
Raw.Read(SIZEOF_SHORTBLOCKHEAD);
if (Raw.Size()==0)
{
- Int64 ArcSize=FileLength();
- if (CurBlockPos>ArcSize || NextBlockPos>ArcSize)
- {
- #ifndef SHELL_EXT
- Log(FileName,St(MLogUnexpEOF));
- #endif
- ErrHandler.SetErrorCode(WARNING);
- }
- return(0);
+ UnexpEndArcMsg();
+ return 0;
}
- Raw.Get(ShortBlock.HeadCRC);
- byte HeadType;
- Raw.Get(HeadType);
- ShortBlock.HeadType=(HEADER_TYPE)HeadType;
- Raw.Get(ShortBlock.Flags);
- Raw.Get(ShortBlock.HeadSize);
+ 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)
{
-#ifndef SHELL_EXT
- Log(FileName,St(MLogFileHead),"???");
-#endif
- BrokenFileHeader=true;
- ErrHandler.SetErrorCode(CRC_ERROR);
- return(0);
+ BrokenHeaderMsg();
+ return 0;
}
- if (ShortBlock.HeadType==COMM_HEAD)
+ // 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;
+ }
+ 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.HeadType==MAIN_HEAD && (ShortBlock.Flags & MHD_COMMENT)!=0)
- Raw.Read(SIZEOF_NEWMHD-SIZEOF_SHORTBLOCKHEAD);
+ 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;
+ NextBlockPos=CurBlockPos+FullHeaderSize(ShortBlock.HeadSize);
- switch(ShortBlock.HeadType)
+ switch(ShortBlock.HeaderType)
{
- case MAIN_HEAD:
- *(BaseBlock *)&NewMhd=ShortBlock;
- Raw.Get(NewMhd.HighPosAV);
- Raw.Get(NewMhd.PosAV);
- if (NewMhd.Flags & MHD_ENCRYPTVER)
- Raw.Get(NewMhd.EncryptVer);
- break;
- case ENDARC_HEAD:
- *(BaseBlock *)&EndArcHead=ShortBlock;
- if (EndArcHead.Flags & EARC_DATACRC)
- Raw.Get(EndArcHead.ArcDataCRC);
- if (EndArcHead.Flags & EARC_VOLNUMBER)
- Raw.Get(EndArcHead.VolNumber);
+ case HEAD_MAIN:
+ MainHead.Reset();
+ *(BaseBlock *)&MainHead=ShortBlock;
+ MainHead.HighPosAV=Raw.Get2();
+ MainHead.PosAV=Raw.Get4();
+
+ Volume=(MainHead.Flags & MHD_VOLUME)!=0;
+ Solid=(MainHead.Flags & MHD_SOLID)!=0;
+ Locked=(MainHead.Flags & MHD_LOCK)!=0;
+ Protected=(MainHead.Flags & MHD_PROTECT)!=0;
+ Encrypted=(MainHead.Flags & MHD_PASSWORD)!=0;
+ Signed=MainHead.PosAV!=0 || MainHead.HighPosAV!=0;
+ MainHead.CommentInHeader=(MainHead.Flags & MHD_COMMENT)!=0;
+
+ // Only for encrypted 3.0+ archives. 2.x archives did not have this
+ // flag, so for non-encrypted archives, we'll set it later based on
+ // file attributes.
+ FirstVolume=(MainHead.Flags & MHD_FIRSTVOLUME)!=0;
+
+ NewNumbering=(MainHead.Flags & MHD_NEWNUMBERING)!=0;
break;
- case FILE_HEAD:
- case NEWSUB_HEAD:
+ case HEAD_FILE:
+ case HEAD_SERVICE:
{
- FileHeader *hd=ShortBlock.HeadType==FILE_HEAD ? &NewLhd:&SubHead;
+ bool FileBlock=ShortBlock.HeaderType==HEAD_FILE;
+ FileHeader *hd=FileBlock ? &FileHead:&SubHead;
+ hd->Reset();
+
*(BaseBlock *)hd=ShortBlock;
- Raw.Get(hd->PackSize);
- Raw.Get(hd->UnpSize);
- Raw.Get(hd->HostOS);
- Raw.Get(hd->FileCRC);
- Raw.Get(hd->FileTime);
- Raw.Get(hd->UnpVer);
- Raw.Get(hd->Method);
- Raw.Get(hd->NameSize);
- Raw.Get(hd->FileAttr);
- if (hd->Flags & LHD_LARGE)
+
+ hd->SplitBefore=(hd->Flags & LHD_SPLIT_BEFORE)!=0;
+ hd->SplitAfter=(hd->Flags & LHD_SPLIT_AFTER)!=0;
+ hd->Encrypted=(hd->Flags & LHD_PASSWORD)!=0;
+ hd->SaltSet=(hd->Flags & LHD_SALT)!=0;
+
+ // RAR versions earlier than 2.0 do not set the solid flag
+ // in file header. They use only a global solid archive flag.
+ 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();
+
+ // RAR15 did not use the special dictionary size to mark dirs.
+ if (hd->UnpVer<20 && (hd->FileAttr & 0x10)!=0)
+ hd->Dir=true;
+
+ hd->CryptMethod=CRYPT_NONE;
+ if (hd->Encrypted)
+ switch(hd->UnpVer)
+ {
+ case 13: hd->CryptMethod=CRYPT_RAR13; break;
+ case 15: hd->CryptMethod=CRYPT_RAR15; break;
+ case 20:
+ case 26: hd->CryptMethod=CRYPT_RAR20; break;
+ default: hd->CryptMethod=CRYPT_RAR30; break;
+ }
+
+ 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)
{
- Raw.Get(hd->HighPackSize);
- Raw.Get(hd->HighUnpSize);
+ HighPackSize=Raw.Get4();
+ HighUnpSize=Raw.Get4();
+ hd->UnknownUnpSize=(LowUnpSize==0xffffffff && HighUnpSize==0xffffffff);
}
- else
+ else
{
- hd->HighPackSize=hd->HighUnpSize=0;
- if (hd->UnpSize==0xffffffff)
- {
- hd->UnpSize=int64to32(INT64MAX);
- hd->HighUnpSize=int64to32(INT64MAX>>32);
- }
+ 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->FullPackSize=int32to64(hd->HighPackSize,hd->PackSize);
- hd->FullUnpSize=int32to64(hd->HighUnpSize,hd->UnpSize);
+ hd->PackSize=INT32TO64(HighPackSize,hd->DataSize);
+ hd->UnpSize=INT32TO64(HighUnpSize,LowUnpSize);
+ if (hd->UnknownUnpSize)
+ hd->UnpSize=INT64NDF;
char FileName[NM*4];
- int NameSize=Min(hd->NameSize,sizeof(FileName)-1);
- Raw.Get((byte *)FileName,NameSize);
- FileName[NameSize]=0;
+ size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1);
+ Raw.GetB((byte *)FileName,ReadNameSize);
+ FileName[ReadNameSize]=0;
- strncpyz(hd->FileName,FileName,ASIZE(hd->FileName));
-
- if (hd->HeadType==NEWSUB_HEAD)
+ if (FileBlock)
{
- int DataSize=hd->HeadSize-hd->NameSize-SIZEOF_NEWLHD;
- if (hd->Flags & LHD_SALT)
- DataSize-=SALT_SIZE;
- if (DataSize>0)
+ *hd->FileName=0;
+ if ((hd->Flags & LHD_UNICODE)!=0)
{
- hd->SubData.Alloc(DataSize);
- Raw.Get(&hd->SubData[0],DataSize);
- if (hd->CmpName(SUBHEAD_TYPE_RR))
- {
- byte *D=&hd->SubData[8];
- RecoverySectors=D[0]+((uint)D[1]<<8)+((uint)D[2]<<16)+((uint)D[3]<<24);
- }
+ EncodeFileName NameCoder;
+ size_t Length=strlen(FileName);
+ Length++;
+ if (ReadNameSize>Length)
+ NameCoder.Decode(FileName,ReadNameSize,(byte *)FileName+Length,
+ ReadNameSize-Length,hd->FileName,
+ ASIZE(hd->FileName));
}
+
+ if (*hd->FileName==0)
+ ArcCharToWide(FileName,hd->FileName,ASIZE(hd->FileName),ACTW_OEM);
+
+#ifndef SFX_MODULE
+ ConvertNameCase(hd->FileName);
+#endif
+ ConvertFileHeader(hd);
}
else
- if (hd->HeadType==FILE_HEAD)
+ {
+ 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)
+ DataSize-=SIZE_SALT30;
+
+ if (DataSize>0)
{
- if (hd->Flags & LHD_UNICODE)
- {
- EncodeFileName NameCoder;
- int Length=strlen(FileName);
- if (Length==hd->NameSize)
- {
- UtfToWide(FileName,hd->FileNameW,sizeof(hd->FileNameW)/sizeof(hd->FileNameW[0])-1);
- WideToChar(hd->FileNameW,hd->FileName,sizeof(hd->FileName)/sizeof(hd->FileName[0])-1);
- ExtToInt(hd->FileName,hd->FileName);
- }
- else
- {
- Length++;
- NameCoder.Decode(FileName,(byte *)FileName+Length,
- hd->NameSize-Length,hd->FileNameW,
- sizeof(hd->FileNameW)/sizeof(hd->FileNameW[0]));
- }
- if (*hd->FileNameW==0)
- hd->Flags &= ~LHD_UNICODE;
- }
- else
- *hd->FileNameW=0;
-#ifndef SFX_MODULE
- ConvertNameCase(hd->FileName);
- ConvertNameCase(hd->FileNameW);
-#endif
- ConvertUnknownHeader();
+ // 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)
- Raw.Get(hd->Salt,SALT_SIZE);
- hd->mtime.SetDos(hd->FileTime);
- hd->ctime.Reset();
- hd->atime.Reset();
- hd->arctime.Reset();
- if (hd->Flags & LHD_EXTTIME)
+
+ if (hd->CmpName(SUBHEAD_TYPE_CMT))
+ MainComment=true;
+ }
+ if ((hd->Flags & LHD_SALT)!=0)
+ Raw.GetB(hd->Salt,SIZE_SALT30);
+ hd->mtime.SetDos(FileTime);
+ if ((hd->Flags & LHD_EXTTIME)!=0)
{
- ushort Flags;
- Raw.Get(Flags);
+ ushort Flags=Raw.Get2();
RarTime *tbl[4];
- tbl[0]=&NewLhd.mtime;
- tbl[1]=&NewLhd.ctime;
- tbl[2]=&NewLhd.atime;
- tbl[3]=&NewLhd.arctime;
+ 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)
+ if ((rmode & 8)==0 || CurTime==NULL)
continue;
if (I!=0)
{
- uint DosTime;
- Raw.Get(DosTime);
+ uint DosTime=Raw.Get4();
CurTime->SetDos(DosTime);
}
RarLocalTime rlt;
@@ -243,108 +409,90 @@ int Archive::ReadHeader()
if (rmode & 4)
rlt.Second++;
rlt.Reminder=0;
- int count=rmode&3;
- for (int J=0;J<count;J++)
+ uint count=rmode&3;
+ for (uint J=0;J<count;J++)
{
- byte CurByte;
- Raw.Get(CurByte);
+ byte CurByte=Raw.Get1();
rlt.Reminder|=(((uint)CurByte)<<((J+3-count)*8));
}
+ // Convert from 100ns RAR precision to REMINDER_PRECISION.
+ rlt.Reminder*=RarTime::REMINDER_PRECISION/10000000;
CurTime->SetLocal(&rlt);
}
}
- NextBlockPos+=hd->FullPackSize;
- bool CRCProcessedOnly=(hd->Flags & LHD_COMMENT)!=0;
- HeaderCRC=~Raw.GetCRC(CRCProcessedOnly)&0xffff;
+ // Set to 0 in case of overflow, so end of ReadHeader cares about it.
+ NextBlockPos=SafeAdd(NextBlockPos,hd->PackSize,0);
+
+ bool CRCProcessedOnly=hd->CommentInHeader;
+ ushort HeaderCRC=Raw.GetCRC15(CRCProcessedOnly);
if (hd->HeadCRC!=HeaderCRC)
{
- if (hd->HeadType==NEWSUB_HEAD)
- strcat(hd->FileName,"- ???");
- BrokenFileHeader=true;
- ErrHandler.SetErrorCode(WARNING);
-#ifndef SHELL_EXT
- Log(Archive::FileName,St(MLogFileHead),IntNameToExt(hd->FileName));
- Alarm();
-#endif
+ BrokenHeader=true;
+ ErrHandler.SetErrorCode(RARX_WARNING);
+
+ // If we have a broken encrypted header, we do not need to display
+ // the error message here, because it will be displayed for such
+ // headers later in this function. Also such headers are unlikely
+ // to have anything sensible in file name field, so it is useless
+ // to display the file name.
+ if (!Decrypt)
+ uiMsg(UIERROR_FHEADERBROKEN,Archive::FileName,hd->FileName);
}
}
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)
+ VolNumber=EndArcHead.VolNumber=Raw.Get2();
+ break;
#ifndef SFX_MODULE
- case COMM_HEAD:
+ case HEAD3_CMT:
*(BaseBlock *)&CommHead=ShortBlock;
- Raw.Get(CommHead.UnpSize);
- Raw.Get(CommHead.UnpVer);
- Raw.Get(CommHead.Method);
- Raw.Get(CommHead.CommCRC);
- break;
- case SIGN_HEAD:
- *(BaseBlock *)&SignHead=ShortBlock;
- Raw.Get(SignHead.CreationTime);
- Raw.Get(SignHead.ArcNameSize);
- Raw.Get(SignHead.UserNameSize);
- break;
- case AV_HEAD:
- *(BaseBlock *)&AVHead=ShortBlock;
- Raw.Get(AVHead.UnpVer);
- Raw.Get(AVHead.Method);
- Raw.Get(AVHead.AVVer);
- Raw.Get(AVHead.AVInfoCRC);
+ CommHead.UnpSize=Raw.Get2();
+ CommHead.UnpVer=Raw.Get1();
+ CommHead.Method=Raw.Get1();
+ CommHead.CommCRC=Raw.Get2();
break;
- case PROTECT_HEAD:
+ case HEAD3_PROTECT:
*(BaseBlock *)&ProtectHead=ShortBlock;
- Raw.Get(ProtectHead.DataSize);
- Raw.Get(ProtectHead.Version);
- Raw.Get(ProtectHead.RecSectors);
- Raw.Get(ProtectHead.TotalBlocks);
- Raw.Get(ProtectHead.Mark,8);
+ ProtectHead.DataSize=Raw.Get4();
+ ProtectHead.Version=Raw.Get1();
+ ProtectHead.RecSectors=Raw.Get2();
+ ProtectHead.TotalBlocks=Raw.Get4();
+ Raw.GetB(ProtectHead.Mark,8);
NextBlockPos+=ProtectHead.DataSize;
- RecoverySectors=ProtectHead.RecSectors;
break;
- case SUB_HEAD:
+ case HEAD3_OLDSERVICE: // RAR 2.9 and earlier.
*(BaseBlock *)&SubBlockHead=ShortBlock;
- Raw.Get(SubBlockHead.DataSize);
+ SubBlockHead.DataSize=Raw.Get4();
NextBlockPos+=SubBlockHead.DataSize;
- Raw.Get(SubBlockHead.SubType);
- Raw.Get(SubBlockHead.Level);
+ SubBlockHead.SubType=Raw.Get2();
+ SubBlockHead.Level=Raw.Get1();
switch(SubBlockHead.SubType)
{
- case UO_HEAD:
- *(SubBlockHeader *)&UOHead=SubBlockHead;
- Raw.Get(UOHead.OwnerNameSize);
- Raw.Get(UOHead.GroupNameSize);
- if (UOHead.OwnerNameSize>NM-1)
- UOHead.OwnerNameSize=NM-1;
- if (UOHead.GroupNameSize>NM-1)
- UOHead.GroupNameSize=NM-1;
- Raw.Get((byte *)UOHead.OwnerName,UOHead.OwnerNameSize);
- Raw.Get((byte *)UOHead.GroupName,UOHead.GroupNameSize);
- UOHead.OwnerName[UOHead.OwnerNameSize]=0;
- UOHead.GroupName[UOHead.GroupNameSize]=0;
- break;
- case MAC_HEAD:
- *(SubBlockHeader *)&MACHead=SubBlockHead;
- Raw.Get(MACHead.fileType);
- Raw.Get(MACHead.fileCreator);
- break;
- case EA_HEAD:
- case BEEA_HEAD:
case NTACL_HEAD:
*(SubBlockHeader *)&EAHead=SubBlockHead;
- Raw.Get(EAHead.UnpSize);
- Raw.Get(EAHead.UnpVer);
- Raw.Get(EAHead.Method);
- Raw.Get(EAHead.EACRC);
+ EAHead.UnpSize=Raw.Get4();
+ EAHead.UnpVer=Raw.Get1();
+ EAHead.Method=Raw.Get1();
+ EAHead.EACRC=Raw.Get4();
break;
case STREAM_HEAD:
*(SubBlockHeader *)&StreamHead=SubBlockHead;
- Raw.Get(StreamHead.UnpSize);
- Raw.Get(StreamHead.UnpVer);
- Raw.Get(StreamHead.Method);
- Raw.Get(StreamHead.StreamCRC);
- Raw.Get(StreamHead.StreamNameSize);
- if (StreamHead.StreamNameSize>NM-1)
- StreamHead.StreamNameSize=NM-1;
- Raw.Get((byte *)StreamHead.StreamName,StreamHead.StreamNameSize);
+ StreamHead.UnpSize=Raw.Get4();
+ StreamHead.UnpVer=Raw.Get1();
+ StreamHead.Method=Raw.Get1();
+ StreamHead.StreamCRC=Raw.Get4();
+ StreamHead.StreamNameSize=Raw.Get2();
+ if (StreamHead.StreamNameSize>=ASIZE(StreamHead.StreamName))
+ StreamHead.StreamNameSize=ASIZE(StreamHead.StreamName)-1;
+ Raw.GetB(StreamHead.StreamName,StreamHead.StreamNameSize);
StreamHead.StreamName[StreamHead.StreamNameSize]=0;
break;
}
@@ -352,326 +500,1018 @@ int Archive::ReadHeader()
#endif
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.
+ // Old Unix owners header didn't include string fields into header size,
+ // but included them into CRC, so it couldn't be verified with generic
+ // approach here.
+ if (ShortBlock.HeadCRC!=HeaderCRC && ShortBlock.HeaderType!=HEAD3_SIGN &&
+ ShortBlock.HeaderType!=HEAD3_AV &&
+ (ShortBlock.HeaderType!=HEAD3_OLDSERVICE || SubBlockHead.SubType!=UO_HEAD))
+ {
+ bool Recovered=false;
+ if (ShortBlock.HeaderType==HEAD_ENDARC && EndArcHead.RevSpace)
+ {
+ // Last 7 bytes of recovered volume can contain zeroes, because
+ // REV files store its own information (volume number, etc.) here.
+ int64 Length=Tell();
+ Seek(Length-7,SEEK_SET);
+ Recovered=true;
+ for (int J=0;J<7;J++)
+ if (GetByte()!=0)
+ Recovered=false;
+ }
+ if (!Recovered)
+ {
+ BrokenHeader=true;
+ ErrHandler.SetErrorCode(RARX_CRC);
+
+ if (Decrypt)
{
- uint DataSize;
- Raw.Get(DataSize);
- NextBlockPos+=DataSize;
+ uiMsg(UIERROR_CHECKSUMENC,FileName,FileName);
+ FailedHeaderDecryption=true;
+ return 0;
}
- break;
+ }
}
- HeaderCRC=~Raw.GetCRC(false)&0xffff;
- CurHeaderType=ShortBlock.HeadType;
+
+ return Raw.Size();
+}
+
+
+size_t Archive::ReadHeader50()
+{
+ RawRead Raw(this);
+
+ bool Decrypt=Encrypted && CurBlockPos>(int64)SFXSize+SIZEOF_MARKHEAD5;
+
if (Decrypt)
{
- NextBlockPos+=Raw.PaddedSize()+SALT_SIZE;
+#if defined(RAR_NOCRYPT)
+ return 0;
+#else
+
+ if (Cmd->SkipEncrypted)
+ {
+ uiMsg(UIMSG_SKIPENCARC,FileName);
+ FailedHeaderDecryption=true; // Suppress error messages and quit quietly.
+ return 0;
+ }
- if (ShortBlock.HeadCRC!=HeaderCRC)
+ byte HeadersInitV[SIZE_INITV];
+ if (Read(HeadersInitV,SIZE_INITV)!=SIZE_INITV)
{
- bool Recovered=false;
- if (ShortBlock.HeadType==ENDARC_HEAD && (EndArcHead.Flags & EARC_REVSPACE)!=0)
+ UnexpEndArcMsg();
+ return 0;
+ }
+
+ // We repeat the password request only for manually entered passwords
+ // and not for -p<pwd>. Wrong password can be intentionally provided
+ // in -p<pwd> to not stop batch processing for encrypted archives.
+ bool GlobalPassword=Cmd->Password.IsSet() || uiIsGlobalPasswordSet();
+
+ RarCheckPassword CheckPwd;
+ if (CryptHead.UsePswCheck && !BrokenHeader)
+ CheckPwd.Set(CryptHead.Salt,HeadersInitV,CryptHead.Lg2Count,CryptHead.PswCheck);
+
+ while (true) // Repeat the password prompt for wrong passwords.
+ {
+ RequestArcPassword(CheckPwd.IsSet() ? &CheckPwd:NULL);
+
+ byte PswCheck[SIZE_PSWCHECK];
+ HeadersCrypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,CryptHead.Salt,HeadersInitV,CryptHead.Lg2Count,NULL,PswCheck);
+ // Verify password validity. If header is damaged, we cannot rely on
+ // password check value, because it can be damaged too.
+ if (CryptHead.UsePswCheck && !BrokenHeader &&
+ memcmp(PswCheck,CryptHead.PswCheck,SIZE_PSWCHECK)!=0)
{
- // Last 7 bytes of recovered volume can contain zeroes, because
- // REV files store its own information (volume number, etc.) here.
- SaveFilePos SavePos(*this);
- Int64 Length=Tell();
- Seek(Length-7,SEEK_SET);
- Recovered=true;
- for (int J=0;J<7;J++)
- if (GetByte()!=0)
- Recovered=false;
+ if (GlobalPassword) // For -p<pwd> or Ctrl+P.
+ {
+ // This message is used by Android GUI to reset cached passwords.
+ // Update appropriate code if changed.
+ uiMsg(UIERROR_BADPSW,FileName,FileName);
+ FailedHeaderDecryption=true;
+ ErrHandler.SetErrorCode(RARX_BADPWD);
+ return 0;
+ }
+ else // For passwords entered manually.
+ {
+ // This message is used by Android GUI and Windows GUI and SFX to
+ // reset cached passwords. Update appropriate code if changed.
+ uiMsg(UIWAIT_BADPSW,FileName,FileName);
+ Cmd->Password.Clean();
+ }
+
+#ifdef RARDLL
+ // Avoid new requests for unrar.dll to prevent the infinite loop
+ // if app always returns the same password.
+ ErrHandler.SetErrorCode(RARX_BADPWD);
+ Cmd->DllError=ERAR_BAD_PASSWORD;
+ ErrHandler.Exit(RARX_BADPWD);
+#else
+ continue; // Request a password again.
+#endif
}
- if (!Recovered)
- {
-#ifndef SILENT
- Log(FileName,St(MEncrBadCRC),FileName);
+ break;
+ }
+
+ Raw.SetCrypt(&HeadersCrypt);
#endif
- Close();
+ }
- BrokenFileHeader=true;
- ErrHandler.SetErrorCode(CRC_ERROR);
- return(0);
-// ErrHandler.Exit(CRC_ERROR);
- }
+ // Header size must not occupy more than 3 variable length integer bytes
+ // resulting in 2 MB maximum header size (MAX_HEADER_SIZE_RAR5),
+ // 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)
+ {
+ UnexpEndArcMsg();
+ return 0;
+ }
+
+ ShortBlock.Reset();
+ ShortBlock.HeadCRC=Raw.Get4();
+ uint SizeBytes=Raw.GetVSize(4);
+ uint64 BlockSize=Raw.GetV();
+
+ if (BlockSize==0 || SizeBytes==0)
+ {
+ BrokenHeaderMsg();
+ return 0;
+ }
+
+ 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)
+ {
+ BrokenHeaderMsg();
+ return 0;
+ }
+
+ Raw.Read(SizeToRead);
+
+ if (Raw.Size()<HeaderSize)
+ {
+ UnexpEndArcMsg();
+ return 0;
+ }
+
+ 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)
+ {
+ BrokenHeaderMsg(); // Report, but attempt to process.
+
+ BrokenHeader=true;
+ ErrHandler.SetErrorCode(RARX_CRC);
+
+ if (Decrypt)
+ {
+ uiMsg(UIERROR_CHECKSUMENC,FileName,FileName);
+ FailedHeaderDecryption=true;
+ return 0;
+ }
+ }
+
+ uint64 ExtraSize=0;
+ if ((ShortBlock.Flags & HFL_EXTRA)!=0)
+ {
+ ExtraSize=Raw.GetV();
+ if (ExtraSize>=ShortBlock.HeadSize)
+ {
+ BrokenHeaderMsg();
+ return 0;
}
}
- if (NextBlockPos<=CurBlockPos)
+ uint64 DataSize=0;
+ if ((ShortBlock.Flags & HFL_DATA)!=0)
+ DataSize=Raw.GetV();
+
+ NextBlockPos=CurBlockPos+FullHeaderSize(ShortBlock.HeadSize);
+ // Set to 0 in case of overflow, so end of ReadHeader cares about it.
+ NextBlockPos=SafeAdd(NextBlockPos,DataSize,0);
+
+ switch(ShortBlock.HeaderType)
{
-#ifndef SHELL_EXT
- Log(FileName,St(MLogFileHead),"???");
+ case HEAD_CRYPT:
+ {
+ *(BaseBlock *)&CryptHead=ShortBlock;
+ uint CryptVersion=(uint)Raw.GetV();
+ if (CryptVersion>CRYPT_VERSION)
+ {
+ wchar Info[20];
+ swprintf(Info,ASIZE(Info),L"h%u",CryptVersion);
+ UnkEncVerMsg(FileName,Info);
+ return 0;
+ }
+ uint EncFlags=(uint)Raw.GetV();
+ CryptHead.UsePswCheck=(EncFlags & CHFL_CRYPT_PSWCHECK)!=0;
+ CryptHead.Lg2Count=Raw.Get1();
+ if (CryptHead.Lg2Count>CRYPT5_KDF_LG2_COUNT_MAX)
+ {
+ wchar Info[20];
+ swprintf(Info,ASIZE(Info),L"hc%u",CryptHead.Lg2Count);
+ UnkEncVerMsg(FileName,Info);
+ return 0;
+ }
+
+ Raw.GetB(CryptHead.Salt,SIZE_SALT50);
+ if (CryptHead.UsePswCheck)
+ {
+ Raw.GetB(CryptHead.PswCheck,SIZE_PSWCHECK);
+
+ byte csum[SIZE_PSWCHECK_CSUM];
+ Raw.GetB(csum,SIZE_PSWCHECK_CSUM);
+
+ sha256_context ctx;
+ sha256_init(&ctx);
+ sha256_process(&ctx, CryptHead.PswCheck, SIZE_PSWCHECK);
+
+ byte Digest[SHA256_DIGEST_SIZE];
+ sha256_done(&ctx, Digest);
+
+ CryptHead.UsePswCheck=memcmp(csum,Digest,SIZE_PSWCHECK_CSUM)==0;
+ }
+ Encrypted=true;
+ }
+ break;
+ case HEAD_MAIN:
+ {
+ MainHead.Reset();
+ *(BaseBlock *)&MainHead=ShortBlock;
+ uint ArcFlags=(uint)Raw.GetV();
+
+ Volume=(ArcFlags & MHFL_VOLUME)!=0;
+ Solid=(ArcFlags & MHFL_SOLID)!=0;
+ Locked=(ArcFlags & MHFL_LOCK)!=0;
+ Protected=(ArcFlags & MHFL_PROTECT)!=0;
+ Signed=false;
+ NewNumbering=true;
+
+ if ((ArcFlags & MHFL_VOLNUMBER)!=0)
+ VolNumber=(uint)Raw.GetV();
+ else
+ VolNumber=0;
+ FirstVolume=Volume && VolNumber==0;
+
+ if (ExtraSize!=0)
+ ProcessExtra50(&Raw,(size_t)ExtraSize,&MainHead);
+
+#ifdef USE_QOPEN
+ if (!ProhibitQOpen && MainHead.Locator && MainHead.QOpenOffset>0 && Cmd->QOpenMode!=QOPEN_NONE)
+ {
+ // We seek to QO block in the end of archive when processing
+ // QOpen.Load, so we need to preserve current block positions
+ // to not break normal archive processing by calling function.
+ int64 SaveCurBlockPos=CurBlockPos,SaveNextBlockPos=NextBlockPos;
+ HEADER_TYPE SaveCurHeaderType=CurHeaderType;
+
+ QOpen.Init(this,false);
+ QOpen.Load(MainHead.QOpenOffset);
+
+ CurBlockPos=SaveCurBlockPos;
+ NextBlockPos=SaveNextBlockPos;
+ CurHeaderType=SaveCurHeaderType;
+ }
+#endif
+ }
+ break;
+ case HEAD_FILE:
+ case HEAD_SERVICE:
+ {
+ FileHeader *hd=ShortBlock.HeaderType==HEAD_FILE ? &FileHead:&SubHead;
+ hd->Reset(); // Clear hash, time fields and other stuff like flags.
+ *(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.SetUnix((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;
+
+ // "+ 50" to not mix with old RAR format algorithms. For example,
+ // we may need to use the compression algorithm 15 in the future,
+ // but it was already used in RAR 1.5 and Unpack needs to distinguish
+ // them.
+ hd->UnpVer=(CompInfo & 0x3f) + 50;
+ if (hd->UnpVer!=50) // Only 5.0 compression is known now.
+ hd->UnpVer=VER_UNKNOWN;
+
+ 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);
+
+ 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));
+
+ // 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)
+ {
+#ifndef SFX_MODULE
+ ConvertNameCase(hd->FileName);
#endif
- BrokenFileHeader=true;
- ErrHandler.SetErrorCode(CRC_ERROR);
- return(0);
+ ConvertFileHeader(hd);
+ }
+
+ if (!FileBlock && hd->CmpName(SUBHEAD_TYPE_CMT))
+ MainComment=true;
+
+
+ if (BadCRC) // Add the file name to broken header message displayed above.
+ uiMsg(UIERROR_FHEADERBROKEN,Archive::FileName,hd->FileName);
+ }
+ 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;
}
- return(Raw.Size());
+
+ return Raw.Size();
}
-#ifndef SFX_MODULE
-int Archive::ReadOldHeader()
+#if !defined(RAR_NOCRYPT)
+void Archive::RequestArcPassword(RarCheckPassword *CheckPwd)
{
- RawRead Raw(this);
- if (CurBlockPos<=SFXSize)
+ if (!Cmd->Password.IsSet())
{
- Raw.Read(SIZEOF_OLDMHD);
- Raw.Get(OldMhd.Mark,4);
- Raw.Get(OldMhd.HeadSize);
- Raw.Get(OldMhd.Flags);
- NextBlockPos=CurBlockPos+OldMhd.HeadSize;
- CurHeaderType=MAIN_HEAD;
+#ifdef RARDLL
+ if (Cmd->Callback!=NULL)
+ {
+ wchar PasswordW[MAXPASSWORD];
+ *PasswordW=0;
+ if (Cmd->Callback(UCM_NEEDPASSWORDW,Cmd->UserData,(LPARAM)PasswordW,ASIZE(PasswordW))==-1)
+ *PasswordW=0;
+ if (*PasswordW==0)
+ {
+ char PasswordA[MAXPASSWORD];
+ *PasswordA=0;
+ if (Cmd->Callback(UCM_NEEDPASSWORD,Cmd->UserData,(LPARAM)PasswordA,ASIZE(PasswordA))==-1)
+ *PasswordA=0;
+ GetWideName(PasswordA,NULL,PasswordW,ASIZE(PasswordW));
+ cleandata(PasswordA,sizeof(PasswordA));
+ }
+ Cmd->Password.Set(PasswordW);
+ cleandata(PasswordW,sizeof(PasswordW));
+ }
+ if (!Cmd->Password.IsSet())
+ {
+ Close();
+ Cmd->DllError=ERAR_MISSING_PASSWORD;
+ ErrHandler.Exit(RARX_USERBREAK);
+ }
+#else
+ if (!uiGetPassword(UIPASSWORD_ARCHIVE,FileName,&Cmd->Password,CheckPwd))
+ {
+ Close();
+ uiMsg(UIERROR_INCERRCOUNT); // Prevent archive deleting if delete after extraction is on.
+ ErrHandler.Exit(RARX_USERBREAK);
+ }
+#endif
+ Cmd->ManualPassword=true;
}
- else
+}
+#endif
+
+
+void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,const 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;
+ Raw->SetPos(ExtraStart);
+ while (Raw->DataLeft()>=2)
{
- OldFileHeader OldLhd;
- Raw.Read(SIZEOF_OLDLHD);
- NewLhd.HeadType=FILE_HEAD;
- Raw.Get(NewLhd.PackSize);
- Raw.Get(NewLhd.UnpSize);
- Raw.Get(OldLhd.FileCRC);
- Raw.Get(NewLhd.HeadSize);
- Raw.Get(NewLhd.FileTime);
- Raw.Get(OldLhd.FileAttr);
- Raw.Get(OldLhd.Flags);
- Raw.Get(OldLhd.UnpVer);
- Raw.Get(OldLhd.NameSize);
- Raw.Get(OldLhd.Method);
-
- NewLhd.Flags=OldLhd.Flags|LONG_BLOCK;
- NewLhd.UnpVer=(OldLhd.UnpVer==2) ? 13 : 10;
- NewLhd.Method=OldLhd.Method+0x30;
- NewLhd.NameSize=OldLhd.NameSize;
- NewLhd.FileAttr=OldLhd.FileAttr;
- NewLhd.FileCRC=OldLhd.FileCRC;
- NewLhd.FullPackSize=NewLhd.PackSize;
- NewLhd.FullUnpSize=NewLhd.UnpSize;
-
- NewLhd.mtime.SetDos(NewLhd.FileTime);
- NewLhd.ctime.Reset();
- NewLhd.atime.Reset();
- NewLhd.arctime.Reset();
-
- Raw.Read(OldLhd.NameSize);
- Raw.Get((byte *)NewLhd.FileName,OldLhd.NameSize);
- NewLhd.FileName[OldLhd.NameSize]=0;
- ConvertNameCase(NewLhd.FileName);
- *NewLhd.FileNameW=0;
+ int64 FieldSize=Raw->GetV(); // Needs to be signed for check below and can be negative.
+ if (FieldSize<=0 || Raw->DataLeft()==0 || FieldSize>(int64)Raw->DataLeft())
+ break;
+ size_t NextPos=size_t(Raw->GetPos()+FieldSize);
+ uint64 FieldType=Raw->GetV();
- if (Raw.Size()!=0)
- NextBlockPos=CurBlockPos+NewLhd.HeadSize+NewLhd.PackSize;
- CurHeaderType=FILE_HEAD;
+ FieldSize=int64(NextPos-Raw->GetPos()); // Field size without size and type fields.
+
+ if (FieldSize<0) // FieldType is longer than expected extra field size.
+ break;
+
+ if (bb->HeaderType==HEAD_MAIN)
+ {
+ MainHeader *hd=(MainHeader *)bb;
+ switch(FieldType)
+ {
+ case 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;
+ }
+ }
+ break;
+ case MHEXTRA_METADATA:
+ {
+ uint Flags=(uint)Raw->GetV();
+ if ((Flags & MHEXTRA_METADATA_NAME)!=0)
+ {
+ uint64 NameSize=Raw->GetV();
+ if (NameSize>0 && NameSize<0x10000) // Prevent excessive allocation.
+ {
+ std::vector<char> NameU((size_t)NameSize); // UTF-8 name.
+ Raw->GetB(&NameU[0],(size_t)NameSize);
+ // If starts from 0, the name was longer than reserved space
+ // when saving this extra field.
+ if (NameU[0]!=0)
+ {
+ NameU.push_back(0);
+ std::vector<wchar> NameW(NameU.size()*4);
+ UtfToWide(&NameU[0],&NameW[0],NameW.size());
+ hd->OrigName.assign(&NameW[0]);
+ }
+ }
+ }
+ if ((Flags & MHEXTRA_METADATA_CTIME)!=0)
+ if ((Flags & MHEXTRA_METADATA_UNIXTIME)!=0)
+ if ((Flags & MHEXTRA_METADATA_UNIX_NS)!=0)
+ hd->OrigTime.SetUnixNS(Raw->Get8());
+ else
+ hd->OrigTime.SetUnix((time_t)Raw->Get4());
+ else
+ hd->OrigTime.SetWin(Raw->Get8());
+ }
+ break;
+ }
+ }
+
+ if (bb->HeaderType==HEAD_FILE || bb->HeaderType==HEAD_SERVICE)
+ {
+ FileHeader *hd=(FileHeader *)bb;
+ switch(FieldType)
+ {
+ case FHEXTRA_CRYPT:
+ {
+ FileHeader *hd=(FileHeader *)bb;
+ uint EncVersion=(uint)Raw->GetV();
+ if (EncVersion>CRYPT_VERSION)
+ {
+ wchar Info[20];
+ swprintf(Info,ASIZE(Info),L"x%u",EncVersion);
+ UnkEncVerMsg(hd->FileName,Info);
+ }
+ else
+ {
+ uint Flags=(uint)Raw->GetV();
+ hd->UsePswCheck=(Flags & FHEXTRA_CRYPT_PSWCHECK)!=0;
+ hd->UseHashKey=(Flags & FHEXTRA_CRYPT_HASHMAC)!=0;
+ hd->Lg2Count=Raw->Get1();
+ if (hd->Lg2Count>CRYPT5_KDF_LG2_COUNT_MAX)
+ {
+ wchar Info[20];
+ swprintf(Info,ASIZE(Info),L"xc%u",hd->Lg2Count);
+ UnkEncVerMsg(hd->FileName,Info);
+ }
+ Raw->GetB(hd->Salt,SIZE_SALT50);
+ Raw->GetB(hd->InitV,SIZE_INITV);
+ if (hd->UsePswCheck)
+ {
+ Raw->GetB(hd->PswCheck,SIZE_PSWCHECK);
+
+ // It is important to know if password check data is valid.
+ // If it is damaged and header CRC32 fails to detect it,
+ // archiver would refuse to decompress a possibly valid file.
+ // Since we want to be sure distinguishing a wrong password
+ // or corrupt file data, we use 64-bit password check data
+ // and to control its validity we use 32 bits of password
+ // check data SHA-256 additionally to 32-bit header CRC32.
+ byte csum[SIZE_PSWCHECK_CSUM];
+ Raw->GetB(csum,SIZE_PSWCHECK_CSUM);
+
+ sha256_context ctx;
+ sha256_init(&ctx);
+ sha256_process(&ctx, hd->PswCheck, SIZE_PSWCHECK);
+
+ byte Digest[SHA256_DIGEST_SIZE];
+ sha256_done(&ctx, Digest);
+
+ hd->UsePswCheck=memcmp(csum,Digest,SIZE_PSWCHECK_CSUM)==0;
+
+ // RAR 5.21 and earlier set PswCheck field in service records to 0
+ // even if UsePswCheck was present.
+ if (bb->HeaderType==HEAD_SERVICE && memcmp(hd->PswCheck,"\0\0\0\0\0\0\0\0",SIZE_PSWCHECK)==0)
+ hd->UsePswCheck=0;
+ }
+ hd->SaltSet=true;
+ hd->CryptMethod=CRYPT_RAR50;
+ hd->Encrypted=true;
+ }
+ }
+ break;
+ 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>=5)
+ {
+ byte Flags=(byte)Raw->GetV();
+ bool UnixTime=(Flags & FHEXTRA_HTIME_UNIXTIME)!=0;
+ if ((Flags & FHEXTRA_HTIME_MTIME)!=0)
+ if (UnixTime)
+ hd->mtime.SetUnix(Raw->Get4());
+ else
+ hd->mtime.SetWin(Raw->Get8());
+ if ((Flags & FHEXTRA_HTIME_CTIME)!=0)
+ if (UnixTime)
+ hd->ctime.SetUnix(Raw->Get4());
+ else
+ hd->ctime.SetWin(Raw->Get8());
+ if ((Flags & FHEXTRA_HTIME_ATIME)!=0)
+ if (UnixTime)
+ hd->atime.SetUnix((time_t)Raw->Get4());
+ else
+ hd->atime.SetWin(Raw->Get8());
+ if (UnixTime && (Flags & FHEXTRA_HTIME_UNIX_NS)!=0) // Add nanoseconds.
+ {
+ uint ns;
+ if ((Flags & FHEXTRA_HTIME_MTIME)!=0 && (ns=(Raw->Get4() & 0x3fffffff))<1000000000)
+ hd->mtime.Adjust(ns);
+ if ((Flags & FHEXTRA_HTIME_CTIME)!=0 && (ns=(Raw->Get4() & 0x3fffffff))<1000000000)
+ hd->ctime.Adjust(ns);
+ if ((Flags & FHEXTRA_HTIME_ATIME)!=0 && (ns=(Raw->Get4() & 0x3fffffff))<1000000000)
+ hd->atime.Adjust(ns);
+ }
+ }
+ break;
+ case FHEXTRA_VERSION:
+ if (FieldSize>=1)
+ {
+ Raw->GetV(); // Skip flags field.
+ uint Version=(uint)Raw->GetV();
+ if (Version!=0)
+ {
+ hd->Version=true;
+
+ wchar VerText[20];
+ swprintf(VerText,ASIZE(VerText),L";%u",Version);
+ wcsncatz(hd->FileName,VerText,ASIZE(hd->FileName));
+ }
+ }
+ 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=(gid_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:
+ {
+ // RAR 5.21 and earlier set FHEXTRA_SUBDATA size to 1 less than
+ // required. It did not hurt extraction, because UnRAR 5.21
+ // and earlier ignored this field and set FieldSize as data left
+ // in entire extra area. But now we set the correct field size
+ // and set FieldSize based on the actual extra record size,
+ // so we need to adjust it for those older archives here.
+ // FHEXTRA_SUBDATA in those archives always belongs to HEAD_SERVICE
+ // and always is last in extra area. So since its size is by 1
+ // less than needed, we always have 1 byte left in extra area,
+ // which fact we use here to detect such archives.
+ if (bb->HeaderType==HEAD_SERVICE && Raw->Size()-NextPos==1)
+ FieldSize++;
+
+ // We cannot allocate too much memory here, because above
+ // we check FieldSize againt Raw size and we control that Raw size
+ // is sensible when reading headers.
+ hd->SubData.Alloc((size_t)FieldSize);
+ Raw->GetB(hd->SubData.Addr(0),(size_t)FieldSize);
+ }
+ break;
+ }
+ }
+
+ Raw->SetPos(NextPos);
}
- return(NextBlockPos>CurBlockPos ? Raw.Size():0);
}
-#endif
-void Archive::ConvertNameCase(char *Name)
+#ifndef SFX_MODULE
+size_t Archive::ReadHeader14()
{
- if (Cmd->ConvertNames==NAMES_UPPERCASE)
+ RawRead Raw(this);
+ if (CurBlockPos<=(int64)SFXSize)
{
- IntToExt(Name,Name);
- strupper(Name);
- ExtToInt(Name,Name);
+ Raw.Read(SIZEOF_MAINHEAD14);
+ MainHead.Reset();
+ byte Mark[4];
+ Raw.GetB(Mark,4);
+ uint HeadSize=Raw.Get2();
+ if (HeadSize<7)
+ return false;
+ byte Flags=Raw.Get1();
+ NextBlockPos=CurBlockPos+HeadSize;
+ CurHeaderType=HEAD_MAIN;
+
+ Volume=(Flags & MHD_VOLUME)!=0;
+ Solid=(Flags & MHD_SOLID)!=0;
+ Locked=(Flags & MHD_LOCK)!=0;
+ MainHead.CommentInHeader=(Flags & MHD_COMMENT)!=0;
+ MainHead.PackComment=(Flags & MHD_PACK_COMMENT)!=0;
}
- if (Cmd->ConvertNames==NAMES_LOWERCASE)
+ else
{
- IntToExt(Name,Name);
- strlower(Name);
- ExtToInt(Name,Name);
+ 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();
+ if (FileHead.HeadSize<21)
+ return false;
+ 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;
+ FileHead.CryptMethod=FileHead.Encrypted ? CRYPT_RAR13:CRYPT_NONE;
+
+ FileHead.PackSize=FileHead.DataSize;
+ FileHead.WinSize=0x10000;
+ FileHead.Dir=(FileHead.FileAttr & 0x10)!=0;
+
+ FileHead.HostOS=HOST_MSDOS;
+ FileHead.HSType=HSYS_WINDOWS;
+
+ FileHead.mtime.SetDos(FileTime);
+
+ Raw.Read(NameSize);
+
+ char FileName[NM];
+ size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1);
+ Raw.GetB((byte *)FileName,ReadNameSize);
+ FileName[ReadNameSize]=0;
+ IntToExt(FileName,FileName,ASIZE(FileName));
+ CharToWide(FileName,FileHead.FileName,ASIZE(FileHead.FileName));
+ ConvertNameCase(FileHead.FileName);
+ ConvertFileHeader(&FileHead);
+
+ if (Raw.Size()!=0)
+ NextBlockPos=CurBlockPos+FileHead.HeadSize+FileHead.PackSize;
+ CurHeaderType=HEAD_FILE;
}
+ return NextBlockPos>CurBlockPos ? Raw.Size() : 0;
}
+#endif
#ifndef SFX_MODULE
void Archive::ConvertNameCase(wchar *Name)
{
if (Cmd->ConvertNames==NAMES_UPPERCASE)
- strupperw(Name);
+ wcsupper(Name);
if (Cmd->ConvertNames==NAMES_LOWERCASE)
- strlowerw(Name);
+ wcslower(Name);
}
#endif
bool Archive::IsArcDir()
{
- return((NewLhd.Flags & LHD_WINDOWMASK)==LHD_DIRECTORY);
-}
-
-
-bool Archive::IsArcLabel()
-{
- return(NewLhd.HostOS<=HOST_WIN32 && (NewLhd.FileAttr & 8));
+ return FileHead.Dir;
}
void Archive::ConvertAttributes()
{
-#if defined(_WIN_32) || defined(_EMX)
- switch(NewLhd.HostOS)
- {
- case HOST_MSDOS:
- case HOST_OS2:
- case HOST_WIN32:
- break;
- case HOST_UNIX:
- case HOST_BEOS:
- if ((NewLhd.Flags & LHD_WINDOWMASK)==LHD_DIRECTORY)
- NewLhd.FileAttr=0x10;
- else
- NewLhd.FileAttr=0x20;
- break;
- default:
- if ((NewLhd.Flags & LHD_WINDOWMASK)==LHD_DIRECTORY)
- NewLhd.FileAttr=0x10;
- else
- NewLhd.FileAttr=0x20;
- break;
- }
+#if defined(_WIN_ALL) || defined(_EMX)
+ if (FileHead.HSType!=HSYS_WINDOWS)
+ FileHead.FileAttr=FileHead.Dir ? 0x10 : 0x20;
#endif
#ifdef _UNIX
+ // umask defines which permission bits must not be set by default
+ // when creating a file or directory. The typical default value
+ // for the process umask is S_IWGRP | S_IWOTH (octal 022),
+ // resulting in 0644 mode for new files.
+ // Normally umask is applied automatically when creating a file,
+ // but we set attributes with chmod later, so we need to calculate
+ // resulting attributes here. We do it only for non-Unix archives.
+ // We restore native Unix attributes as is, because it can be backup.
static mode_t mask = (mode_t) -1;
if (mask == (mode_t) -1)
{
+ // umask call returns the current umask value. Argument (022) is not
+ // really important here.
mask = umask(022);
+
+ // Restore the original umask value, which was changed to 022 above.
umask(mask);
}
- switch(NewLhd.HostOS)
+
+ switch(FileHead.HSType)
{
- case HOST_MSDOS:
- case HOST_OS2:
- case HOST_WIN32:
- if (NewLhd.FileAttr & 0x10)
- NewLhd.FileAttr=0x41ff & ~mask;
- else
- if (NewLhd.FileAttr & 1)
- NewLhd.FileAttr=0x8124 & ~mask;
+ case HSYS_WINDOWS:
+ {
+ // Mapping MSDOS, OS/2 and Windows file attributes to Unix.
+
+ if (FileHead.FileAttr & 0x10) // FILE_ATTRIBUTE_DIRECTORY
+ {
+ // For directories we use 0777 mask.
+ FileHead.FileAttr=0777 & ~mask;
+ }
else
- NewLhd.FileAttr=0x81b6 & ~mask;
+ if (FileHead.FileAttr & 1) // FILE_ATTRIBUTE_READONLY
+ {
+ // For read only files we use 0444 mask with 'w' bits turned off.
+ FileHead.FileAttr=0444 & ~mask;
+ }
+ else
+ {
+ // umask does not set +x for regular files, so we use 0666
+ // instead of 0777 as for directories.
+ FileHead.FileAttr=0666 & ~mask;
+ }
+ }
break;
- case HOST_UNIX:
- case HOST_BEOS:
+ case HSYS_UNIX:
break;
default:
- if ((NewLhd.Flags & LHD_WINDOWMASK)==LHD_DIRECTORY)
- NewLhd.FileAttr=0x41ff & ~mask;
+ if (FileHead.Dir)
+ FileHead.FileAttr=0x41ff & ~mask;
else
- NewLhd.FileAttr=0x81b6 & ~mask;
+ FileHead.FileAttr=0x81b6 & ~mask;
break;
}
#endif
}
-void Archive::ConvertUnknownHeader()
+void Archive::ConvertFileHeader(FileHeader *hd)
{
- if (NewLhd.UnpVer<20 && (NewLhd.FileAttr & 0x10))
- NewLhd.Flags|=LHD_DIRECTORY;
- if (NewLhd.HostOS>=HOST_MAX)
- {
- if ((NewLhd.Flags & LHD_WINDOWMASK)==LHD_DIRECTORY)
- NewLhd.FileAttr=0x10;
+ if (hd->HSType==HSYS_UNKNOWN)
+ if (hd->Dir)
+ hd->FileAttr=0x10;
else
- NewLhd.FileAttr=0x20;
- }
- for (char *s=NewLhd.FileName;*s!=0;s=charnext(s))
+ hd->FileAttr=0x20;
+
+#ifdef _WIN_ALL
+ if (hd->HSType==HSYS_UNIX) // Convert Unix, OS X and Android decomposed chracters to Windows precomposed.
+ ConvertToPrecomposed(hd->FileName,ASIZE(hd->FileName));
+#endif
+
+ for (wchar *s=hd->FileName;*s!=0;s++)
{
- if (*s=='/' || *s=='\\')
- *s=CPATHDIVIDER;
-#if defined(_APPLE) && !defined(UNICODE_SUPPORTED)
- if ((byte)*s<32 || (byte)*s>127)
+#ifdef _UNIX
+ // Backslash is the invalid character for Windows file headers,
+ // but it can present in Unix file names extracted in Unix.
+ if (*s=='\\' && Format==RARFMT50 && hd->HSType==HSYS_WINDOWS)
*s='_';
#endif
-#if defined(_WIN_32) || defined(_EMX)
+#if defined(_WIN_ALL) || defined(_EMX)
+ // RAR 5.0 archives do not use '\' as path separator, so if we see it,
+ // it means that it is a part of Unix file name, which we cannot
+ // extract in Windows.
+ if (*s=='\\' && Format==RARFMT50)
+ *s='_';
+
// ':' in file names is allowed in Unix, but not in Windows.
// Even worse, file data will be written to NTFS stream on NTFS,
- // so automatic name correction on file create error in extraction
- // routine does not work. Let's better replace ':' now.
- if (NewLhd.HostOS==HOST_UNIX && *s==':')
+ // so automatic name correction on file create error in extraction
+ // routine does not work. In Windows and DOS versions we better
+ // replace ':' now.
+ if (*s==':')
*s='_';
#endif
+ // This code must be performed only after other path separator checks,
+ // because it produces backslashes illegal for some of checks above.
+ // Backslash is allowed in file names in Unix, but not in Windows.
+ // Still, RAR 4.x uses backslashes as path separator even in Unix.
+ // Forward slash is not allowed in both systems. In RAR 5.0 we use
+ // the forward slash as universal path separator.
+ if (*s=='/' || *s=='\\' && Format!=RARFMT50)
+ *s=CPATHDIVIDER;
}
+}
- for (wchar *s=NewLhd.FileNameW;*s!=0;s++)
- {
- if (*s=='/' || *s=='\\')
- *s=CPATHDIVIDER;
-#if defined(_WIN_32) || defined(_EMX)
- // ':' in file names is allowed in Unix, but not in Windows.
- // Even worse, file data will be written to NTFS stream on NTFS,
- // so automatic name correction on file create error in extraction
- // routine does not work. Let's better replace ':' now.
- if (NewLhd.HostOS==HOST_UNIX && *s==':')
- *s='_';
-#endif
- }
+int64 Archive::GetStartPos()
+{
+ int64 StartPos=SFXSize+MarkHead.HeadSize;
+ if (Format==RARFMT15)
+ StartPos+=MainHead.HeadSize;
+ else // RAR 5.0.
+ StartPos+=CryptHead.HeadSize+FullHeaderSize(MainHead.HeadSize);
+ return StartPos;
}
-#ifndef SHELL_EXT
-bool Archive::ReadSubData(Array<byte> *UnpData,File *DestFile)
+bool Archive::ReadSubData(Array<byte> *UnpData,File *DestFile,bool TestMode)
{
- if (HeaderCRC!=SubHead.HeadCRC)
+ if (BrokenHeader)
{
-#ifndef SHELL_EXT
- Log(FileName,St(MSubHeadCorrupt));
-#endif
- ErrHandler.SetErrorCode(CRC_ERROR);
- return(false);
+ uiMsg(UIERROR_SUBHEADERBROKEN,FileName);
+ ErrHandler.SetErrorCode(RARX_CRC);
+ return false;
}
- if (SubHead.Method<0x30 || SubHead.Method>0x35 || SubHead.UnpVer>/*PACK_VER*/36)
+ if (SubHead.Method>5 || SubHead.UnpVer>(Format==RARFMT50 ? VER_UNPACK5:VER_UNPACK))
{
-#ifndef SHELL_EXT
- Log(FileName,St(MSubHeadUnknown));
-#endif
- return(false);
+ uiMsg(UIERROR_SUBHEADERUNKNOWN,FileName);
+ return false;
}
- if (SubHead.PackSize==0 && (SubHead.Flags & LHD_SPLIT_AFTER)==0)
- return(true);
+ if (SubHead.PackSize==0 && !SubHead.SplitAfter)
+ return true;
SubDataIO.Init();
Unpack Unpack(&SubDataIO);
- Unpack.Init();
+ Unpack.Init(SubHead.WinSize,false);
if (DestFile==NULL)
{
- UnpData->Alloc(SubHead.UnpSize);
- SubDataIO.SetUnpackToMemory(&(*UnpData)[0],SubHead.UnpSize);
+ if (SubHead.UnpSize>0x1000000)
+ {
+ // Prevent the excessive allocation. When reading to memory, normally
+ // this function operates with reasonably small blocks, such as
+ // the archive comment, NTFS ACL or "Zone.Identifier" NTFS stream.
+ uiMsg(UIERROR_SUBHEADERUNKNOWN,FileName);
+ return false;
+ }
+ if (UnpData==NULL)
+ SubDataIO.SetTestMode(true);
+ else
+ {
+ UnpData->Alloc((size_t)SubHead.UnpSize);
+ SubDataIO.SetUnpackToMemory(&(*UnpData)[0],(uint)SubHead.UnpSize);
+ }
}
- if (SubHead.Flags & LHD_PASSWORD)
- if (*Cmd->Password)
- SubDataIO.SetEncryption(SubHead.UnpVer,Cmd->Password,
- (SubHead.Flags & LHD_SALT) ? SubHead.Salt:NULL,false,
- SubHead.UnpVer>=36);
+ if (SubHead.Encrypted)
+ if (Cmd->Password.IsSet())
+ SubDataIO.SetEncryption(false,SubHead.CryptMethod,&Cmd->Password,
+ SubHead.SaltSet ? SubHead.Salt:NULL,SubHead.InitV,
+ SubHead.Lg2Count,SubHead.HashKey,SubHead.PswCheck);
else
- return(false);
+ return false;
+ SubDataIO.UnpHash.Init(SubHead.FileHash.Type,1);
SubDataIO.SetPackedSizeToRead(SubHead.PackSize);
SubDataIO.EnableShowProgress(false);
SubDataIO.SetFiles(this,DestFile);
- SubDataIO.UnpVolume=(SubHead.Flags & LHD_SPLIT_AFTER);
+ SubDataIO.SetTestMode(TestMode);
+ SubDataIO.UnpVolume=SubHead.SplitAfter;
SubDataIO.SetSubHeader(&SubHead,NULL);
Unpack.SetDestSize(SubHead.UnpSize);
- if (SubHead.Method==0x30)
+ if (SubHead.Method==0)
CmdExtract::UnstoreFile(SubDataIO,SubHead.UnpSize);
else
Unpack.DoUnpack(SubHead.UnpVer,false);
- if (SubHead.FileCRC!=~SubDataIO.UnpFileCRC)
+ if (!SubDataIO.UnpHash.Cmp(&SubHead.FileHash,SubHead.UseHashKey ? SubHead.HashKey:NULL))
{
-#ifndef SHELL_EXT
- Log(FileName,St(MSubHeadDataCRC),SubHead.FileName);
-#endif
- ErrHandler.SetErrorCode(CRC_ERROR);
+ uiMsg(UIERROR_SUBHEADERDATABROKEN,FileName,SubHead.FileName);
+ ErrHandler.SetErrorCode(RARX_CRC);
if (UnpData!=NULL)
UnpData->Reset();
- return(false);
+ return false;
}
- return(true);
+ return true;
}
-#endif
diff --git a/unrar/unrar/array.hpp b/unrar/unrar/array.hpp
index 1130bfb..ac786f7 100644
--- a/unrar/unrar/array.hpp
+++ b/unrar/unrar/array.hpp
@@ -7,28 +7,38 @@ template <class T> class Array
{
private:
T *Buffer;
- int BufSize;
- int AllocSize;
+ size_t BufSize;
+ size_t AllocSize;
+ size_t MaxSize;
public:
Array();
- Array(int Size);
+ Array(size_t Size);
+ Array(const Array &Src); // Copy constructor.
~Array();
inline void CleanData();
- inline T& operator [](int Item);
- inline int Size();
- void Add(int Items);
- void Alloc(int Items);
+ inline T& operator [](size_t Item) const;
+ inline T* operator + (size_t Pos);
+ inline size_t Size(); // Returns the size in items, not in bytes.
+ void Add(size_t Items);
+ void Alloc(size_t Items);
void Reset();
- void operator = (Array<T> &Src);
+ void SoftReset();
+ Array<T>& operator = (const Array<T> &Src);
void Push(T Item);
- T* Addr() {return(Buffer);}
+ void Append(T *Item,size_t Count);
+ T* Addr(size_t Item) {return Buffer+Item;}
+ void SetMaxSize(size_t Size) {MaxSize=Size;}
+ T* Begin() {return Buffer;}
+ T* End() {return Buffer==NULL ? NULL:Buffer+BufSize;}
};
+
template <class T> void Array<T>::CleanData()
{
Buffer=NULL;
BufSize=0;
AllocSize=0;
+ MaxSize=0;
}
@@ -38,52 +48,72 @@ template <class T> Array<T>::Array()
}
-template <class T> Array<T>::Array(int Size)
+template <class T> Array<T>::Array(size_t Size)
{
- Buffer=(T *)rarmalloc(sizeof(T)*Size);
- if (Buffer==NULL && Size!=0)
- ErrHandler.MemoryError();
+ CleanData();
+ Add(Size);
+}
- AllocSize=BufSize=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);
+ free(Buffer);
+}
+
+
+template <class T> inline T& Array<T>::operator [](size_t Item) const
+{
+ return Buffer[Item];
}
-template <class T> inline T& Array<T>::operator [](int Item)
+template <class T> inline T* Array<T>::operator +(size_t Pos)
{
- return(Buffer[Item]);
+ return Buffer+Pos;
}
-template <class T> inline int Array<T>::Size()
+template <class T> inline size_t Array<T>::Size()
{
- return(BufSize);
+ return BufSize;
}
-template <class T> void Array<T>::Add(int Items)
+template <class T> void Array<T>::Add(size_t Items)
{
BufSize+=Items;
if (BufSize>AllocSize)
{
- int Suggested=AllocSize+AllocSize/4+32;
- int NewSize=Max(BufSize,Suggested);
+ if (MaxSize!=0 && BufSize>MaxSize)
+ {
+ ErrHandler.GeneralErrMsg(L"Maximum allowed array size (%u) is exceeded",MaxSize);
+ ErrHandler.MemoryError();
+ }
+
+ size_t Suggested=AllocSize+AllocSize/4+32;
+ size_t NewSize=Max(BufSize,Suggested);
- Buffer=(T *)rarrealloc(Buffer,NewSize*sizeof(T));
- if (Buffer==NULL)
+ T *NewBuffer=(T *)realloc(Buffer,NewSize*sizeof(T));
+ if (NewBuffer==NULL)
ErrHandler.MemoryError();
+ Buffer=NewBuffer;
AllocSize=NewSize;
}
}
-template <class T> void Array<T>::Alloc(int Items)
+template <class T> void Array<T>::Alloc(size_t Items)
{
if (Items>AllocSize)
Add(Items-BufSize);
@@ -96,7 +126,7 @@ template <class T> void Array<T>::Reset()
{
if (Buffer!=NULL)
{
- rarfree(Buffer);
+ free(Buffer);
Buffer=NULL;
}
BufSize=0;
@@ -104,12 +134,21 @@ template <class T> void Array<T>::Reset()
}
-template <class T> void Array<T>::operator =(Array<T> &Src)
+// 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> Array<T>& Array<T>::operator =(const Array<T> &Src)
{
Reset();
Alloc(Src.BufSize);
if (Src.BufSize!=0)
memcpy((void *)Buffer,(void *)Src.Buffer,Src.BufSize*sizeof(T));
+ return *this;
}
@@ -119,4 +158,12 @@ template <class T> void Array<T>::Push(T Item)
(*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
diff --git a/unrar/unrar/beosea.cpp b/unrar/unrar/beosea.cpp
deleted file mode 100644
index 86eb7d4..0000000
--- a/unrar/unrar/beosea.cpp
+++ /dev/null
@@ -1,113 +0,0 @@
-
-
-void ExtractBeEA(Archive &Arc,char *FileName)
-{
- if (Arc.HeaderCRC!=Arc.EAHead.HeadCRC)
- {
- Log(Arc.FileName,St(MEABroken),FileName);
- ErrHandler.SetErrorCode(CRC_ERROR);
- return;
- }
- if (Arc.EAHead.Method<0x31 || Arc.EAHead.Method>0x35 || Arc.EAHead.UnpVer>PACK_VER)
- {
- Log(Arc.FileName,St(MEAUnknHeader),FileName);
- return;
- }
-
- ComprDataIO DataIO;
- Unpack Unpack(&DataIO);
- Unpack.Init();
-
- Array<byte> UnpData(Arc.EAHead.UnpSize);
- DataIO.SetUnpackToMemory(&UnpData[0],Arc.EAHead.UnpSize);
- DataIO.SetPackedSizeToRead(Arc.EAHead.DataSize);
- DataIO.EnableShowProgress(false);
- DataIO.SetFiles(&Arc,NULL);
- Unpack.SetDestSize(Arc.EAHead.UnpSize);
- Unpack.DoUnpack(Arc.EAHead.UnpVer,false);
-
- if (Arc.EAHead.EACRC!=~DataIO.UnpFileCRC)
- {
- Log(Arc.FileName,St(MEABroken),FileName);
- ErrHandler.SetErrorCode(CRC_ERROR);
- return;
- }
- int fd = open(FileName,O_WRONLY);
- if (fd==-1)
- {
- Log(Arc.FileName,St(MCannotSetEA),FileName);
- ErrHandler.SetErrorCode(WARNING);
- return;
- }
-
- int AttrPos=0;
- while (AttrPos<Arc.EAHead.UnpSize)
- {
- unsigned char *CurItem=&UnpData[AttrPos];
- int NameSize=CurItem[0]+((int)CurItem[1]<<8);
- int Type=CurItem[2]+((int)CurItem[3]<<8)+((int)CurItem[4]<<16)+((int)CurItem[5]<<24);
- int Size=CurItem[6]+((int)CurItem[7]<<8)+((int)CurItem[8]<<16)+((int)CurItem[9]<<24);
- char Name[1024];
- if (NameSize>=sizeof(Name))
- {
- Log(Arc.FileName,St(MCannotSetEA),FileName);
- ErrHandler.SetErrorCode(WARNING);
- break;
- }
- memcpy(Name,CurItem+10,NameSize);
- Name[NameSize]=0;
- if (fs_write_attr(fd,Name,Type,0,CurItem+10+NameSize,Size)==-1)
- {
- Log(Arc.FileName,St(MCannotSetEA),FileName);
- ErrHandler.SetErrorCode(WARNING);
- break;
- }
- AttrPos+=10+NameSize+Size;
- }
- close(fd);
- mprintf(St(MShowEA));
-}
-
-
-void ExtractBeEANew(Archive &Arc,char *FileName)
-{
- Array<byte> SubData;
- if (!Arc.ReadSubData(&SubData,NULL))
- return;
-
- int fd = open(FileName,O_WRONLY);
- if (fd==-1)
- {
- Log(Arc.FileName,St(MCannotSetEA),FileName);
- ErrHandler.SetErrorCode(WARNING);
- return;
- }
-
- int AttrPos=0;
- while (AttrPos<Arc.EAHead.UnpSize)
- {
- unsigned char *CurItem=&SubData[AttrPos];
- int NameSize=CurItem[0]+((int)CurItem[1]<<8);
- int Type=CurItem[2]+((int)CurItem[3]<<8)+((int)CurItem[4]<<16)+((int)CurItem[5]<<24);
- int Size=CurItem[6]+((int)CurItem[7]<<8)+((int)CurItem[8]<<16)+((int)CurItem[9]<<24);
- char Name[1024];
- if (NameSize>=sizeof(Name))
- {
- Log(Arc.FileName,St(MCannotSetEA),FileName);
- ErrHandler.SetErrorCode(WARNING);
- break;
- }
- memcpy(Name,CurItem+10,NameSize);
- Name[NameSize]=0;
- if (fs_write_attr(fd,Name,Type,0,CurItem+10+NameSize,Size)==-1)
- {
- Log(Arc.FileName,St(MCannotSetEA),FileName);
- ErrHandler.SetErrorCode(WARNING);
- break;
- }
- AttrPos+=10+NameSize+Size;
- }
- close(fd);
- mprintf(St(MShowEA));
-}
-
diff --git a/unrar/unrar/blake2s.cpp b/unrar/unrar/blake2s.cpp
new file mode 100644
index 0000000..317603d
--- /dev/null
+++ b/unrar/unrar/blake2s.cpp
@@ -0,0 +1,183 @@
+// 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;
+}
+
+
+#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 = 0;
+ }
+ }
+}
+
+
+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 );
+}
+
diff --git a/unrar/unrar/blake2s.hpp b/unrar/unrar/blake2s.hpp
new file mode 100644
index 0000000..06e396a
--- /dev/null
+++ b/unrar/unrar/blake2s.hpp
@@ -0,0 +1,107 @@
+// Based on public domain code written in 2012 by Samuel Neves
+#ifndef _RAR_BLAKE2_
+#define _RAR_BLAKE2_
+
+#define BLAKE2_DIGEST_SIZE 32
+#define BLAKE2_THREADS_NUMBER 8
+
+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
+{
+ // Use constexpr instead of enums, because otherwise clang -std=c++20
+ // issues a warning about "arithmetic between different enumeration types"
+ // in ubuf[BLAKE_DATA_SIZE + BLAKE_ALIGNMENT] declaration.
+ static constexpr size_t BLAKE_ALIGNMENT = 64;
+
+ // buffer and uint32 h[8], t[2], f[2];
+ // 2 * BLAKE2S_BLOCKBYTES is the buf size in blake2_code_20140114.zip.
+ // It might differ in later versions.
+ static constexpr size_t 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
+
diff --git a/unrar/unrar/blake2s_sse.cpp b/unrar/unrar/blake2s_sse.cpp
new file mode 100644
index 0000000..1a02f21
--- /dev/null
+++ b/unrar/unrar/blake2s_sse.cpp
@@ -0,0 +1,129 @@
+// Based on public domain code written in 2012 by Samuel Neves
+
+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.
+// We also tried _mm_i32gather_epi32 and _mm256_i32gather_epi32 AVX2 gather
+// instructions here, but they did not show any speed gain on i7-6700K.
+#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;
+}
diff --git a/unrar/unrar/blake2sp.cpp b/unrar/unrar/blake2sp.cpp
new file mode 100644
index 0000000..da64588
--- /dev/null
+++ b/unrar/unrar/blake2sp.cpp
@@ -0,0 +1,153 @@
+/*
+ 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/>.
+*/
+
+#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 );
+}
diff --git a/unrar/unrar/cmddata.cpp b/unrar/unrar/cmddata.cpp
index 1f17cce..ffc5ff9 100644
--- a/unrar/unrar/cmddata.cpp
+++ b/unrar/unrar/cmddata.cpp
@@ -1,128 +1,159 @@
#include "rar.hpp"
+#include "cmdfilter.cpp"
+#include "cmdmix.cpp"
+
CommandData::CommandData()
{
- FileArgs=ExclArgs=InclArgs=StoreArgs=ArcNames=NULL;
Init();
}
-CommandData::~CommandData()
-{
- Close();
-}
-
-
void CommandData::Init()
{
- Close();
+ RAROptions::Init();
*Command=0;
*ArcName=0;
- *ArcNameW=0;
FileLists=false;
NoMoreSwitches=false;
- TimeConverted=false;
- FileArgs=new StringList;
- ExclArgs=new StringList;
- InclArgs=new StringList;
- StoreArgs=new StringList;
- ArcNames=new StringList;
+ ListMode=RCLM_AUTO;
+
+ BareOutput=false;
+
+
+ FileArgs.Reset();
+ ExclArgs.Reset();
+ InclArgs.Reset();
+ ArcNames.Reset();
+ StoreArgs.Reset();
+ Password.Clean();
+ NextVolSizes.clear();
}
-void CommandData::Close()
+// Return the pointer to next position in the string and store dynamically
+// allocated command line parameter in Par.
+static const wchar *AllocCmdParam(const wchar *CmdLine,wchar **Par)
{
- delete FileArgs;
- delete ExclArgs;
- delete InclArgs;
- delete StoreArgs;
- delete ArcNames;
- FileArgs=ExclArgs=InclArgs=StoreArgs=ArcNames=NULL;
- NextVolSizes.Reset();
+ const wchar *NextCmd=GetCmdParam(CmdLine,NULL,0);
+ if (NextCmd==NULL)
+ return NULL;
+ size_t ParSize=NextCmd-CmdLine+2; // Parameter size including the trailing zero.
+ *Par=(wchar *)malloc(ParSize*sizeof(wchar));
+ if (*Par==NULL)
+ return NULL;
+ return GetCmdParam(CmdLine,*Par,ParSize);
+}
+
+
+#if !defined(SFX_MODULE)
+void CommandData::ParseCommandLine(bool Preprocess,int argc, char *argv[])
+{
+ *Command=0;
+ NoMoreSwitches=false;
+#ifdef CUSTOM_CMDLINE_PARSER
+ // In Windows we may prefer to implement our own command line parser
+ // to avoid replacing \" by " in standard parser. Such replacing corrupts
+ // destination paths like "dest path\" in extraction commands.
+ const wchar *CmdLine=GetCommandLine();
+
+ wchar *Par;
+ for (bool FirstParam=true;;FirstParam=false)
+ {
+ if ((CmdLine=AllocCmdParam(CmdLine,&Par))==NULL)
+ break;
+ if (!FirstParam) // First parameter is the executable name.
+ if (Preprocess)
+ PreprocessArg(Par);
+ else
+ ParseArg(Par);
+ free(Par);
+ }
+#else
+ Array<wchar> Arg;
+ for (int I=1;I<argc;I++)
+ {
+ Arg.Alloc(strlen(argv[I])+1);
+ CharToWide(argv[I],&Arg[0],Arg.Size());
+ if (Preprocess)
+ PreprocessArg(&Arg[0]);
+ else
+ ParseArg(&Arg[0]);
+ }
+#endif
+ if (!Preprocess)
+ ParseDone();
}
+#endif
#if !defined(SFX_MODULE)
-void CommandData::ParseArg(char *Arg,wchar *ArgW)
+void CommandData::ParseArg(wchar *Arg)
{
if (IsSwitch(*Arg) && !NoMoreSwitches)
- if (Arg[1]=='-')
+ if (Arg[1]=='-' && Arg[2]==0)
NoMoreSwitches=true;
else
- ProcessSwitch(&Arg[1],(ArgW!=NULL && *ArgW!=0 ? &ArgW[1]:NULL));
+ ProcessSwitch(Arg+1);
else
if (*Command==0)
{
- strncpyz(Command,Arg,ASIZE(Command));
- if (ArgW!=NULL)
- strncpyw(CommandW,ArgW,sizeof(CommandW)/sizeof(CommandW[0]));
- if (etoupper(*Command)=='S')
+ wcsncpyz(Command,Arg,ASIZE(Command));
+
+
+ *Command=toupperw(*Command);
+ // 'I' and 'S' commands can contain case sensitive strings after
+ // the first character, so we must not modify their case.
+ // 'S' can contain SFX name, which case is important in Unix.
+ if (*Command!='I' && *Command!='S')
+ wcsupper(Command);
+ if (*Command=='P') // Enforce -idq for print command.
{
- const char *SFXName=Command[1] ? Command+1:DefSFXName;
- if (PointToName(SFXName)!=SFXName || FileExist(SFXName))
- strcpy(SFXModule,SFXName);
- else
- GetConfigName(SFXName,SFXModule,true);
+ MsgStream=MSG_ERRONLY;
+ SetConsoleMsgStream(MSG_ERRONLY);
}
-#ifndef GUI
- *Command=etoupper(*Command);
- if (*Command!='I' && *Command!='S')
- strupper(Command);
-#endif
}
else
if (*ArcName==0)
- {
- strncpyz(ArcName,Arg,ASIZE(ArcName));
- if (ArgW!=NULL)
- strncpyzw(ArcNameW,ArgW,ASIZE(ArcNameW));
- }
+ wcsncpyz(ArcName,Arg,ASIZE(ArcName));
else
{
- size_t Length=strlen(Arg);
- char EndChar=Length==0 ? 0:Arg[Length-1];
- char CmdChar=etoupper(*Command);
- bool Add=strchr("AFUM",CmdChar)!=NULL;
+ // Check if last character is the path separator.
+ size_t Length=wcslen(Arg);
+ wchar EndChar=Length==0 ? 0:Arg[Length-1];
+ bool EndSeparator=IsDriveDiv(EndChar) || IsPathDiv(EndChar);
+
+ wchar CmdChar=toupperw(*Command);
+ bool Add=wcschr(L"AFUM",CmdChar)!=NULL;
bool Extract=CmdChar=='X' || CmdChar=='E';
- if ((IsDriveDiv(EndChar) || IsPathDiv(EndChar)) && !Add)
- {
- strncpyz(ExtrPath,Arg,ASIZE(ExtrPath));
- if (ArgW!=NULL)
- strncpyzw(ExtrPathW,ArgW,ASIZE(ExtrPathW));
- }
+ bool Repair=CmdChar=='R' && Command[1]==0;
+ if (EndSeparator && !Add)
+ wcsncpyz(ExtrPath,Arg,ASIZE(ExtrPath));
else
- if ((Add || CmdChar=='T') && *Arg!='@')
- FileArgs->AddString(Arg);
+ if ((Add || CmdChar=='T') && (*Arg!='@' || ListMode==RCLM_REJECT_LISTS))
+ FileArgs.AddString(Arg);
else
{
- struct FindData FileData;
- bool Found=FindFile::FastFind(Arg,NULL,&FileData);
- if (!Found && *Arg=='@' && !IsWildcard(Arg))
+ FindData FileData;
+ bool Found=FindFile::FastFind(Arg,&FileData);
+ if ((!Found || ListMode==RCLM_ACCEPT_LISTS) &&
+ ListMode!=RCLM_REJECT_LISTS && *Arg=='@' && !IsWildcard(Arg+1))
{
FileLists=true;
- RAR_CHARSET Charset=FilelistCharset;
+ ReadTextFile(Arg+1,&FileArgs,false,true,FilelistCharset,true,true,true);
-#if defined(_WIN_32) && !defined(GUI)
- // for compatibility reasons we use OEM encoding
- // in Win32 console version by default
-
- if (Charset==RCH_DEFAULT)
- Charset=RCH_OEM;
-#endif
-
- ReadTextFile(Arg+1,FileArgs,false,true,Charset,true,true,true);
}
- else
- if (Found && FileData.IsDir && Extract && *ExtrPath==0)
+ else // We use 'destpath\' when extracting and reparing.
+ if (Found && FileData.IsDir && (Extract || Repair) && *ExtrPath==0)
{
- strcpy(ExtrPath,Arg);
- AddEndSlash(ExtrPath);
+ wcsncpyz(ExtrPath,Arg,ASIZE(ExtrPath));
+ AddEndSlash(ExtrPath,ASIZE(ExtrPath));
}
else
- FileArgs->AddString(Arg);
+ FileArgs.AddString(Arg);
}
}
}
@@ -131,252 +162,181 @@ void CommandData::ParseArg(char *Arg,wchar *ArgW)
void CommandData::ParseDone()
{
- if (FileArgs->ItemsCount()==0 && !FileLists)
- FileArgs->AddString(MASKALL);
- char CmdChar=etoupper(*Command);
- bool Extract=CmdChar=='X' || CmdChar=='E';
+ if (FileArgs.ItemsCount()==0 && !FileLists)
+ FileArgs.AddString(MASKALL);
+ wchar CmdChar=toupperw(Command[0]);
+ bool Extract=CmdChar=='X' || CmdChar=='E' || CmdChar=='P';
if (Test && Extract)
- Test=false;
- BareOutput=(CmdChar=='L' || CmdChar=='V') && Command[1]=='B';
+ Test=false; // Switch '-t' is senseless for 'X', 'E', 'P' commands.
+
+ // Suppress the copyright message and final end of line for 'lb' and 'vb'.
+ if ((CmdChar=='L' || CmdChar=='V') && Command[1]=='B')
+ BareOutput=true;
}
-#if !defined(SFX_MODULE) && !defined(_WIN_CE)
+#if !defined(SFX_MODULE)
void CommandData::ParseEnvVar()
{
char *EnvStr=getenv("RAR");
if (EnvStr!=NULL)
- ProcessSwitchesString(EnvStr);
+ {
+ Array<wchar> EnvStrW(strlen(EnvStr)+1);
+ CharToWide(EnvStr,&EnvStrW[0],EnvStrW.Size());
+ ProcessSwitchesString(&EnvStrW[0]);
+ }
}
#endif
-// return 'false' if -cfg- is present and preprocess switches
-// which must be processed before the rest of command line
-
-#ifndef SFX_MODULE
-bool CommandData::IsConfigEnabled(int argc,char *argv[])
+#if !defined(SFX_MODULE)
+// Preprocess those parameters, which must be processed before the rest of
+// command line. Return 'false' to stop further processing.
+void CommandData::PreprocessArg(const wchar *Arg)
{
- bool ConfigEnabled=true;
- for (int I=1;I<argc;I++)
- if (IsSwitch(*argv[I]))
+ if (IsSwitch(Arg[0]) && !NoMoreSwitches)
+ {
+ Arg++;
+ if (Arg[0]=='-' && Arg[1]==0) // Switch "--".
+ NoMoreSwitches=true;
+ if (wcsicomp(Arg,L"cfg-")==0)
+ ConfigDisabled=true;
+ if (wcsnicomp(Arg,L"ilog",4)==0)
{
- if (stricomp(&argv[I][1],"cfg-")==0)
- ConfigEnabled=false;
-#ifndef GUI
- if (strnicomp(&argv[I][1],"ilog",4)==0)
- {
- // ensure that correct log file name is already set
- // if we need to report an error when processing the command line
- ProcessSwitch(&argv[I][1]);
- InitLogOptions(LogName);
- }
-#endif
- if (strnicomp(&argv[I][1],"sc",2)==0)
- {
- // process -cs before reading any file lists
- ProcessSwitch(&argv[I][1]);
- }
+ // Ensure that correct log file name is already set
+ // if we need to report an error when processing the command line.
+ ProcessSwitch(Arg);
+ InitLogOptions(LogName,ErrlogCharset);
+ }
+ if (wcsnicomp(Arg,L"sc",2)==0)
+ {
+ // Process -sc before reading any file lists.
+ ProcessSwitch(Arg);
+ if (*LogName!=0)
+ InitLogOptions(LogName,ErrlogCharset);
}
- return(ConfigEnabled);
+ }
+ else
+ if (*Command==0)
+ wcsncpy(Command,Arg,ASIZE(Command)); // Need for rar.ini.
}
#endif
-#if !defined(GUI) && !defined(SFX_MODULE)
-void CommandData::ReadConfig(int argc,char *argv[])
+#if !defined(SFX_MODULE)
+void CommandData::ReadConfig()
{
StringList List;
if (ReadTextFile(DefConfigName,&List,true))
{
- char *Str;
+ wchar *Str;
while ((Str=List.GetString())!=NULL)
{
- while (isspace(*Str))
+ while (IsSpace(*Str))
Str++;
- if (strnicomp(Str,"switches=",9)==0)
+ if (wcsnicomp(Str,L"switches=",9)==0)
ProcessSwitchesString(Str+9);
+ if (*Command!=0)
+ {
+ wchar Cmd[16];
+ wcsncpyz(Cmd,Command,ASIZE(Cmd));
+ wchar C0=toupperw(Cmd[0]);
+ wchar C1=toupperw(Cmd[1]);
+ if (C0=='I' || C0=='L' || C0=='M' || C0=='S' || C0=='V')
+ Cmd[1]=0;
+ if (C0=='R' && (C1=='R' || C1=='V'))
+ Cmd[2]=0;
+ wchar SwName[16+ASIZE(Cmd)];
+ swprintf(SwName,ASIZE(SwName),L"switches_%ls=",Cmd);
+ size_t Length=wcslen(SwName);
+ if (wcsnicomp(Str,SwName,Length)==0)
+ ProcessSwitchesString(Str+Length);
+ }
}
}
}
#endif
-#if !defined(SFX_MODULE) && !defined(_WIN_CE)
-void CommandData::ProcessSwitchesString(char *Str)
+#if !defined(SFX_MODULE)
+void CommandData::ProcessSwitchesString(const wchar *Str)
{
- while (*Str)
+ wchar *Par;
+ while ((Str=AllocCmdParam(Str,&Par))!=NULL)
{
- while (!IsSwitch(*Str) && *Str!=0)
- Str++;
- if (*Str==0)
- break;
- char *Next=Str;
- while (!(Next[0]==' ' && IsSwitch(Next[1])) && *Next!=0)
- Next++;
- char NextChar=*Next;
- *Next=0;
- ProcessSwitch(Str+1);
- *Next=NextChar;
- Str=Next;
+ if (IsSwitch(*Par))
+ ProcessSwitch(Par+1);
+ free(Par);
}
}
#endif
#if !defined(SFX_MODULE)
-void CommandData::ProcessSwitch(char *Switch,wchar *SwitchW)
+void CommandData::ProcessSwitch(const wchar *Switch)
{
- switch(etoupper(Switch[0]))
+ switch(toupperw(Switch[0]))
{
- case 'I':
- if (strnicomp(&Switch[1],"LOG",3)==0)
- {
- strncpyz(LogName,Switch[4] ? Switch+4:DefLogName,ASIZE(LogName));
- break;
- }
- if (stricomp(&Switch[1],"SND")==0)
- {
- Sound=true;
- break;
- }
- if (stricomp(&Switch[1],"ERR")==0)
- {
- MsgStream=MSG_STDERR;
- break;
- }
- if (strnicomp(&Switch[1],"EML",3)==0)
- {
- strncpyz(EmailTo,Switch[4] ? Switch+4:"@",ASIZE(EmailTo));
- EmailTo[sizeof(EmailTo)-1]=0;
- break;
- }
- if (stricomp(&Switch[1],"NUL")==0)
- {
- MsgStream=MSG_NULL;
- break;
- }
- if (etoupper(Switch[1])=='D')
- {
- for (int I=2;Switch[I]!=0;I++)
- switch(etoupper(Switch[I]))
- {
- case 'Q':
- MsgStream=MSG_ERRONLY;
- break;
- case 'C':
- DisableCopyright=true;
- break;
- case 'D':
- DisableDone=true;
- break;
- case 'P':
- DisablePercentage=true;
- break;
- }
- break;
- }
- if (stricomp(&Switch[1],"OFF")==0)
- {
- Shutdown=true;
- break;
- }
- break;
- case 'T':
- switch(etoupper(Switch[1]))
- {
- case 'K':
- ArcTime=ARCTIME_KEEP;
- break;
- case 'L':
- ArcTime=ARCTIME_LATEST;
- break;
- case 'O':
- FileTimeBefore.SetAgeText(Switch+2);
- break;
- case 'N':
- FileTimeAfter.SetAgeText(Switch+2);
- break;
- case 'B':
- FileTimeBefore.SetIsoText(Switch+2);
- break;
- case 'A':
- FileTimeAfter.SetIsoText(Switch+2);
- break;
- case 'S':
- {
- EXTTIME_MODE Mode=EXTTIME_HIGH3;
- bool CommonMode=Switch[2]>='0' && Switch[2]<='4';
- if (CommonMode)
- Mode=(EXTTIME_MODE)(Switch[2]-'0');
- if (Switch[2]=='-')
- Mode=EXTTIME_NONE;
- if (CommonMode || Switch[2]=='-' || Switch[2]=='+' || Switch[2]==0)
- xmtime=xctime=xatime=Mode;
- else
- {
- if (Switch[3]>='0' && Switch[3]<='4')
- Mode=(EXTTIME_MODE)(Switch[3]-'0');
- if (Switch[3]=='-')
- Mode=EXTTIME_NONE;
- switch(etoupper(Switch[2]))
- {
- case 'M':
- xmtime=Mode;
- break;
- case 'C':
- xctime=Mode;
- break;
- case 'A':
- xatime=Mode;
- break;
- case 'R':
- xarctime=Mode;
- break;
- }
- }
- }
- break;
- case '-':
- Test=false;
- break;
- case 0:
- Test=true;
- break;
- default:
- BadSwitch(Switch);
- break;
- }
+ case '@':
+ ListMode=Switch[1]=='+' ? RCLM_ACCEPT_LISTS:RCLM_REJECT_LISTS;
break;
case 'A':
- switch(etoupper(Switch[1]))
+ switch(toupperw(Switch[1]))
{
case 'C':
ClearArc=true;
break;
case 'D':
- AppendArcNameToPath=true;
+ if (Switch[2]==0)
+ AppendArcNameToPath=APPENDARCNAME_DESTPATH;
+ else
+ if (Switch[2]=='1')
+ AppendArcNameToPath=APPENDARCNAME_OWNSUBDIR;
+ else
+ if (Switch[2]=='2')
+ AppendArcNameToPath=APPENDARCNAME_OWNDIR;
break;
+#ifndef SFX_MODULE
case 'G':
if (Switch[2]=='-' && Switch[3]==0)
GenerateArcName=0;
else
+ if (toupperw(Switch[2])=='F')
+ wcsncpyz(DefGenerateMask,Switch+3,ASIZE(DefGenerateMask));
+ else
+ {
+ GenerateArcName=true;
+ wcsncpyz(GenerateMask,Switch+2,ASIZE(GenerateMask));
+ }
+ break;
+#endif
+ case 'I':
+ IgnoreGeneralAttr=true;
+ break;
+ case 'M':
+ switch(toupperw(Switch[2]))
{
- GenerateArcName=true;
- strncpyz(GenerateMask,Switch+2,ASIZE(GenerateMask));
+ case 0:
+ case 'S':
+ ArcMetadata=ARCMETA_SAVE;
+ break;
+ case 'R':
+ ArcMetadata=ARCMETA_RESTORE;
+ break;
+ default:
+ BadSwitch(Switch);
+ break;
}
break;
- case 'N': //reserved for archive name
+ case 'N': // Reserved for archive name.
break;
case 'O':
AddArcOnly=true;
break;
case 'P':
- strcpy(ArcPath,Switch+2);
- if (SwitchW!=NULL && *SwitchW!=0)
- strcpyw(ArcPathW,SwitchW+2);
+ wcsncpyz(ArcPath,Switch+2,ASIZE(ArcPath));
break;
case 'S':
SyncFiles=true;
@@ -386,9 +346,24 @@ void CommandData::ProcessSwitch(char *Switch,wchar *SwitchW)
break;
}
break;
+ case 'C':
+ if (Switch[2]==0)
+ switch(toupperw(Switch[1]))
+ {
+ case '-':
+ DisableComment=true;
+ break;
+ case 'U':
+ ConvertNames=NAMES_UPPERCASE;
+ break;
+ case 'L':
+ ConvertNames=NAMES_LOWERCASE;
+ break;
+ }
+ break;
case 'D':
if (Switch[2]==0)
- switch(etoupper(Switch[1]))
+ switch(toupperw(Switch[1]))
{
case 'S':
DisableSortSolid=true;
@@ -401,98 +376,8 @@ void CommandData::ProcessSwitch(char *Switch,wchar *SwitchW)
break;
}
break;
- case 'O':
- switch(etoupper(Switch[1]))
- {
- case '+':
- Overwrite=OVERWRITE_ALL;
- break;
- case '-':
- Overwrite=OVERWRITE_NONE;
- break;
- case 0:
- Overwrite=OVERWRITE_FORCE_ASK;
- break;
- case 'R':
- Overwrite=OVERWRITE_AUTORENAME;
- break;
- case 'W':
- ProcessOwners=true;
- break;
-#ifdef SAVE_LINKS
- case 'L':
- SaveLinks=true;
- break;
-#endif
-#ifdef _WIN_32
- case 'S':
- SaveStreams=true;
- break;
- case 'C':
- SetCompressedAttr=true;
- break;
-#endif
- default :
- BadSwitch(Switch);
- break;
- }
- break;
- case 'R':
- switch(etoupper(Switch[1]))
- {
- case 0:
- Recurse=RECURSE_ALWAYS;
- break;
- case '-':
- Recurse=0;
- break;
- case '0':
- Recurse=RECURSE_WILDCARDS;
- break;
-#ifndef _WIN_CE
- case 'I':
- {
- Priority=atoi(Switch+2);
- char *ChPtr=strchr(Switch+2,':');
- if (ChPtr!=NULL)
- {
- SleepTime=atoi(ChPtr+1);
- InitSystemOptions(SleepTime);
- }
- SetPriority(Priority);
- }
- break;
-#endif
- }
- break;
- case 'Y':
- AllYes=true;
- break;
- case 'N':
- case 'X':
- if (Switch[1]!=0)
- {
- StringList *Args=etoupper(Switch[0])=='N' ? InclArgs:ExclArgs;
- if (Switch[1]=='@' && !IsWildcard(Switch))
- {
- RAR_CHARSET Charset=FilelistCharset;
-
-#if defined(_WIN_32) && !defined(GUI)
- // for compatibility reasons we use OEM encoding
- // in Win32 console version by default
-
- if (Charset==RCH_DEFAULT)
- Charset=RCH_OEM;
-#endif
-
- ReadTextFile(Switch+2,Args,false,true,Charset,true,true,true);
- }
- else
- Args->AddString(Switch+1);
- }
- break;
case 'E':
- switch(etoupper(Switch[1]))
+ switch(toupperw(Switch[1]))
{
case 'P':
switch(Switch[2])
@@ -509,90 +394,186 @@ void CommandData::ProcessSwitch(char *Switch,wchar *SwitchW)
case '3':
ExclPath=EXCL_ABSPATH;
break;
+ case '4':
+ wcsncpyz(ExclArcPath,Switch+3,ASIZE(ExclArcPath));
+ break;
}
break;
- case 'D':
- ExclEmptyDir=true;
- break;
- case 'E':
- ProcessEA=false;
- break;
- case 'N':
- NoEndBlock=true;
- break;
default:
if (Switch[1]=='+')
{
- InclFileAttr=GetExclAttr(&Switch[2]);
+ InclFileAttr|=GetExclAttr(Switch+2,InclDir);
InclAttrSet=true;
}
else
- ExclFileAttr=GetExclAttr(&Switch[1]);
+ ExclFileAttr|=GetExclAttr(Switch+1,ExclDir);
break;
}
break;
- case 'P':
+ case 'F':
if (Switch[1]==0)
- {
- GetPassword(PASSWORD_GLOBAL,NULL,Password,sizeof(Password));
- eprintf("\n");
- }
+ FreshFiles=true;
else
- strncpyz(Password,Switch+1,ASIZE(Password));
+ BadSwitch(Switch);
break;
case 'H':
- if (etoupper(Switch[1])=='P')
+ switch (toupperw(Switch[1]))
{
- EncryptHeaders=true;
- if (Switch[2]!=0)
- strncpyz(Password,Switch+2,ASIZE(Password));
- else
- if (*Password==0)
+ case 'P':
+ EncryptHeaders=true;
+ if (Switch[2]!=0)
{
- GetPassword(PASSWORD_GLOBAL,NULL,Password,sizeof(Password));
- eprintf("\n");
+ if (wcslen(Switch+2)>=MAXPASSWORD)
+ uiMsg(UIERROR_TRUNCPSW,MAXPASSWORD-1);
+ Password.Set(Switch+2);
+ cleandata((void *)Switch,wcslen(Switch)*sizeof(Switch[0]));
}
+ else
+ if (!Password.IsSet())
+ {
+ uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password,NULL);
+ eprintf(L"\n");
+ }
+ break;
+ default :
+ BadSwitch(Switch);
+ break;
}
break;
- case 'Z':
- strncpyz(CommentFile,Switch[1]!=0 ? Switch+1:"stdin",ASIZE(CommentFile));
+ case 'I':
+ if (wcsnicomp(Switch+1,L"LOG",3)==0)
+ {
+ wcsncpyz(LogName,Switch[4]!=0 ? Switch+4:DefLogName,ASIZE(LogName));
+ break;
+ }
+ if (wcsnicomp(Switch+1,L"SND",3)==0)
+ {
+ Sound=Switch[4]=='-' ? SOUND_NOTIFY_OFF : SOUND_NOTIFY_ON;
+ break;
+ }
+ if (wcsicomp(Switch+1,L"ERR")==0)
+ {
+ MsgStream=MSG_STDERR;
+ // Set it immediately when parsing the command line, so it also
+ // affects messages issued while parsing the command line.
+ SetConsoleMsgStream(MSG_STDERR);
+ break;
+ }
+ if (wcsnicomp(Switch+1,L"EML",3)==0)
+ {
+ wcsncpyz(EmailTo,Switch[4]!=0 ? Switch+4:L"@",ASIZE(EmailTo));
+ break;
+ }
+ if (wcsicomp(Switch+1,L"M")==0) // For compatibility with pre-WinRAR 6.0 -im syntax. Replaced with -idv.
+ {
+ VerboseOutput=true;
+ break;
+ }
+ if (wcsicomp(Switch+1,L"NUL")==0)
+ {
+ MsgStream=MSG_NULL;
+ SetConsoleMsgStream(MSG_NULL);
+ break;
+ }
+ if (toupperw(Switch[1])=='D')
+ {
+ for (uint I=2;Switch[I]!=0;I++)
+ switch(toupperw(Switch[I]))
+ {
+ case 'Q':
+ MsgStream=MSG_ERRONLY;
+ SetConsoleMsgStream(MSG_ERRONLY);
+ break;
+ case 'C':
+ DisableCopyright=true;
+ break;
+ case 'D':
+ DisableDone=true;
+ break;
+ case 'P':
+ DisablePercentage=true;
+ break;
+ case 'N':
+ DisableNames=true;
+ break;
+ case 'V':
+ VerboseOutput=true;
+ break;
+ }
+ break;
+ }
+ if (wcsnicomp(Switch+1,L"OFF",3)==0)
+ {
+ switch(Switch[4])
+ {
+ case 0:
+ case '1':
+ Shutdown=POWERMODE_OFF;
+ break;
+ case '2':
+ Shutdown=POWERMODE_HIBERNATE;
+ break;
+ case '3':
+ Shutdown=POWERMODE_SLEEP;
+ break;
+ case '4':
+ Shutdown=POWERMODE_RESTART;
+ break;
+ }
+ break;
+ }
+ if (wcsicomp(Switch+1,L"VER")==0)
+ {
+ PrintVersion=true;
+ break;
+ }
+ break;
+ case 'K':
+ switch(toupperw(Switch[1]))
+ {
+ case 'B':
+ KeepBroken=true;
+ break;
+ case 0:
+ Lock=true;
+ break;
+ }
break;
case 'M':
- switch(etoupper(Switch[1]))
+ switch(toupperw(Switch[1]))
{
case 'C':
{
- char *Str=Switch+2;
+ const wchar *Str=Switch+2;
if (*Str=='-')
- for (int I=0;I<sizeof(FilterModes)/sizeof(FilterModes[0]);I++)
+ for (uint I=0;I<ASIZE(FilterModes);I++)
FilterModes[I].State=FILTER_DISABLE;
else
- while (*Str)
+ while (*Str!=0)
{
int Param1=0,Param2=0;
FilterState State=FILTER_AUTO;
FilterType Type=FILTER_NONE;
- if (isdigit(*Str))
+ if (IsDigit(*Str))
{
- Param1=atoi(Str);
- while (isdigit(*Str))
+ Param1=atoiw(Str);
+ while (IsDigit(*Str))
Str++;
}
- if (*Str==':' && isdigit(Str[1]))
+ if (*Str==':' && IsDigit(Str[1]))
{
- Param2=atoi(++Str);
- while (isdigit(*Str))
+ Param2=atoiw(++Str);
+ while (IsDigit(*Str))
Str++;
}
- switch(etoupper(*(Str++)))
+ switch(toupperw(*(Str++)))
{
case 'T': Type=FILTER_PPM; break;
case 'E': Type=FILTER_E8; break;
case 'D': Type=FILTER_DELTA; break;
case 'A': Type=FILTER_AUDIO; break;
case 'C': Type=FILTER_RGB; break;
- case 'I': Type=FILTER_ITANIUM; break;
- case 'L': Type=FILTER_UPCASETOLOW; break;
+ case 'R': Type=FILTER_ARM; break;
}
if (*Str=='+' || *Str=='-')
State=*(Str++)=='+' ? FILTER_FORCE:FILTER_DISABLE;
@@ -605,46 +586,39 @@ void CommandData::ProcessSwitch(char *Switch,wchar *SwitchW)
case 'M':
break;
case 'D':
- {
- if ((WinSize=atoi(&Switch[2]))==0)
- WinSize=0x10000<<(etoupper(Switch[2])-'A');
- else
- WinSize*=1024;
- if (!CheckWinSize())
- BadSwitch(Switch);
- }
+ break;
+ case 'E':
+ if (toupperw(Switch[2])=='S' && Switch[3]==0)
+ SkipEncrypted=true;
break;
case 'S':
{
- char *Names=Switch+2,DefNames[512];
- if (*Names==0)
- {
- strcpy(DefNames,DefaultStoreList);
- Names=DefNames;
- }
+ wchar StoreNames[1024];
+ wcsncpyz(StoreNames,(Switch[2]==0 ? DefaultStoreList:Switch+2),ASIZE(StoreNames));
+ wchar *Names=StoreNames;
while (*Names!=0)
{
- char *End=strchr(Names,';');
+ wchar *End=wcschr(Names,';');
if (End!=NULL)
*End=0;
if (*Names=='.')
Names++;
- char Mask[NM];
- if (strpbrk(Names,"*?.")==NULL)
- sprintf(Mask,"*.%s",Names);
+ wchar Mask[NM];
+ if (wcspbrk(Names,L"*?.")==NULL)
+ swprintf(Mask,ASIZE(Mask),L"*.%ls",Names);
else
- strcpy(Mask,Names);
- StoreArgs->AddString(Mask);
+ wcsncpyz(Mask,Names,ASIZE(Mask));
+ StoreArgs.AddString(Mask);
if (End==NULL)
break;
Names=End+1;
}
}
break;
-#ifdef PACK_SMP
+#ifdef RAR_SMP
case 'T':
- Threads=atoi(Switch+2);
- if (Threads>MaxSearchThreads)
+ Threads=atoiw(Switch+2);
+ if (Threads>MaxPoolThreads || Threads<1)
BadSwitch(Switch);
else
{
@@ -658,121 +632,146 @@ void CommandData::ProcessSwitch(char *Switch,wchar *SwitchW)
break;
}
break;
- case 'V':
- switch(etoupper(Switch[1]))
+ case 'N':
+ case 'X':
+ if (Switch[1]!=0)
{
-#ifdef _WIN_32
- case 'D':
- EraseDisk=true;
+ StringList *Args=toupperw(Switch[0])=='N' ? &InclArgs:&ExclArgs;
+ if (Switch[1]=='@' && !IsWildcard(Switch))
+ ReadTextFile(Switch+2,Args,false,true,FilelistCharset,true,true,true);
+ else
+ Args->AddString(Switch+1);
+ }
+ break;
+ case 'O':
+ switch(toupperw(Switch[1]))
+ {
+ case '+':
+ Overwrite=OVERWRITE_ALL;
+ break;
+ case '-':
+ Overwrite=OVERWRITE_NONE;
+ break;
+ case 0:
+ Overwrite=OVERWRITE_FORCE_ASK;
+ break;
+#ifdef _WIN_ALL
+ case 'C':
+ SetCompressedAttr=true;
break;
#endif
+ case 'H':
+ SaveHardLinks=true;
+ break;
+
+
+#ifdef SAVE_LINKS
+ case 'L':
+ SaveSymLinks=true;
+ if (toupperw(Switch[2])=='A')
+ AbsoluteLinks=true;
+ break;
+#endif
+#ifdef _WIN_ALL
case 'N':
- OldNumbering=true;
+ if (toupperw(Switch[2])=='I')
+ AllowIncompatNames=true;
break;
+#endif
case 'P':
- VolumePause=true;
+ wcsncpyz(ExtrPath,Switch+2,ASIZE(ExtrPath));
+ AddEndSlash(ExtrPath,ASIZE(ExtrPath));
break;
- case 'E':
- if (etoupper(Switch[2])=='R')
- VersionControl=atoi(Switch+3)+1;
+ case 'R':
+ Overwrite=OVERWRITE_AUTORENAME;
break;
- case '-':
- VolSize=0;
+#ifdef _WIN_ALL
+ case 'S':
+ SaveStreams=true;
break;
- default:
- {
- Int64 NewVolSize=atoil(&Switch[1]);
-
- if (NewVolSize==0)
- NewVolSize=INT64ERR;
- else
- switch (Switch[strlen(Switch)-1])
- {
- case 'f':
- case 'F':
- switch(int64to32(NewVolSize))
- {
- case 360:
- NewVolSize=362496;
- break;
- case 720:
- NewVolSize=730112;
- break;
- case 1200:
- NewVolSize=1213952;
- break;
- case 1440:
- NewVolSize=1457664;
- break;
- case 2880:
- NewVolSize=2915328;
- break;
- }
- break;
- case 'k':
- NewVolSize*=1024;
- break;
- case 'm':
- NewVolSize*=1024*1024;
- break;
- case 'M':
- NewVolSize*=1000*1000;
- break;
- case 'g':
- NewVolSize*=1024*1024;
- NewVolSize*=1024;
- break;
- case 'G':
- NewVolSize*=1000*1000;
- NewVolSize*=1000;
- break;
- case 'b':
- case 'B':
- break;
- default:
- NewVolSize*=1000;
- break;
- }
- if (VolSize==0)
- VolSize=NewVolSize;
- else
- NextVolSizes.Push(NewVolSize);
- }
+#endif
+ case 'W':
+ ProcessOwners=true;
+ break;
+ default :
+ BadSwitch(Switch);
break;
}
break;
- case 'F':
+ case 'P':
if (Switch[1]==0)
- FreshFiles=true;
+ {
+ uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password,NULL);
+ eprintf(L"\n");
+ }
else
- BadSwitch(Switch);
+ {
+ if (wcslen(Switch+1)>=MAXPASSWORD)
+ uiMsg(UIERROR_TRUNCPSW,MAXPASSWORD-1);
+ Password.Set(Switch+1);
+ cleandata((void *)Switch,wcslen(Switch)*sizeof(Switch[0]));
+ }
break;
- case 'U':
- if (Switch[1]==0)
- UpdateFiles=true;
+#ifndef SFX_MODULE
+ case 'Q':
+ if (toupperw(Switch[1])=='O')
+ switch(toupperw(Switch[2]))
+ {
+ case 0:
+ QOpenMode=QOPEN_AUTO;
+ break;
+ case '-':
+ QOpenMode=QOPEN_NONE;
+ break;
+ case '+':
+ QOpenMode=QOPEN_ALWAYS;
+ break;
+ default:
+ BadSwitch(Switch);
+ break;
+ }
else
BadSwitch(Switch);
break;
- case 'W':
- strncpyz(TempPath,&Switch[1],ASIZE(TempPath));
- AddEndSlash(TempPath);
- break;
- case 'S':
- if (strnicomp(Switch,"SFX",3)==0)
+#endif
+ case 'R':
+ switch(toupperw(Switch[1]))
{
- const char *SFXName=Switch[3] ? Switch+3:DefSFXName;
- if (PointToName(SFXName)!=SFXName || FileExist(SFXName))
- strcpy(SFXModule,SFXName);
- else
- GetConfigName(SFXName,SFXModule,true);
+ case 0:
+ Recurse=RECURSE_ALWAYS;
+ break;
+ case '-':
+ Recurse=RECURSE_DISABLE;
+ break;
+ case '0':
+ Recurse=RECURSE_WILDCARDS;
+ break;
+ case 'I':
+ {
+ Priority=atoiw(Switch+2);
+ if (Priority<0 || Priority>15)
+ BadSwitch(Switch);
+ const wchar *ChPtr=wcschr(Switch+2,':');
+ if (ChPtr!=NULL)
+ {
+ SleepTime=atoiw(ChPtr+1);
+ if (SleepTime>1000)
+ BadSwitch(Switch);
+ InitSystemOptions(SleepTime);
+ }
+ SetPriority(Priority);
+ }
+ break;
}
- if (isdigit(Switch[1]))
+ break;
+ case 'S':
+ if (IsDigit(Switch[1]))
{
Solid|=SOLID_COUNT;
- SolidCount=atoi(&Switch[1]);
+ SolidCount=atoiw(&Switch[1]);
}
else
- switch(etoupper(Switch[1]))
+ switch(toupperw(Switch[1]))
{
case 0:
Solid|=SOLID_NORMAL;
@@ -789,18 +788,24 @@ void CommandData::ProcessSwitch(char *Switch,wchar *SwitchW)
case 'D':
Solid|=SOLID_VOLUME_DEPENDENT;
break;
+ case 'I':
+ ProhibitConsoleInput();
+ wcsncpyz(UseStdin,Switch[2] ? Switch+2:L"stdin",ASIZE(UseStdin));
+ break;
case 'L':
- if (isdigit(Switch[2]))
- FileSizeLess=atoil(Switch+2);
+ if (IsDigit(Switch[2]))
+ FileSizeLess=atoilw(Switch+2);
break;
case 'M':
- if (isdigit(Switch[2]))
- FileSizeMore=atoil(Switch+2);
+ if (IsDigit(Switch[2]))
+ FileSizeMore=atoilw(Switch+2);
break;
case 'C':
{
+ bool AlreadyBad=false; // Avoid reporting "bad switch" several times.
+
RAR_CHARSET rch=RCH_DEFAULT;
- switch(etoupper(Switch[2]))
+ switch(toupperw(Switch[2]))
{
case 'A':
rch=RCH_ANSI;
@@ -811,62 +816,121 @@ void CommandData::ProcessSwitch(char *Switch,wchar *SwitchW)
case 'U':
rch=RCH_UNICODE;
break;
+ case 'F':
+ rch=RCH_UTF8;
+ break;
default :
BadSwitch(Switch);
+ AlreadyBad=true;
break;
};
- if (Switch[3]==0)
- CommentCharset=FilelistCharset=rch;
- else
- for (int I=3;Switch[I]!=0;I++)
- switch(etoupper(Switch[I]))
- {
- case 'C':
- CommentCharset=rch;
- break;
- case 'L':
- FilelistCharset=rch;
- break;
- default:
- BadSwitch(Switch);
- break;
- }
+ if (!AlreadyBad)
+ if (Switch[3]==0)
+ CommentCharset=FilelistCharset=ErrlogCharset=RedirectCharset=rch;
+ else
+ for (uint I=3;Switch[I]!=0 && !AlreadyBad;I++)
+ switch(toupperw(Switch[I]))
+ {
+ case 'C':
+ CommentCharset=rch;
+ break;
+ case 'L':
+ FilelistCharset=rch;
+ break;
+ case 'R':
+ RedirectCharset=rch;
+ break;
+ default:
+ BadSwitch(Switch);
+ AlreadyBad=true;
+ break;
+ }
+ // Set it immediately when parsing the command line, so it also
+ // affects messages issued while parsing the command line.
+ SetConsoleRedirectCharset(RedirectCharset);
}
break;
}
break;
- case 'C':
- if (Switch[2]==0)
- switch(etoupper(Switch[1]))
- {
- case '-':
- DisableComment=true;
- break;
- case 'U':
- ConvertNames=NAMES_UPPERCASE;
- break;
- case 'L':
- ConvertNames=NAMES_LOWERCASE;
- break;
- }
- break;
- case 'K':
- switch(etoupper(Switch[1]))
+ case 'T':
+ switch(toupperw(Switch[1]))
{
+ case 'K':
+ ArcTime=ARCTIME_KEEP;
+ break;
+ case 'L':
+ ArcTime=ARCTIME_LATEST;
+ break;
+ case 'O':
+ SetTimeFilters(Switch+2,true,true);
+ break;
+ case 'N':
+ SetTimeFilters(Switch+2,false,true);
+ break;
case 'B':
- KeepBroken=true;
+ SetTimeFilters(Switch+2,true,false);
+ break;
+ case 'A':
+ SetTimeFilters(Switch+2,false,false);
+ break;
+ case 'S':
+ SetStoreTimeMode(Switch+2);
+ break;
+ case '-':
+ Test=false;
break;
case 0:
- Lock=true;
+ Test=true;
+ break;
+ default:
+ BadSwitch(Switch);
+ break;
+ }
+ break;
+ case 'U':
+ if (Switch[1]==0)
+ UpdateFiles=true;
+ else
+ BadSwitch(Switch);
+ break;
+ case 'V':
+ switch(toupperw(Switch[1]))
+ {
+ case 'P':
+ VolumePause=true;
break;
+ case 'E':
+ if (toupperw(Switch[2])=='R')
+ VersionControl=atoiw(Switch+3)+1;
+ break;
+ case '-':
+ VolSize=0;
+ break;
+ default:
+ VolSize=VOLSIZE_AUTO; // UnRAR -v switch for list command.
+ break;
+ }
+ break;
+ case 'W':
+ wcsncpyz(TempPath,Switch+1,ASIZE(TempPath));
+ AddEndSlash(TempPath,ASIZE(TempPath));
+ break;
+ case 'Y':
+ AllYes=true;
+ break;
+ case 'Z':
+ if (Switch[1]==0)
+ {
+ // If comment file is not specified, we read data from stdin.
+ wcsncpyz(CommentFile,L"stdin",ASIZE(CommentFile));
}
+ else
+ wcsncpyz(CommentFile,Switch+1,ASIZE(CommentFile));
break;
-#ifndef GUI
case '?' :
- OutHelp();
+ OutHelp(RARX_SUCCESS);
break;
-#endif
default :
BadSwitch(Switch);
break;
@@ -875,320 +939,60 @@ void CommandData::ProcessSwitch(char *Switch,wchar *SwitchW)
#endif
-#ifndef SFX_MODULE
-void CommandData::BadSwitch(char *Switch)
+#if !defined(SFX_MODULE)
+void CommandData::BadSwitch(const wchar *Switch)
{
mprintf(St(MUnknownOption),Switch);
- ErrHandler.Exit(USER_ERROR);
+ mprintf(L"\n");
+ ErrHandler.Exit(RARX_USERERROR);
}
#endif
-#ifndef GUI
-void CommandData::OutTitle()
+void CommandData::ProcessCommand()
{
- if (BareOutput || DisableCopyright)
- return;
-#if defined(__GNUC__) && defined(SFX_MODULE)
- mprintf(St(MCopyrightS));
-#else
-#ifndef SILENT
- static bool TitleShown=false;
- if (TitleShown)
- return;
- TitleShown=true;
- char Version[50];
- int Beta=RARVER_BETA;
- if (Beta!=0)
- sprintf(Version,"%d.%02d %s %d",RARVER_MAJOR,RARVER_MINOR,St(MBeta),RARVER_BETA);
- else
- sprintf(Version,"%d.%02d",RARVER_MAJOR,RARVER_MINOR);
-#ifdef UNRAR
- mprintf(St(MUCopyright),Version,RARVER_YEAR);
-#else
-#endif
-#endif
-#endif
-}
-#endif
-
+#ifndef SFX_MODULE
-inline bool CmpMSGID(MSGID i1,MSGID i2)
-{
-#ifdef MSGID_INT
- return(i1==i2);
-#else
- // If MSGID is const char*, we cannot compare pointers only.
- // Pointers to different instances of same strings can differ,
- // so we need to compare complete strings.
- return(strcmp(i1,i2)==0);
-#endif
-}
+ const wchar *SingleCharCommands=L"FUADPXETK";
+ if (Command[0]!=0 && Command[1]!=0 && wcschr(SingleCharCommands,Command[0])!=NULL || *ArcName==0)
+ OutHelp(*Command==0 ? RARX_SUCCESS:RARX_USERERROR); // Return 'success' for 'rar' without parameters.
-void CommandData::OutHelp()
-{
-#if !defined(GUI) && !defined(SILENT)
- OutTitle();
- static MSGID Help[]={
-#ifdef SFX_MODULE
- MCHelpCmd,MSHelpCmdE,MSHelpCmdT,MSHelpCmdV
-#elif defined(UNRAR)
- MUNRARTitle1,MRARTitle2,MCHelpCmd,MCHelpCmdE,MCHelpCmdL,
- MCHelpCmdP,MCHelpCmdT,MCHelpCmdV,MCHelpCmdX,MCHelpSw,
- MCHelpSwm,MCHelpSwAC,MCHelpSwAD,MCHelpSwAP,
- MCHelpSwCm,MCHelpSwCFGm,MCHelpSwCL,MCHelpSwCU,
- MCHelpSwDH,MCHelpSwEP,MCHelpSwEP3,MCHelpSwF,MCHelpSwIDP,MCHelpSwIERR,
- MCHelpSwINUL,MCHelpSwIOFF,MCHelpSwKB,MCHelpSwN,MCHelpSwNa,MCHelpSwNal,
- MCHelpSwO,MCHelpSwOC,MCHelpSwOR,MCHelpSwOW,MCHelpSwP,
- MCHelpSwPm,MCHelpSwR,MCHelpSwRI,MCHelpSwSL,MCHelpSwSM,MCHelpSwTA,
- MCHelpSwTB,MCHelpSwTN,MCHelpSwTO,MCHelpSwTS,MCHelpSwU,MCHelpSwVUnr,
- MCHelpSwVER,MCHelpSwVP,MCHelpSwX,MCHelpSwXa,MCHelpSwXal,MCHelpSwY
+ const wchar *ArcExt=GetExt(ArcName);
+#ifdef _UNIX
+ if (ArcExt==NULL && (!FileExist(ArcName) || IsDir(GetFileAttr(ArcName))))
+ wcsncatz(ArcName,L".rar",ASIZE(ArcName));
#else
- MRARTitle1,MRARTitle2,MCHelpCmd,MCHelpCmdA,MCHelpCmdC,MCHelpCmdCF,
- MCHelpCmdCH,MCHelpCmdCW,MCHelpCmdD,MCHelpCmdE,MCHelpCmdF,MCHelpCmdI,
- MCHelpCmdK,MCHelpCmdL,MCHelpCmdM,MCHelpCmdP,MCHelpCmdR,MCHelpCmdRC,
- MCHelpCmdRN,MCHelpCmdRR,MCHelpCmdRV,MCHelpCmdS,MCHelpCmdT,MCHelpCmdU,
- MCHelpCmdV,MCHelpCmdX,MCHelpSw,MCHelpSwm,MCHelpSwAC,MCHelpSwAD,MCHelpSwAG,
- MCHelpSwAO,MCHelpSwAP,MCHelpSwAS,MCHelpSwAV,MCHelpSwAVm,MCHelpSwCm,
- MCHelpSwCFGm,MCHelpSwCL,MCHelpSwCU,MCHelpSwDF,MCHelpSwDH,MCHelpSwDR,
- MCHelpSwDS,MCHelpSwDW,MCHelpSwEa,MCHelpSwED,MCHelpSwEE,MCHelpSwEN,
- MCHelpSwEP,MCHelpSwEP1,MCHelpSwEP2,MCHelpSwEP3,MCHelpSwF,MCHelpSwHP,
- MCHelpSwIDP,MCHelpSwIEML,MCHelpSwIERR,MCHelpSwILOG,MCHelpSwINUL,
- MCHelpSwIOFF,MCHelpSwISND,MCHelpSwK,MCHelpSwKB,MCHelpSwMn,MCHelpSwMC,
- MCHelpSwMD,MCHelpSwMS,MCHelpSwMT,MCHelpSwN,MCHelpSwNa,MCHelpSwNal,
- MCHelpSwO,MCHelpSwOC,MCHelpSwOL,MCHelpSwOR,MCHelpSwOS,MCHelpSwOW,
- MCHelpSwP,MCHelpSwPm,MCHelpSwR,MCHelpSwR0,MCHelpSwRI,MCHelpSwRR,
- MCHelpSwRV,MCHelpSwS,MCHelpSwSm,MCHelpSwSC,MCHelpSwSFX,MCHelpSwSI,
- MCHelpSwSL,MCHelpSwSM,MCHelpSwT,MCHelpSwTA,MCHelpSwTB,MCHelpSwTK,
- MCHelpSwTL,MCHelpSwTN,MCHelpSwTO,MCHelpSwTS,MCHelpSwU,MCHelpSwV,
- MCHelpSwVn,MCHelpSwVD,MCHelpSwVER,MCHelpSwVN,MCHelpSwVP,MCHelpSwW,
- MCHelpSwX,MCHelpSwXa,MCHelpSwXal,MCHelpSwY,MCHelpSwZ
+ if (ArcExt==NULL)
+ wcsncatz(ArcName,L".rar",ASIZE(ArcName));
#endif
- };
-
- for (int I=0;I<sizeof(Help)/sizeof(Help[0]);I++)
+ // Treat arcname.part1 as arcname.part1.rar.
+ if (ArcExt!=NULL && wcsnicomp(ArcExt,L".part",5)==0 && IsDigit(ArcExt[5]) &&
+ !FileExist(ArcName))
{
-#ifndef SFX_MODULE
-#ifdef DISABLEAUTODETECT
- if (Help[I]==MCHelpSwV)
- continue;
-#endif
-#ifndef _WIN_32
- static MSGID Win32Only[]={
- MCHelpSwIEML,MCHelpSwVD,MCHelpSwAO,MCHelpSwOS,MCHelpSwIOFF,
- MCHelpSwEP2,MCHelpSwOC,MCHelpSwDR,MCHelpSwRI
- };
- bool Found=false;
- for (int J=0;J<sizeof(Win32Only)/sizeof(Win32Only[0]);J++)
- if (CmpMSGID(Help[I],Win32Only[J]))
- {
- Found=true;
- break;
- }
- if (Found)
- continue;
-#endif
-#if !defined(_UNIX) && !defined(_WIN_32)
- if (CmpMSGID(Help[I],MCHelpSwOW))
- continue;
-#endif
-#if !defined(_WIN_32) && !defined(_EMX)
- if (CmpMSGID(Help[I],MCHelpSwAC))
- continue;
-#endif
-#ifndef SAVE_LINKS
- if (CmpMSGID(Help[I],MCHelpSwOL))
- continue;
-#endif
-#ifndef PACK_SMP
- if (CmpMSGID(Help[I],MCHelpSwMT))
- continue;
-#endif
-#ifndef _BEOS
- if (CmpMSGID(Help[I],MCHelpSwEE))
- {
-#if defined(_EMX) && !defined(_DJGPP)
- if (_osmode != OS2_MODE)
- continue;
-#else
- continue;
-#endif
- }
-#endif
-#endif
- mprintf(St(Help[I]));
+ wchar Name[NM];
+ wcsncpyz(Name,ArcName,ASIZE(Name));
+ wcsncatz(Name,L".rar",ASIZE(Name));
+ if (FileExist(Name))
+ wcsncpyz(ArcName,Name,ASIZE(ArcName));
}
- mprintf("\n");
- ErrHandler.Exit(USER_ERROR);
-#endif
-}
-
-bool CommandData::ExclCheckArgs(StringList *Args,char *CheckName,bool CheckFullPath,int MatchMode)
-{
- char *Name=ConvertPath(CheckName,NULL);
- char FullName[NM],*CurName;
- *FullName=0;
- Args->Rewind();
- while ((CurName=Args->GetString())!=NULL)
-#ifndef SFX_MODULE
- if (CheckFullPath && IsFullPath(CurName))
- {
- if (*FullName==0)
- ConvertNameToFull(CheckName,FullName);
- if (CmpName(CurName,FullName,MatchMode))
- return(true);
- }
- else
-#endif
- if (CmpName(ConvertPath(CurName,NULL),Name,MatchMode))
- return(true);
- return(false);
-}
-
-
-bool CommandData::ExclCheck(char *CheckName,bool CheckFullPath)
-{
- if (ExclCheckArgs(ExclArgs,CheckName,CheckFullPath,MATCH_WILDSUBPATH))
- return(true);
- if (InclArgs->ItemsCount()==0)
- return(false);
- if (ExclCheckArgs(InclArgs,CheckName,false,MATCH_WILDSUBPATH))
- return(false);
- return(true);
-}
-
-
-
-
-#ifndef SFX_MODULE
-bool CommandData::TimeCheck(RarTime &ft)
-{
- if (FileTimeBefore.IsSet() && ft>=FileTimeBefore)
- return(true);
- if (FileTimeAfter.IsSet() && ft<=FileTimeAfter)
- return(true);
-/*
- if (FileTimeOlder!=0 || FileTimeNewer!=0)
+ if (wcschr(L"AFUMD",*Command)==NULL && *UseStdin==0)
{
- if (!TimeConverted)
+ if (GenerateArcName)
{
- if (FileTimeOlder!=0)
- FileTimeOlder=SecondsToDosTime(FileTimeOlder);
- if (FileTimeNewer!=0)
- FileTimeNewer=SecondsToDosTime(FileTimeNewer);
- TimeConverted=true;
+ const wchar *Mask=*GenerateMask!=0 ? GenerateMask:DefGenerateMask;
+ GenerateArchiveName(ArcName,ASIZE(ArcName),Mask,false);
}
- if (FileTimeOlder!=0 && ft>=FileTimeOlder)
- return(true);
- if (FileTimeNewer!=0 && ft<=FileTimeNewer)
- return(true);
- }
-*/
- return(false);
-}
-#endif
-
-
-#ifndef SFX_MODULE
-bool CommandData::SizeCheck(Int64 Size)
-{
- if (FileSizeLess!=INT64ERR && Size>=FileSizeLess)
- return(true);
- if (FileSizeMore!=INT64ERR && Size<=FileSizeMore)
- return(true);
- return(false);
-}
-#endif
-
-
-int CommandData::IsProcessFile(FileHeader &NewLhd,bool *ExactMatch,int MatchType)
-{
- if (strlen(NewLhd.FileName)>=NM || strlenw(NewLhd.FileNameW)>=NM)
- return(0);
- if (ExclCheck(NewLhd.FileName,false))
- return(0);
-#ifndef SFX_MODULE
- if (TimeCheck(NewLhd.mtime))
- return(0);
- if ((NewLhd.FileAttr & ExclFileAttr)!=0 || InclAttrSet && (NewLhd.FileAttr & InclFileAttr)==0)
- return(0);
- if ((NewLhd.Flags & LHD_WINDOWMASK)!=LHD_DIRECTORY && SizeCheck(NewLhd.FullUnpSize))
- return(0);
-#endif
- char *ArgName;
- wchar *ArgNameW;
- FileArgs->Rewind();
- for (int StringCount=1;FileArgs->GetString(&ArgName,&ArgNameW);StringCount++)
- {
-#ifndef SFX_MODULE
- bool Unicode=(NewLhd.Flags & LHD_UNICODE) || ArgNameW!=NULL;
- if (Unicode)
- {
- wchar NameW[NM],ArgW[NM],*NamePtr=NewLhd.FileNameW;
- bool CorrectUnicode=true;
- if (ArgNameW==NULL)
- {
- if (!CharToWide(ArgName,ArgW) || *ArgW==0)
- CorrectUnicode=false;
- ArgNameW=ArgW;
- }
- if ((NewLhd.Flags & LHD_UNICODE)==0)
- {
- if (!CharToWide(NewLhd.FileName,NameW) || *NameW==0)
- CorrectUnicode=false;
- NamePtr=NameW;
- }
- if (CmpName(ArgNameW,NamePtr,MatchType))
- {
- if (ExactMatch!=NULL)
- *ExactMatch=stricompcw(ArgNameW,NamePtr)==0;
- return(StringCount);
- }
- if (CorrectUnicode)
- continue;
- }
-#endif
- if (CmpName(ArgName,NewLhd.FileName,MatchType))
- {
- if (ExactMatch!=NULL)
- *ExactMatch=stricompc(ArgName,NewLhd.FileName)==0;
- return(StringCount);
- }
- }
- return(0);
-}
-
-
-#ifndef GUI
-void CommandData::ProcessCommand()
-{
-#ifndef SFX_MODULE
-
- const char *SingleCharCommands="FUADPXETK";
- if (Command[1] && strchr(SingleCharCommands,*Command)!=NULL || *ArcName==0)
- OutHelp();
-
-#ifdef _UNIX
- if (GetExt(ArcName)==NULL && (!FileExist(ArcName) || IsDir(GetFileAttr(ArcName))))
- strcat(ArcName,".rar");
-#else
- if (GetExt(ArcName)==NULL)
- strcat(ArcName,".rar");
-#endif
-
- if (strchr("AFUMD",*Command)==NULL)
- {
StringList ArcMasks;
ArcMasks.AddString(ArcName);
- ScanTree Scan(&ArcMasks,Recurse,SaveLinks,SCAN_SKIPDIRS);
+ ScanTree Scan(&ArcMasks,Recurse,SaveSymLinks,SCAN_SKIPDIRS);
FindData FindData;
while (Scan.GetNext(&FindData)==SCAN_SUCCESS)
- AddArcName(FindData.Name,FindData.NameW);
+ AddArcName(FindData.Name);
}
else
- AddArcName(ArcName,NULL);
+ AddArcName(ArcName);
#endif
switch(Command[0])
@@ -1197,10 +1001,9 @@ void CommandData::ProcessCommand()
case 'X':
case 'E':
case 'T':
- case 'I':
{
- CmdExtract Extract;
- Extract.DoExtract(this);
+ CmdExtract Extract(this);
+ Extract.DoExtract();
}
break;
#ifndef SILENT
@@ -1209,77 +1012,72 @@ void CommandData::ProcessCommand()
ListArchive(this);
break;
default:
- OutHelp();
+ OutHelp(RARX_USERERROR);
#endif
}
if (!BareOutput)
- mprintf("\n");
+ mprintf(L"\n");
}
-#endif
-void CommandData::AddArcName(char *Name,wchar *NameW)
+void CommandData::AddArcName(const wchar *Name)
{
- ArcNames->AddString(Name,NameW);
+ ArcNames.AddString(Name);
}
-bool CommandData::GetArcName(char *Name,wchar *NameW,int MaxSize)
+bool CommandData::GetArcName(wchar *Name,int MaxSize)
{
- if (!ArcNames->GetString(Name,NameW,NM))
- return(false);
- return(true);
+ return ArcNames.GetString(Name,MaxSize);
}
bool CommandData::IsSwitch(int Ch)
{
-#if defined(_WIN_32) || defined(_EMX)
- return(Ch=='-' || Ch=='/');
+#if defined(_WIN_ALL) || defined(_EMX)
+ return Ch=='-' || Ch=='/';
#else
- return(Ch=='-');
+ return Ch=='-';
#endif
}
#ifndef SFX_MODULE
-uint CommandData::GetExclAttr(char *Str)
+uint CommandData::GetExclAttr(const wchar *Str,bool &Dir)
{
- if (isdigit(*Str))
- return(strtol(Str,NULL,0));
- else
+ if (IsDigit(*Str))
+ return wcstol(Str,NULL,0);
+
+ uint Attr=0;
+ while (*Str!=0)
{
- uint Attr;
- for (Attr=0;*Str;Str++)
- switch(etoupper(*Str))
- {
+ switch(toupperw(*Str))
+ {
+ case 'D':
+ Dir=true;
+ break;
#ifdef _UNIX
- case 'D':
- Attr|=S_IFDIR;
- break;
- case 'V':
- Attr|=S_IFCHR;
- break;
-#elif defined(_WIN_32) || defined(_EMX)
- case 'R':
- Attr|=0x1;
- break;
- case 'H':
- Attr|=0x2;
- break;
- case 'S':
- Attr|=0x4;
- break;
- case 'D':
- Attr|=0x10;
- break;
- case 'A':
- Attr|=0x20;
- break;
+ case 'V':
+ Attr|=S_IFCHR;
+ break;
+#elif defined(_WIN_ALL) || defined(_EMX)
+ case 'R':
+ Attr|=0x1;
+ break;
+ case 'H':
+ Attr|=0x2;
+ break;
+ case 'S':
+ Attr|=0x4;
+ break;
+ case 'A':
+ Attr|=0x20;
+ break;
#endif
- }
- return(Attr);
+ }
+ Str++;
}
+ return Attr;
}
#endif
@@ -1289,13 +1087,44 @@ uint CommandData::GetExclAttr(char *Str)
#ifndef SFX_MODULE
bool CommandData::CheckWinSize()
{
- static int ValidSize[]={
- 0x10000,0x20000,0x40000,0x80000,0x100000,0x200000,0x400000
- };
- for (int I=0;I<sizeof(ValidSize)/sizeof(ValidSize[0]);I++)
- if (WinSize==ValidSize[I])
- return(true);
+ // Define 0x100000000 as macro to avoid troubles with older compilers.
+ const uint64 MaxDictSize=INT32TO64(1,0);
+ // Limit the dictionary size to 4 GB.
+ for (uint64 I=0x10000;I<=MaxDictSize;I*=2)
+ if (WinSize==I)
+ return true;
WinSize=0x400000;
- return(false);
+ return false;
+}
+#endif
+
+
+#ifndef SFX_MODULE
+void CommandData::ReportWrongSwitches(RARFORMAT Format)
+{
+ if (Format==RARFMT15)
+ {
+ if (HashType!=HASH_CRC32)
+ uiMsg(UIERROR_INCOMPATSWITCH,L"-ht",4);
+#ifdef _WIN_ALL
+ if (SaveSymLinks)
+ uiMsg(UIERROR_INCOMPATSWITCH,L"-ol",4);
+#endif
+ if (SaveHardLinks)
+ uiMsg(UIERROR_INCOMPATSWITCH,L"-oh",4);
+
+#ifdef _WIN_ALL
+ // Do not report a wrong dictionary size here, because we are not sure
+ // yet about archive format. We can switch to RAR5 mode later
+ // if we update RAR5 archive.
+
+
+#endif
+ if (QOpenMode!=QOPEN_AUTO)
+ uiMsg(UIERROR_INCOMPATSWITCH,L"-qo",4);
+ }
+ if (Format==RARFMT50)
+ {
+ }
}
#endif
diff --git a/unrar/unrar/cmddata.hpp b/unrar/unrar/cmddata.hpp
index 64a336d..0feb404 100644
--- a/unrar/unrar/cmddata.hpp
+++ b/unrar/unrar/cmddata.hpp
@@ -1,57 +1,74 @@
#ifndef _RAR_CMDDATA_
#define _RAR_CMDDATA_
-#define DefaultStoreList "7z;ace;arj;bz2;cab;gz;jpeg;jpg;lha;lzh;mp3;rar;taz;tgz;z;zip"
+
+#define DefaultStoreList L"7z;ace;arj;bz2;cab;gz;jpeg;jpg;lha;lz;lzh;mp3;rar;taz;tbz;tbz2;tgz;txz;xz;z;zip;zipx;zst;tzst"
+
+enum RAR_CMD_LIST_MODE {RCLM_AUTO,RCLM_REJECT_LISTS,RCLM_ACCEPT_LISTS};
+
+enum IS_PROCESS_FILE_FLAGS {IPFF_EXCLUDE_PARENT=1};
class CommandData:public RAROptions
{
private:
- void ProcessSwitchesString(char *Str);
- void ProcessSwitch(char *Switch,wchar *SwitchW=NULL);
- void BadSwitch(char *Switch);
- bool ExclCheckArgs(StringList *Args,char *CheckName,bool CheckFullPath,int MatchMode);
- uint GetExclAttr(char *Str);
+ void ProcessSwitch(const wchar *Switch);
+ void BadSwitch(const wchar *Switch);
+ uint GetExclAttr(const wchar *Str,bool &Dir);
+#if !defined(SFX_MODULE)
+ void SetTimeFilters(const wchar *Mod,bool Before,bool Age);
+ void SetStoreTimeMode(const wchar *S);
+#endif
bool FileLists;
bool NoMoreSwitches;
- bool TimeConverted;
+ RAR_CMD_LIST_MODE ListMode;
bool BareOutput;
public:
CommandData();
- ~CommandData();
void Init();
- void Close();
- void ParseArg(char *Arg,wchar *ArgW);
+
+ void ParseCommandLine(bool Preprocess,int argc, char *argv[]);
+ void ParseArg(wchar *ArgW);
void ParseDone();
void ParseEnvVar();
- void ReadConfig(int argc,char *argv[]);
- bool IsConfigEnabled(int argc,char *argv[]);
+ void ReadConfig();
+ void PreprocessArg(const wchar *Arg);
+ void ProcessSwitchesString(const wchar *Str);
void OutTitle();
- void OutHelp();
+ void OutHelp(RAR_EXIT ExitCode);
bool IsSwitch(int Ch);
- bool ExclCheck(char *CheckName,bool CheckFullPath);
- bool StoreCheck(char *CheckName);
- bool TimeCheck(RarTime &ft);
- bool SizeCheck(Int64 Size);
- int IsProcessFile(FileHeader &NewLhd,bool *ExactMatch=NULL,int MatchType=MATCH_WILDSUBPATH);
+ bool ExclCheck(const wchar *CheckName,bool Dir,bool CheckFullPath,bool CheckInclList);
+ static bool CheckArgs(StringList *Args,bool Dir,const wchar *CheckName,bool CheckFullPath,int MatchMode);
+ bool ExclDirByAttr(uint FileAttr);
+ bool TimeCheck(RarTime &ftm,RarTime &ftc,RarTime &fta);
+ bool SizeCheck(int64 Size);
+ bool AnyFiltersActive();
+ int IsProcessFile(FileHeader &FileHead,bool *ExactMatch,int MatchType,
+ bool Flags,wchar *MatchedArg,uint MatchedArgSize);
void ProcessCommand();
- void AddArcName(char *Name,wchar *NameW);
- bool GetArcName(char *Name,wchar *NameW,int MaxSize);
+ void AddArcName(const wchar *Name);
+ bool GetArcName(wchar *Name,int MaxSize);
bool CheckWinSize();
- int GetRecoverySize(char *Str,int DefSize);
+ int GetRecoverySize(const wchar *CmdStr,const wchar *Value,int DefSize);
+
+#ifndef SFX_MODULE
+ void ReportWrongSwitches(RARFORMAT Format);
+#endif
+
+ wchar Command[NM+16];
+
+ wchar ArcName[NM];
- char Command[NM+16];
- wchar CommandW[NM+16];
+ StringList FileArgs;
+ StringList ExclArgs;
+ StringList InclArgs;
+ StringList ArcNames;
+ StringList StoreArgs;
- char ArcName[NM];
- wchar ArcNameW[NM];
+ SecPassword Password;
- StringList *FileArgs;
- StringList *ExclArgs;
- StringList *InclArgs;
- StringList *ArcNames;
- StringList *StoreArgs;
+ std::vector<int64> NextVolSizes;
};
#endif
diff --git a/unrar/unrar/cmdfilter.cpp b/unrar/unrar/cmdfilter.cpp
new file mode 100644
index 0000000..e0add14
--- /dev/null
+++ b/unrar/unrar/cmdfilter.cpp
@@ -0,0 +1,354 @@
+// Return 'true' if we need to exclude the file from processing as result
+// of -x switch. If CheckInclList is true, we also check the file against
+// the include list created with -n switch.
+bool CommandData::ExclCheck(const wchar *CheckName,bool Dir,bool CheckFullPath,bool CheckInclList)
+{
+ if (CheckArgs(&ExclArgs,Dir,CheckName,CheckFullPath,MATCH_WILDSUBPATH))
+ return true;
+ if (!CheckInclList || InclArgs.ItemsCount()==0)
+ return false;
+ if (CheckArgs(&InclArgs,Dir,CheckName,CheckFullPath,MATCH_WILDSUBPATH))
+ return false;
+ return true;
+}
+
+
+bool CommandData::CheckArgs(StringList *Args,bool Dir,const wchar *CheckName,bool CheckFullPath,int MatchMode)
+{
+ wchar *Name=ConvertPath(CheckName,NULL,0);
+ wchar FullName[NM];
+ wchar CurMask[NM];
+ *FullName=0;
+ Args->Rewind();
+ while (Args->GetString(CurMask,ASIZE(CurMask)))
+ {
+ wchar *LastMaskChar=PointToLastChar(CurMask);
+ bool DirMask=IsPathDiv(*LastMaskChar); // Mask for directories only.
+
+ if (Dir)
+ {
+ // CheckName is a directory.
+ if (DirMask)
+ {
+ // We process the directory and have the directory exclusion mask.
+ // So let's convert "mask\" to "mask" and process it normally.
+
+ *LastMaskChar=0;
+ }
+ else
+ {
+ // REMOVED, we want -npath\* to match empty folders too.
+ // If mask has wildcards in name part and does not have the trailing
+ // '\' character, we cannot use it for directories.
+
+ // if (IsWildcard(PointToName(CurMask)))
+ // continue;
+ }
+ }
+ else
+ {
+ // If we process a file inside of directory excluded by "dirmask\".
+ // we want to exclude such file too. So we convert "dirmask\" to
+ // "dirmask\*". It is important for operations other than archiving
+ // with -x. When archiving with -x, directory matched by "dirmask\"
+ // is excluded from further scanning.
+
+ if (DirMask)
+ wcsncatz(CurMask,L"*",ASIZE(CurMask));
+ }
+
+#ifndef SFX_MODULE
+ if (CheckFullPath && IsFullPath(CurMask))
+ {
+ // We do not need to do the special "*\" processing here, because
+ // unlike the "else" part of this "if", now we convert names to full
+ // format, so they all include the path, which is matched by "*\"
+ // correctly. Moreover, removing "*\" from mask would break
+ // the comparison, because now all names have the path.
+
+ if (*FullName==0)
+ ConvertNameToFull(CheckName,FullName,ASIZE(FullName));
+ if (CmpName(CurMask,FullName,MatchMode))
+ return true;
+ }
+ else
+#endif
+ {
+ wchar NewName[NM+2],*CurName=Name;
+
+ // Important to convert before "*\" check below, so masks like
+ // d:*\something are processed properly.
+ wchar *CmpMask=ConvertPath(CurMask,NULL,0);
+
+ if (CmpMask[0]=='*' && IsPathDiv(CmpMask[1]))
+ {
+ // We want "*\name" to match 'name' not only in subdirectories,
+ // but also in the current directory. We convert the name
+ // from 'name' to '.\name' to be matched by "*\" part even if it is
+ // in current directory.
+ NewName[0]='.';
+ NewName[1]=CPATHDIVIDER;
+ wcsncpyz(NewName+2,Name,ASIZE(NewName)-2);
+ CurName=NewName;
+ }
+
+ if (CmpName(CmpMask,CurName,MatchMode))
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+#ifndef SFX_MODULE
+// Now this function performs only one task and only in Windows version:
+// it skips symlinks to directories if -e1024 switch is specified.
+// Symlinks are skipped in ScanTree class, so their entire contents
+// is skipped too. Without this function we would check the attribute
+// only directly before archiving, so we would skip the symlink record,
+// but not the contents of symlinked directory.
+bool CommandData::ExclDirByAttr(uint FileAttr)
+{
+#ifdef _WIN_ALL
+ if ((FileAttr & FILE_ATTRIBUTE_REPARSE_POINT)!=0 &&
+ (ExclFileAttr & FILE_ATTRIBUTE_REPARSE_POINT)!=0)
+ return true;
+#endif
+ return false;
+}
+#endif
+
+
+
+
+#if !defined(SFX_MODULE)
+void CommandData::SetTimeFilters(const wchar *Mod,bool Before,bool Age)
+{
+ bool ModeOR=false,TimeMods=false;
+ const wchar *S=Mod;
+ // Check if any 'mca' modifiers are present, set OR mode if 'o' is present,
+ // skip modifiers and set S to beginning of time string. Be sure to check
+ // *S!=0, because termination 0 is a part of string for wcschr.
+ for (;*S!=0 && wcschr(L"MCAOmcao",*S)!=NULL;S++)
+ if (*S=='o' || *S=='O')
+ ModeOR=true;
+ else
+ TimeMods=true;
+
+ if (!TimeMods) // Assume 'm' if no modifiers are specified.
+ Mod=L"m";
+
+ // Set the specified time for every modifier. Be sure to check *Mod!=0,
+ // because termination 0 is a part of string for wcschr. This check is
+ // important when we set Mod to "m" above.
+ for (;*Mod!=0 && wcschr(L"MCAOmcao",*Mod)!=NULL;Mod++)
+ switch(toupperw(*Mod))
+ {
+ case 'M':
+ if (Before)
+ {
+ Age ? FileMtimeBefore.SetAgeText(S):FileMtimeBefore.SetIsoText(S);
+ FileMtimeBeforeOR=ModeOR;
+ }
+ else
+ {
+ Age ? FileMtimeAfter.SetAgeText(S):FileMtimeAfter.SetIsoText(S);
+ FileMtimeAfterOR=ModeOR;
+ }
+ break;
+ case 'C':
+ if (Before)
+ {
+ Age ? FileCtimeBefore.SetAgeText(S):FileCtimeBefore.SetIsoText(S);
+ FileCtimeBeforeOR=ModeOR;
+ }
+ else
+ {
+ Age ? FileCtimeAfter.SetAgeText(S):FileCtimeAfter.SetIsoText(S);
+ FileCtimeAfterOR=ModeOR;
+ }
+ break;
+ case 'A':
+ if (Before)
+ {
+ Age ? FileAtimeBefore.SetAgeText(S):FileAtimeBefore.SetIsoText(S);
+ FileAtimeBeforeOR=ModeOR;
+ }
+ else
+ {
+ Age ? FileAtimeAfter.SetAgeText(S):FileAtimeAfter.SetIsoText(S);
+ FileAtimeAfterOR=ModeOR;
+ }
+ break;
+ }
+}
+#endif
+
+
+#ifndef SFX_MODULE
+// Return 'true' if we need to exclude the file from processing.
+bool CommandData::TimeCheck(RarTime &ftm,RarTime &ftc,RarTime &fta)
+{
+ bool FilterOR=false;
+
+ if (FileMtimeBefore.IsSet()) // Filter present.
+ if (ftm>=FileMtimeBefore) // Condition not matched.
+ if (FileMtimeBeforeOR)
+ FilterOR=true; // Not matched OR filter is present.
+ else
+ return true; // Exclude file in AND mode.
+ else // Condition matched.
+ if (FileMtimeBeforeOR)
+ return false; // Include file in OR mode.
+
+ if (FileMtimeAfter.IsSet()) // Filter present.
+ if (ftm<FileMtimeAfter) // Condition not matched.
+ if (FileMtimeAfterOR)
+ FilterOR=true; // Not matched OR filter is present.
+ else
+ return true; // Exclude file in AND mode.
+ else // Condition matched.
+ if (FileMtimeAfterOR)
+ return false; // Include file in OR mode.
+
+ if (FileCtimeBefore.IsSet()) // Filter present.
+ if (ftc>=FileCtimeBefore) // Condition not matched.
+ if (FileCtimeBeforeOR)
+ FilterOR=true; // Not matched OR filter is present.
+ else
+ return true; // Exclude file in AND mode.
+ else // Condition matched.
+ if (FileCtimeBeforeOR)
+ return false; // Include file in OR mode.
+
+ if (FileCtimeAfter.IsSet()) // Filter present.
+ if (ftc<FileCtimeAfter) // Condition not matched.
+ if (FileCtimeAfterOR)
+ FilterOR=true; // Not matched OR filter is present.
+ else
+ return true; // Exclude file in AND mode.
+ else // Condition matched.
+ if (FileCtimeAfterOR)
+ return false; // Include file in OR mode.
+
+ if (FileAtimeBefore.IsSet()) // Filter present.
+ if (fta>=FileAtimeBefore) // Condition not matched.
+ if (FileAtimeBeforeOR)
+ FilterOR=true; // Not matched OR filter is present.
+ else
+ return true; // Exclude file in AND mode.
+ else // Condition matched.
+ if (FileAtimeBeforeOR)
+ return false; // Include file in OR mode.
+
+ if (FileAtimeAfter.IsSet()) // Filter present.
+ if (fta<FileAtimeAfter) // Condition not matched.
+ if (FileAtimeAfterOR)
+ FilterOR=true; // Not matched OR filter is present.
+ else
+ return true; // Exclude file in AND mode.
+ else // Condition matched.
+ if (FileAtimeAfterOR)
+ return false; // Include file in OR mode.
+
+ return FilterOR; // Exclude if all OR filters are not matched.
+}
+#endif
+
+
+#ifndef SFX_MODULE
+// Return 'true' if we need to exclude the file from processing.
+bool CommandData::SizeCheck(int64 Size)
+{
+ if (Size==INT64NDF) // If called from archive formats like bzip2, not storing the file size.
+ return false;
+ if (FileSizeLess!=INT64NDF && Size>=FileSizeLess)
+ return true;
+ if (FileSizeMore!=INT64NDF && Size<=FileSizeMore)
+ return true;
+ return false;
+}
+#endif
+
+
+
+
+// Return 0 if file must not be processed or a number of matched parameter otherwise.
+int CommandData::IsProcessFile(FileHeader &FileHead,bool *ExactMatch,int MatchType,
+ bool Flags,wchar *MatchedArg,uint MatchedArgSize)
+{
+ if (MatchedArg!=NULL && MatchedArgSize>0)
+ *MatchedArg=0;
+ bool Dir=FileHead.Dir;
+ if (ExclCheck(FileHead.FileName,Dir,false,true))
+ return 0;
+#ifndef SFX_MODULE
+ if (TimeCheck(FileHead.mtime,FileHead.ctime,FileHead.atime))
+ return 0;
+ if ((FileHead.FileAttr & ExclFileAttr)!=0 || FileHead.Dir && ExclDir)
+ return 0;
+ if (InclAttrSet && (FileHead.FileAttr & InclFileAttr)==0 &&
+ (!FileHead.Dir || !InclDir))
+ return 0;
+ if (!Dir && SizeCheck(FileHead.UnpSize))
+ return 0;
+#endif
+ wchar *ArgName;
+ FileArgs.Rewind();
+ for (int StringCount=1;(ArgName=FileArgs.GetString())!=NULL;StringCount++)
+ if (CmpName(ArgName,FileHead.FileName,MatchType))
+ {
+ if (ExactMatch!=NULL)
+ *ExactMatch=wcsicompc(ArgName,FileHead.FileName)==0;
+ if (MatchedArg!=NULL)
+ wcsncpyz(MatchedArg,ArgName,MatchedArgSize);
+ return StringCount;
+ }
+ return 0;
+}
+
+
+#if !defined(SFX_MODULE)
+void CommandData::SetStoreTimeMode(const wchar *S)
+{
+ if (*S==0 || IsDigit(*S) || *S=='-' || *S=='+')
+ {
+ // Apply -ts, -ts1, -ts-, -ts+ to all 3 times.
+ // Handle obsolete -ts[2,3,4] as ts+.
+ EXTTIME_MODE Mode=EXTTIME_MAX;
+ if (*S=='-')
+ Mode=EXTTIME_NONE;
+ if (*S=='1')
+ Mode=EXTTIME_1S;
+ xmtime=xctime=xatime=Mode;
+ S++;
+ }
+
+ while (*S!=0)
+ {
+ EXTTIME_MODE Mode=EXTTIME_MAX;
+ if (S[1]=='-')
+ Mode=EXTTIME_NONE;
+ if (S[1]=='1')
+ Mode=EXTTIME_1S;
+ switch(toupperw(*S))
+ {
+ case 'M':
+ xmtime=Mode;
+ break;
+ case 'C':
+ xctime=Mode;
+ break;
+ case 'A':
+ xatime=Mode;
+ break;
+ case 'P':
+ PreserveAtime=true;
+ break;
+ }
+ S++;
+ }
+}
+#endif
diff --git a/unrar/unrar/cmdmix.cpp b/unrar/unrar/cmdmix.cpp
new file mode 100644
index 0000000..6bd1e1a
--- /dev/null
+++ b/unrar/unrar/cmdmix.cpp
@@ -0,0 +1,125 @@
+void CommandData::OutTitle()
+{
+ if (BareOutput || DisableCopyright)
+ return;
+#if defined(__GNUC__) && defined(SFX_MODULE)
+ mprintf(St(MCopyrightS));
+#else
+#ifndef SILENT
+ static bool TitleShown=false;
+ if (TitleShown)
+ return;
+ TitleShown=true;
+
+ wchar Version[80];
+ if (RARVER_BETA!=0)
+ swprintf(Version,ASIZE(Version),L"%d.%02d %ls %d",RARVER_MAJOR,RARVER_MINOR,St(MBeta),RARVER_BETA);
+ else
+ swprintf(Version,ASIZE(Version),L"%d.%02d",RARVER_MAJOR,RARVER_MINOR);
+#if defined(_WIN_32) || defined(_WIN_64)
+ wcsncatz(Version,L" ",ASIZE(Version));
+#endif
+#ifdef _WIN_32
+ wcsncatz(Version,St(Mx86),ASIZE(Version));
+#endif
+#ifdef _WIN_64
+ wcsncatz(Version,St(Mx64),ASIZE(Version));
+#endif
+ if (PrintVersion)
+ {
+ mprintf(L"%s",Version);
+ exit(0);
+ }
+ mprintf(St(MUCopyright),Version,RARVER_YEAR);
+#endif
+#endif
+}
+
+
+inline bool CmpMSGID(MSGID i1,MSGID i2)
+{
+#ifdef MSGID_INT
+ return i1==i2;
+#else
+ // If MSGID is const char*, we cannot compare pointers only.
+ // Pointers to different instances of same string can differ,
+ // so we need to compare complete strings.
+ return wcscmp(i1,i2)==0;
+#endif
+}
+
+void CommandData::OutHelp(RAR_EXIT ExitCode)
+{
+#if !defined(SILENT)
+ OutTitle();
+ static MSGID Help[]={
+#ifdef SFX_MODULE
+ // Console SFX switches definition.
+ MCHelpCmd,MSHelpCmdE,MSHelpCmdT,MSHelpCmdV
+#else
+ // UnRAR switches definition.
+ MUNRARTitle1,MRARTitle2,MCHelpCmd,MCHelpCmdE,MCHelpCmdL,
+ MCHelpCmdP,MCHelpCmdT,MCHelpCmdV,MCHelpCmdX,MCHelpSw,MCHelpSwm,
+ MCHelpSwAT,MCHelpSwAC,MCHelpSwAD,MCHelpSwAG,MCHelpSwAI,MCHelpSwAP,
+ MCHelpSwCm,MCHelpSwCFGm,MCHelpSwCL,MCHelpSwCU,MCHelpSwDH,MCHelpSwEP,
+ MCHelpSwEP3,MCHelpSwEP4,MCHelpSwF,MCHelpSwIDP,MCHelpSwIERR,
+ MCHelpSwINUL,MCHelpSwIOFF,MCHelpSwKB,MCHelpSwME,MCHelpSwN,MCHelpSwNa,
+ MCHelpSwNal,MCHelpSwO,MCHelpSwOC,MCHelpSwOL,MCHelpSwOP,MCHelpSwOR,
+ MCHelpSwOW,MCHelpSwP,MCHelpSwR,MCHelpSwRI,MCHelpSwSC,MCHelpSwSI,
+ MCHelpSwSL,MCHelpSwSM,MCHelpSwTA,MCHelpSwTB,MCHelpSwTN,MCHelpSwTO,
+ MCHelpSwTS,MCHelpSwU,MCHelpSwVUnr,MCHelpSwVER,MCHelpSwVP,MCHelpSwX,
+ MCHelpSwXa,MCHelpSwXal,MCHelpSwY
+#endif
+ };
+
+ for (uint I=0;I<ASIZE(Help);I++)
+ {
+#ifndef SFX_MODULE
+ if (CmpMSGID(Help[I],MCHelpSwV))
+ continue;
+#ifndef _WIN_ALL
+ static MSGID Win32Only[]={
+ MCHelpSwIEML,MCHelpSwVD,MCHelpSwAO,MCHelpSwOS,MCHelpSwIOFF,
+ MCHelpSwEP2,MCHelpSwOC,MCHelpSwONI,MCHelpSwDR,MCHelpSwRI
+ };
+ bool Found=false;
+ for (uint J=0;J<ASIZE(Win32Only);J++)
+ if (CmpMSGID(Help[I],Win32Only[J]))
+ {
+ Found=true;
+ break;
+ }
+ if (Found)
+ continue;
+#endif
+#ifdef _UNIX
+ if (CmpMSGID(Help[I],MRARTitle2))
+ {
+ mprintf(St(MFwrSlTitle2));
+ continue;
+ }
+#endif
+#if !defined(_UNIX) && !defined(_WIN_ALL)
+ if (CmpMSGID(Help[I],MCHelpSwOW))
+ continue;
+#endif
+#if !defined(_WIN_ALL) && !defined(_EMX)
+ if (CmpMSGID(Help[I],MCHelpSwAC))
+ continue;
+#endif
+#ifndef SAVE_LINKS
+ if (CmpMSGID(Help[I],MCHelpSwOL))
+ continue;
+#endif
+#ifndef RAR_SMP
+ if (CmpMSGID(Help[I],MCHelpSwMT))
+ continue;
+#endif
+#endif
+ mprintf(St(Help[I]));
+ }
+ mprintf(L"\n");
+ ErrHandler.Exit(ExitCode);
+#endif
+}
+
diff --git a/unrar/unrar/coder.cpp b/unrar/unrar/coder.cpp
index cb6a883..9d971a8 100644
--- a/unrar/unrar/coder.cpp
+++ b/unrar/unrar/coder.cpp
@@ -17,9 +17,10 @@ void RangeCoder::InitDecoder(Unpack *UnpackRead)
}
+// (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=-low&(BOT-1)),1)) \
+ while ((low^(low+range))<TOP || range<BOT && ((range=-(int)low&(BOT-1)),1)) \
{ \
code=(code << 8) | read->GetChar(); \
range <<= 8; \
diff --git a/unrar/unrar/coder.hpp b/unrar/unrar/coder.hpp
index f09f911..7b36ff2 100644
--- a/unrar/unrar/coder.hpp
+++ b/unrar/unrar/coder.hpp
@@ -2,7 +2,6 @@
* Contents: 'Carryless rangecoder' by Dmitry Subbotin *
****************************************************************************/
-const uint TOP=1 << 24, BOT=1 << 15;
class RangeCoder
{
diff --git a/unrar/unrar/compress.hpp b/unrar/unrar/compress.hpp
index 3181e45..4ef8570 100644
--- a/unrar/unrar/compress.hpp
+++ b/unrar/unrar/compress.hpp
@@ -1,36 +1,60 @@
#ifndef _RAR_COMPRESS_
#define _RAR_COMPRESS_
-class ComprDataIO;
-class PackingFileTable;
-
-#define CODEBUFSIZE 0x4000
-#define MAXWINSIZE 0x400000
-#define MAXWINMASK (MAXWINSIZE-1)
-
-#define LOW_DIST_REP_COUNT 16
-
-#define NC 299 /* alphabet = {0, 1, 2, ..., NC - 1} */
-#define DC 60
-#define LDC 17
-#define RC 28
-#define HUFF_TABLE_SIZE (NC+DC+RC+LDC)
-#define BC 20
-
-#define NC20 298 /* alphabet = {0, 1, 2, ..., NC - 1} */
-#define DC20 48
-#define RC20 28
-#define BC20 19
-#define MC20 257
-
-enum {CODE_HUFFMAN,CODE_LZ,CODE_LZ2,CODE_REPEATLZ,CODE_CACHELZ,
- CODE_STARTFILE,CODE_ENDFILE,CODE_VM,CODE_VMDATA};
+// Combine pack and unpack constants to class to avoid polluting global
+// namespace with numerous short names.
+class PackDef
+{
+ public:
+ // Maximum LZ match length we can encode even for short distances.
+ static const uint MAX_LZ_MATCH = 0x1001;
+
+ // We increment LZ match length for longer distances, because shortest
+ // matches are not allowed for them. Maximum length increment is 3
+ // for distances larger than 256KB (0x40000). Here we define the maximum
+ // incremented LZ match. Normally packer does not use it, but we must be
+ // ready to process it in corrupt archives.
+ static const uint MAX_INC_LZ_MATCH = MAX_LZ_MATCH + 3;
+
+ static const uint MAX3_LZ_MATCH = 0x101; // Maximum match length for RAR v3.
+ static const uint MAX3_INC_LZ_MATCH = MAX3_LZ_MATCH + 3;
+ 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 {
- FILTER_NONE, FILTER_PPM /*dummy*/, FILTER_E8, FILTER_E8E9,
- FILTER_UPCASETOLOW, FILTER_AUDIO, FILTER_RGB, FILTER_DELTA,
- FILTER_ITANIUM, FILTER_E8E9V2
+ // 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
diff --git a/unrar/unrar/consio.cpp b/unrar/unrar/consio.cpp
index 6644dce..fa35d61 100644
--- a/unrar/unrar/consio.cpp
+++ b/unrar/unrar/consio.cpp
@@ -1,220 +1,340 @@
#include "rar.hpp"
-
-#ifndef GUI
#include "log.cpp"
+
+static MESSAGE_TYPE MsgStream=MSG_STDOUT;
+static RAR_CHARSET RedirectCharset=RCH_DEFAULT;
+static bool ProhibitInput=false;
+
+const int MaxMsgSize=2*NM+2048;
+
+static bool StdoutRedirected=false,StderrRedirected=false,StdinRedirected=false;
+
+#ifdef _WIN_ALL
+static bool IsRedirected(DWORD nStdHandle)
+{
+ HANDLE hStd=GetStdHandle(nStdHandle);
+ DWORD Mode;
+ return GetFileType(hStd)!=FILE_TYPE_CHAR || GetConsoleMode(hStd,&Mode)==0;
+}
#endif
-static int KbdAnsi(char *Addr,int Size);
-#if !defined(GUI) && !defined(SILENT)
-static void RawPrint(char *Msg,MESSAGE_TYPE MessageType);
-static uint GetKey();
+void InitConsole()
+{
+#ifdef _WIN_ALL
+ // We want messages like file names or progress percent to be printed
+ // immediately. Use only in Windows, in Unix they can cause wprintf %ls
+ // to fail with non-English strings.
+ setbuf(stdout,NULL);
+ setbuf(stderr,NULL);
+
+ // Detect if output is redirected and set output mode properly.
+ // We do not want to send Unicode output to files and especially to pipes
+ // like '|more', which cannot handle them correctly in Windows.
+ // In Unix console output is UTF-8 and it is handled correctly
+ // when redirecting, so no need to perform any adjustments.
+ StdoutRedirected=IsRedirected(STD_OUTPUT_HANDLE);
+ StderrRedirected=IsRedirected(STD_ERROR_HANDLE);
+ StdinRedirected=IsRedirected(STD_INPUT_HANDLE);
+#ifdef _MSC_VER
+ if (!StdoutRedirected)
+ _setmode(_fileno(stdout), _O_U16TEXT);
+ if (!StderrRedirected)
+ _setmode(_fileno(stderr), _O_U16TEXT);
#endif
+#elif defined(_UNIX)
+ StdoutRedirected=!isatty(fileno(stdout));
+ StderrRedirected=!isatty(fileno(stderr));
+ StdinRedirected=!isatty(fileno(stdin));
+#endif
+}
-static MESSAGE_TYPE MsgStream=MSG_STDOUT;
-static bool Sound=false;
-const int MaxMsgSize=2*NM+2048;
-void InitConsoleOptions(MESSAGE_TYPE MsgStream,bool Sound)
+void SetConsoleMsgStream(MESSAGE_TYPE MsgStream)
{
::MsgStream=MsgStream;
- ::Sound=Sound;
}
-#if !defined(GUI) && !defined(SILENT)
-void mprintf(const char *fmt,...)
+
+void SetConsoleRedirectCharset(RAR_CHARSET RedirectCharset)
{
- if (MsgStream==MSG_NULL || MsgStream==MSG_ERRONLY)
- return;
- safebuf char Msg[MaxMsgSize];
- va_list argptr;
- va_start(argptr,fmt);
- vsprintf(Msg,fmt,argptr);
- RawPrint(Msg,MsgStream);
- va_end(argptr);
+ ::RedirectCharset=RedirectCharset;
}
-#endif
-#if !defined(GUI) && !defined(SILENT)
-void eprintf(const char *fmt,...)
+void ProhibitConsoleInput()
{
- if (MsgStream==MSG_NULL)
- return;
- safebuf char Msg[MaxMsgSize];
- va_list argptr;
- va_start(argptr,fmt);
- vsprintf(Msg,fmt,argptr);
- RawPrint(Msg,MSG_STDERR);
- va_end(argptr);
+ ProhibitInput=true;
}
-#endif
-#if !defined(GUI) && !defined(SILENT)
-void RawPrint(char *Msg,MESSAGE_TYPE MessageType)
+#ifndef SILENT
+static void cvt_wprintf(FILE *dest,const wchar *fmt,va_list arglist)
{
- File OutFile;
- switch(MessageType)
+ // This buffer is for format string only, not for entire output,
+ // so it can be short enough.
+ wchar fmtw[1024];
+ PrintfPrepareFmt(fmt,fmtw,ASIZE(fmtw));
+#ifdef _WIN_ALL
+ safebuf wchar Msg[MaxMsgSize];
+ if (dest==stdout && StdoutRedirected || dest==stderr && StderrRedirected)
{
- case MSG_STDOUT:
- OutFile.SetHandleType(FILE_HANDLESTD);
- break;
- case MSG_STDERR:
- case MSG_ERRONLY:
- OutFile.SetHandleType(FILE_HANDLEERR);
- break;
- default:
- return;
- }
-#ifdef _WIN_32
- CharToOem(Msg,Msg);
+ HANDLE hOut=GetStdHandle(dest==stdout ? STD_OUTPUT_HANDLE:STD_ERROR_HANDLE);
+ vswprintf(Msg,ASIZE(Msg),fmtw,arglist);
+ DWORD Written;
+ if (RedirectCharset==RCH_UNICODE)
+ WriteFile(hOut,Msg,(DWORD)wcslen(Msg)*sizeof(*Msg),&Written,NULL);
+ else
+ {
+ // Avoid Unicode for redirect in Windows, it does not work with pipes.
+ safebuf char MsgA[MaxMsgSize];
+ if (RedirectCharset==RCH_UTF8)
+ WideToUtf(Msg,MsgA,ASIZE(MsgA));
+ else
+ WideToChar(Msg,MsgA,ASIZE(MsgA));
+ if (RedirectCharset==RCH_DEFAULT || RedirectCharset==RCH_OEM)
+ CharToOemA(MsgA,MsgA); // Console tools like 'more' expect OEM encoding.
- char OutMsg[MaxMsgSize],*OutPos=OutMsg;
- for (int I=0;Msg[I]!=0;I++)
- {
- if (Msg[I]=='\n' && (I==0 || Msg[I-1]!='\r'))
- *(OutPos++)='\r';
- *(OutPos++)=Msg[I];
+ // We already converted \n to \r\n above, so we use WriteFile instead
+ // of C library to avoid unnecessary additional conversion.
+ WriteFile(hOut,MsgA,(DWORD)strlen(MsgA),&Written,NULL);
+ }
+ return;
}
- *OutPos=0;
- strcpy(Msg,OutMsg);
-#endif
-#if defined(_UNIX) || defined(_EMX)
- char OutMsg[MaxMsgSize],*OutPos=OutMsg;
- for (int I=0;Msg[I]!=0;I++)
- if (Msg[I]!='\r')
- *(OutPos++)=Msg[I];
- *OutPos=0;
- strcpy(Msg,OutMsg);
+ // MSVC2008 vfwprintf writes every character to console separately
+ // and it is too slow. We use direct WriteConsole call instead.
+ vswprintf(Msg,ASIZE(Msg),fmtw,arglist);
+ HANDLE hOut=GetStdHandle(dest==stderr ? STD_ERROR_HANDLE:STD_OUTPUT_HANDLE);
+ DWORD Written;
+ WriteConsole(hOut,Msg,(DWORD)wcslen(Msg),&Written,NULL);
+#else
+ vfwprintf(dest,fmtw,arglist);
+ // We do not use setbuf(NULL) in Unix (see comments in InitConsole).
+ fflush(dest);
#endif
+}
- OutFile.Write(Msg,strlen(Msg));
-// OutFile.Flush();
+
+void mprintf(const wchar *fmt,...)
+{
+ if (MsgStream==MSG_NULL || MsgStream==MSG_ERRONLY)
+ return;
+
+ fflush(stderr); // Ensure proper message order.
+
+ va_list arglist;
+ va_start(arglist,fmt);
+ FILE *dest=MsgStream==MSG_STDERR ? stderr:stdout;
+ cvt_wprintf(dest,fmt,arglist);
+ va_end(arglist);
}
#endif
#ifndef SILENT
-void Alarm()
+void eprintf(const wchar *fmt,...)
{
-#ifndef SFX_MODULE
- if (Sound)
- putchar('\007');
-#endif
+ if (MsgStream==MSG_NULL)
+ return;
+
+ fflush(stdout); // Ensure proper message order.
+
+ va_list arglist;
+ va_start(arglist,fmt);
+ cvt_wprintf(stderr,fmt,arglist);
+ va_end(arglist);
}
#endif
#ifndef SILENT
-#ifndef GUI
-void GetPasswordText(char *Str,int MaxLength)
+static void QuitIfInputProhibited()
{
-#ifdef _WIN_32
- HANDLE hConIn=GetStdHandle(STD_INPUT_HANDLE);
- HANDLE hConOut=GetStdHandle(STD_OUTPUT_HANDLE);
- DWORD ConInMode,ConOutMode;
- DWORD Read=0;
- GetConsoleMode(hConIn,&ConInMode);
- GetConsoleMode(hConOut,&ConOutMode);
- SetConsoleMode(hConIn,ENABLE_LINE_INPUT);
- SetConsoleMode(hConOut,ENABLE_PROCESSED_OUTPUT|ENABLE_WRAP_AT_EOL_OUTPUT);
- ReadConsole(hConIn,Str,MaxLength-1,&Read,NULL);
- Str[Read]=0;
- OemToChar(Str,Str);
- SetConsoleMode(hConIn,ConInMode);
- SetConsoleMode(hConOut,ConOutMode);
-#elif defined(_EMX) || defined(_BEOS) || defined(__sparc) || defined(sparc) || defined (__VMS)
- fgets(Str,MaxLength-1,stdin);
+ // We cannot handle user prompts if -si is used to read file or archive data
+ // from stdin.
+ if (ProhibitInput)
+ {
+ mprintf(St(MStdinNoInput));
+ ErrHandler.Exit(RARX_FATAL);
+ }
+}
+
+
+static void GetPasswordText(wchar *Str,uint MaxLength)
+{
+ if (MaxLength==0)
+ return;
+ QuitIfInputProhibited();
+ if (StdinRedirected)
+ getwstr(Str,MaxLength); // Read from pipe or redirected file.
+ else
+ {
+#ifdef _WIN_ALL
+ HANDLE hConIn=GetStdHandle(STD_INPUT_HANDLE);
+ DWORD ConInMode;
+ GetConsoleMode(hConIn,&ConInMode);
+ SetConsoleMode(hConIn,ENABLE_LINE_INPUT); // Remove ENABLE_ECHO_INPUT.
+
+ // We prefer ReadConsole to ReadFile, so we can read Unicode input.
+ DWORD Read=0;
+ ReadConsole(hConIn,Str,MaxLength-1,&Read,NULL);
+ Str[Read]=0;
+ SetConsoleMode(hConIn,ConInMode);
+
+ // If entered password is longer than MAXPASSWORD and truncated,
+ // read its unread part anyway, so it isn't read later as the second
+ // password for -p switch. Low level FlushConsoleInputBuffer doesn't help
+ // for high level ReadConsole, which in line input mode seems to store
+ // the rest of string in its own internal buffer.
+ if (wcschr(Str,'\r')==NULL) // If '\r' is missing, the password was truncated.
+ while (true)
+ {
+ wchar Trail[64];
+ DWORD TrailRead=0;
+ // Use ASIZE(Trail)-1 to reserve the space for trailing 0.
+ ReadConsole(hConIn,Trail,ASIZE(Trail)-1,&TrailRead,NULL);
+ Trail[TrailRead]=0;
+ if (TrailRead==0 || wcschr(Trail,'\r')!=NULL)
+ break;
+ }
+
+#else
+ char StrA[MAXPASSWORD*4]; // "*4" for multibyte UTF-8 characters.
+#if defined(_EMX) || defined (__VMS)
+ fgets(StrA,ASIZE(StrA)-1,stdin);
+#elif defined(__sun)
+ strncpyz(StrA,getpassphrase(""),ASIZE(StrA));
#else
- strncpyz(Str,getpass(""),MaxLength);
+ strncpyz(StrA,getpass(""),ASIZE(StrA));
#endif
+ CharToWide(StrA,Str,MaxLength);
+ cleandata(StrA,sizeof(StrA));
+#endif
+ }
Str[MaxLength-1]=0;
RemoveLF(Str);
}
#endif
-#endif
#ifndef SILENT
-bool GetPassword(PASSWORD_TYPE Type,const char *FileName,char *Password,int MaxLength)
+bool GetConsolePassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password)
{
- Alarm();
+ if (!StdinRedirected)
+ uiAlarm(UIALARM_QUESTION);
+
while (true)
{
- char PromptStr[NM+256];
-#if defined(_EMX) || defined(_BEOS)
- strcpy(PromptStr,St(MAskPswEcho));
-#else
- strcpy(PromptStr,St(MAskPsw));
-#endif
- if (Type!=PASSWORD_GLOBAL)
+// if (!StdinRedirected)
+ if (Type==UIPASSWORD_GLOBAL)
+ eprintf(L"\n%s: ",St(MAskPsw));
+ else
+ eprintf(St(MAskPswFor),FileName);
+
+ wchar PlainPsw[MAXPASSWORD+1];
+ GetPasswordText(PlainPsw,ASIZE(PlainPsw));
+ if (*PlainPsw==0 && Type==UIPASSWORD_GLOBAL)
+ return false;
+ if (wcslen(PlainPsw)>=MAXPASSWORD)
{
- strcat(PromptStr,St(MFor));
- char *NameOnly=PointToName(FileName);
- if (strlen(PromptStr)+strlen(NameOnly)<ASIZE(PromptStr))
- strcat(PromptStr,NameOnly);
+ PlainPsw[MAXPASSWORD-1]=0;
+ uiMsg(UIERROR_TRUNCPSW,MAXPASSWORD-1);
}
- eprintf("\n%s: ",PromptStr);
- GetPasswordText(Password,MaxLength);
- if (*Password==0 && Type==PASSWORD_GLOBAL)
- return(false);
- if (Type==PASSWORD_GLOBAL)
+ if (!StdinRedirected && Type==UIPASSWORD_GLOBAL)
{
eprintf(St(MReAskPsw));
- char CmpStr[MAXPASSWORD];
+ wchar CmpStr[MAXPASSWORD];
GetPasswordText(CmpStr,ASIZE(CmpStr));
- if (*CmpStr==0 || strcmp(Password,CmpStr)!=0)
+ if (*CmpStr==0 || wcscmp(PlainPsw,CmpStr)!=0)
{
eprintf(St(MNotMatchPsw));
- memset(Password,0,MaxLength);
- memset(CmpStr,0,sizeof(CmpStr));
+ cleandata(PlainPsw,sizeof(PlainPsw));
+ cleandata(CmpStr,sizeof(CmpStr));
continue;
}
- memset(CmpStr,0,sizeof(CmpStr));
+ cleandata(CmpStr,sizeof(CmpStr));
}
+ Password->Set(PlainPsw);
+ cleandata(PlainPsw,sizeof(PlainPsw));
break;
}
- return(true);
+ return true;
}
#endif
-#if !defined(GUI) && !defined(SILENT)
-uint GetKey()
+#ifndef SILENT
+bool getwstr(wchar *str,size_t n)
{
- char Str[80];
- bool EndOfFile;
-#if defined(__GNUC__) || defined(sun)
- EndOfFile=(fgets(Str,sizeof(Str),stdin)==NULL);
-#else
- File SrcFile;
- SrcFile.SetHandleType(FILE_HANDLESTD);
- EndOfFile=(SrcFile.Read(Str,sizeof(Str))==0);
-#endif
- if (EndOfFile)
+ // Print buffered prompt title function before waiting for input.
+ fflush(stderr);
+
+ QuitIfInputProhibited();
+
+ *str=0;
+#if defined(_WIN_ALL)
+ // fgetws does not work well with non-English text in Windows,
+ // so we do not use it.
+ if (StdinRedirected) // ReadConsole does not work if redirected.
+ {
+ // fgets does not work well with pipes in Windows in our test.
+ // Let's use files.
+ Array<char> StrA(n*4); // Up to 4 UTF-8 characters per wchar_t.
+ File SrcFile;
+ SrcFile.SetHandleType(FILE_HANDLESTD);
+ SrcFile.SetLineInputMode(true);
+ int ReadSize=SrcFile.Read(&StrA[0],StrA.Size()-1);
+ if (ReadSize<=0)
+ {
+ // Looks like stdin is a null device. We can enter to infinite loop
+ // calling Ask(), so let's better exit.
+ ErrHandler.Exit(RARX_USERBREAK);
+ }
+ StrA[ReadSize]=0;
+
+ // We expect ANSI encoding here, but "echo text|rar ..." to pipe to RAR,
+ // such as send passwords, we get OEM encoding by default, unless we
+ // use "chcp" in console. But we avoid OEM to ANSI conversion,
+ // because we also want to handle ANSI files redirection correctly,
+ // like "rar ... < ansifile.txt".
+ CharToWide(&StrA[0],str,n);
+ cleandata(&StrA[0],StrA.Size()); // We can use this function to enter passwords.
+ }
+ else
{
- // Looks like stdin is a null device. We can enter to infinite loop
- // calling Ask(), so let's better exit.
- ErrHandler.Exit(USER_BREAK);
+ DWORD ReadSize=0;
+ if (ReadConsole(GetStdHandle(STD_INPUT_HANDLE),str,DWORD(n-1),&ReadSize,NULL)==0)
+ return false;
+ str[ReadSize]=0;
}
- return(Str[0]);
+#else
+ if (fgetws(str,n,stdin)==NULL)
+ ErrHandler.Exit(RARX_USERBREAK); // Avoid infinite Ask() loop.
+#endif
+ RemoveLF(str);
+ return true;
}
#endif
-#if !defined(GUI) && !defined(SILENT)
-int Ask(const char *AskStr)
+#ifndef SILENT
+// We allow this function to return 0 in case of invalid input,
+// because it might be convenient to press Enter to some not dangerous
+// prompts like "insert disk with next volume". We should call this function
+// again in case of 0 in dangerous prompt such as overwriting file.
+int Ask(const wchar *AskStr)
{
+ uiAlarm(UIALARM_QUESTION);
+
const int MaxItems=10;
- char Item[MaxItems][40];
+ wchar Item[MaxItems][40];
int ItemKeyPos[MaxItems],NumItems=0;
- for (const char *NextItem=AskStr;NextItem!=NULL;NextItem=strchr(NextItem+1,'_'))
+ for (const wchar *NextItem=AskStr;NextItem!=NULL;NextItem=wcschr(NextItem+1,'_'))
{
- char *CurItem=Item[NumItems];
- strncpyz(CurItem,NextItem+1,ASIZE(Item[0]));
- char *EndItem=strchr(CurItem,'_');
+ wchar *CurItem=Item[NumItems];
+ wcsncpyz(CurItem,NextItem+1,ASIZE(Item[0]));
+ wchar *EndItem=wcschr(CurItem,'_');
if (EndItem!=NULL)
*EndItem=0;
int KeyPos=0,CurKey;
@@ -222,7 +342,7 @@ int Ask(const char *AskStr)
{
bool Found=false;
for (int I=0;I<NumItems && !Found;I++)
- if (loctoupper(Item[I][ItemKeyPos[I]])==loctoupper(CurKey))
+ if (toupperw(Item[I][ItemKeyPos[I]])==toupperw(CurKey))
Found=true;
if (!Found && CurKey!=' ')
break;
@@ -234,61 +354,53 @@ int Ask(const char *AskStr)
for (int I=0;I<NumItems;I++)
{
- eprintf(I==0 ? (NumItems>4 ? "\n":" "):", ");
+ eprintf(I==0 ? (NumItems>3 ? L"\n":L" "):L", ");
int KeyPos=ItemKeyPos[I];
for (int J=0;J<KeyPos;J++)
- eprintf("%c",Item[I][J]);
- eprintf("[%c]%s",Item[I][KeyPos],&Item[I][KeyPos+1]);
+ eprintf(L"%c",Item[I][J]);
+ eprintf(L"[%c]%ls",Item[I][KeyPos],&Item[I][KeyPos+1]);
}
- eprintf(" ");
- int Ch=GetKey();
-#if defined(_WIN_32)
- OemToCharBuff((LPCSTR)&Ch,(LPTSTR)&Ch,1);
-#endif
- Ch=loctoupper(Ch);
+ eprintf(L" ");
+ wchar Str[50];
+ getwstr(Str,ASIZE(Str));
+ wchar Ch=toupperw(Str[0]);
for (int I=0;I<NumItems;I++)
if (Ch==Item[I][ItemKeyPos[I]])
- return(I+1);
- return(0);
+ return I+1;
+ return 0;
}
#endif
-int KbdAnsi(char *Addr,int Size)
+static bool IsCommentUnsafe(const wchar *Data,size_t Size)
{
- int RetCode=0;
-#ifndef GUI
- for (int I=0;I<Size;I++)
- if (Addr[I]==27 && Addr[I+1]=='[')
- {
- for (int J=I+2;J<Size;J++)
+ for (size_t I=0;I<Size;I++)
+ if (Data[I]==27 && Data[I+1]=='[')
+ for (size_t J=I+2;J<Size;J++)
{
- if (Addr[J]=='\"')
- return(2);
- if (!isdigit(Addr[J]) && Addr[J]!=';')
+ // Return true for <ESC>[{key};"{string}"p used to redefine
+ // a keyboard key on some terminals.
+ if (Data[J]=='\"')
+ return true;
+ if (!IsDigit(Data[J]) && Data[J]!=';')
break;
}
- RetCode=1;
- }
-#endif
- return(RetCode);
+ return false;
}
-void OutComment(char *Comment,int Size)
+void OutComment(const wchar *Comment,size_t Size)
{
-#ifndef GUI
- if (KbdAnsi(Comment,Size)==2)
+ if (IsCommentUnsafe(Comment,Size))
return;
- const int MaxOutSize=0x400;
- for (int I=0;I<Size;I+=MaxOutSize)
+ const size_t MaxOutSize=0x400;
+ for (size_t I=0;I<Size;I+=MaxOutSize)
{
- char Msg[MaxOutSize+1];
- int CopySize=Min(MaxOutSize,Size-I);
- strncpy(Msg,Comment+I,CopySize);
+ wchar Msg[MaxOutSize+1];
+ size_t CopySize=Min(MaxOutSize,Size-I);
+ wcsncpy(Msg,Comment+I,CopySize);
Msg[CopySize]=0;
- mprintf("%s",Msg);
+ mprintf(L"%s",Msg);
}
- mprintf("\n");
-#endif
+ mprintf(L"\n");
}
diff --git a/unrar/unrar/consio.hpp b/unrar/unrar/consio.hpp
index b077cea..bf97289 100644
--- a/unrar/unrar/consio.hpp
+++ b/unrar/unrar/consio.hpp
@@ -1,44 +1,28 @@
#ifndef _RAR_CONSIO_
#define _RAR_CONSIO_
-enum {ALARM_SOUND,ERROR_SOUND,QUESTION_SOUND};
-
-enum PASSWORD_TYPE {PASSWORD_GLOBAL,PASSWORD_FILE,PASSWORD_ARCHIVE};
-
-void InitConsoleOptions(MESSAGE_TYPE MsgStream,bool Sound);
+void InitConsole();
+void SetConsoleMsgStream(MESSAGE_TYPE MsgStream);
+void SetConsoleRedirectCharset(RAR_CHARSET RedirectCharset);
+void ProhibitConsoleInput();
+void OutComment(const wchar *Comment,size_t Size);
#ifndef SILENT
-void mprintf(const char *fmt,...);
-void eprintf(const char *fmt,...);
-void Alarm();
-void GetPasswordText(char *Str,int MaxLength);
-bool GetPassword(PASSWORD_TYPE Type,const char *FileName,char *Password,int MaxLength);
-int Ask(const char *AskStr);
+bool GetConsolePassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password);
#endif
-void OutComment(char *Comment,int Size);
-
#ifdef SILENT
-#ifdef __GNUC__
- #define mprintf(args...)
- #define eprintf(args...)
+ inline void mprintf(const wchar *fmt,...) {}
+ inline void eprintf(const wchar *fmt,...) {}
+ inline void Alarm() {}
+ inline int Ask(const wchar *AskStr) {return 0;}
+ inline bool getwstr(wchar *str,size_t n) {return false;}
#else
- #ifdef _MSC_VER
- inline void mprintf(const char *fmt,...) {}
- #else
- inline void mprintf(const char *fmt,const char *a=NULL,const char *b=NULL) {}
- #endif
- inline void eprintf(const char *fmt,const char *a=NULL,const char *b=NULL) {}
- inline void mprintf(const char *fmt,int b) {}
- inline void eprintf(const char *fmt,int b) {}
- inline void mprintf(const char *fmt,const char *a,int b) {}
- inline void eprintf(const char *fmt,const char *a,int b) {}
-#endif
-inline void Alarm() {}
-inline void GetPasswordText(char *Str,int MaxLength) {}
-inline unsigned int GetKey() {return(0);}
-inline bool GetPassword(PASSWORD_TYPE Type,const char *FileName,char *Password,int MaxLength) {return(false);}
-inline int Ask(const char *AskStr) {return(0);}
+ void mprintf(const wchar *fmt,...);
+ void eprintf(const wchar *fmt,...);
+ void Alarm();
+ int Ask(const wchar *AskStr);
+ bool getwstr(wchar *str,size_t n);
#endif
#endif
diff --git a/unrar/unrar/crc.cpp b/unrar/unrar/crc.cpp
index 9758241..0c6aef1 100644
--- a/unrar/unrar/crc.cpp
+++ b/unrar/unrar/crc.cpp
@@ -1,60 +1,103 @@
+// 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 CRCTab[256];
+#ifndef SFX_MODULE
+// User suggested to avoid BSD license in SFX module, so they do not need
+// to include the license to SFX archive.
+#define USE_SLICING
+#endif
+
+static uint crc_tables[8][256]; // Tables for Slicing-by-8.
-void InitCRC()
+
+// Build the classic CRC32 lookup table.
+// We also provide this function to legacy RAR and ZIP decryption code.
+void InitCRC32(uint *CRCTab)
{
- for (int I=0;I<256;I++)
+ if (CRCTab[1]!=0)
+ return;
+ for (uint I=0;I<256;I++)
{
uint C=I;
- for (int J=0;J<8;J++)
- C=(C & 1) ? (C>>1)^0xEDB88320L : (C>>1);
+ for (uint J=0;J<8;J++)
+ C=(C & 1) ? (C>>1)^0xEDB88320 : (C>>1);
CRCTab[I]=C;
}
}
-uint CRC(uint StartCRC,const void *Addr,size_t Size)
+static void InitTables()
{
- if (CRCTab[1]==0)
- InitCRC();
- byte *Data=(byte *)Addr;
-#if defined(LITTLE_ENDIAN) && defined(PRESENT_INT32) && defined(ALLOW_NOT_ALIGNED_INT)
+ InitCRC32(crc_tables[0]);
-#ifdef _MSC_VER
- // avoid a warning about 'Data' pointer truncation in 64 bit mode
- #pragma warning( disable : 4311 )
-#endif
-
- while (Size>0 && ((long)Data & 7))
+#ifdef USE_SLICING
+ for (uint I=0;I<256;I++) // Build additional lookup tables.
{
- StartCRC=CRCTab[(byte)(StartCRC^Data[0])]^(StartCRC>>8);
- Size--;
- Data++;
+ 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;
+ }
}
- while (Size>=8)
+#endif
+}
+
+
+struct CallInitCRC {CallInitCRC() {InitTables();}} static CallInit32;
+
+uint CRC32(uint StartCRC,const void *Addr,size_t Size)
+{
+ byte *Data=(byte *)Addr;
+
+#ifdef USE_SLICING
+ // 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)
{
- StartCRC^=*(uint32 *)Data;
- StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8);
- StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8);
- StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8);
- StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8);
- StartCRC^=*(uint32 *)(Data+4);
- StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8);
- StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8);
- StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8);
- StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8);
- Data+=8;
- Size-=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)];
}
#endif
- for (size_t I=0;I<Size;I++)
- StartCRC=CRCTab[(byte)(StartCRC^Data[I])]^(StartCRC>>8);
- return(StartCRC);
+
+ for (;Size>0;Size--,Data++) // Process left data.
+ StartCRC=crc_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8);
+
+ return StartCRC;
}
+
#ifndef SFX_MODULE
-ushort OldCRC(ushort StartCRC,const void *Addr,size_t Size)
+// 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++)
@@ -62,6 +105,169 @@ ushort OldCRC(ushort StartCRC,const void *Addr,size_t Size)
StartCRC=(StartCRC+Data[I])&0xffff;
StartCRC=((StartCRC<<1)|(StartCRC>>15))&0xffff;
}
- return(StartCRC);
+ return StartCRC;
+}
+#endif
+
+
+#if 0
+static uint64 crc64_tables[8][256]; // Tables for Slicing-by-8 for CRC64.
+
+void InitCRC64(uint64 *CRCTab)
+{
+ const uint64 poly=INT32TO64(0xC96C5795, 0xD7870F42); // 0xC96C5795D7870F42;
+ for (uint I=0;I<256;I++)
+ {
+ uint64 C=I;
+ for (uint J=0;J<8;J++)
+ C=(C & 1) ? (C>>1)^poly: (C>>1);
+ CRCTab[I]=C;
+ }
+}
+
+
+static void InitTables64()
+{
+ InitCRC64(crc64_tables[0]);
+
+ for (uint I=0;I<256;I++) // Build additional lookup tables.
+ {
+ uint64 C=crc64_tables[0][I];
+ for (uint J=1;J<8;J++)
+ {
+ C=crc64_tables[0][(byte)C]^(C>>8);
+ crc64_tables[J][I]=C;
+ }
+ }
}
+
+
+// We cannot place the intialization to CRC64(), because we use this function
+// in multithreaded mode and it conflicts with multithreading.
+struct CallInitCRC64 {CallInitCRC64() {InitTables64();}} static CallInit64;
+
+uint64 CRC64(uint64 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)!=0;Size--,Data++)
+ StartCRC=crc64_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8);
+
+ for (byte *DataEnd=Data+Size/8*8; Data<DataEnd; Data+=8 )
+ {
+ uint64 Index=StartCRC;
+#ifdef BIG_ENDIAN
+ Index ^= (uint64(Data[0])|(uint64(Data[1])<<8)|(uint64(Data[2])<<16)|(uint64(Data[3])<<24))|
+ (uint64(Data[4])<<32)|(uint64(Data[5])<<40)|(uint64(Data[6])<<48)|(uint64(Data[7])<<56);
+#else
+ Index ^= *(uint64 *)Data;
+#endif
+ StartCRC = crc64_tables[ 7 ] [ ( byte ) (Index ) ] ^
+ crc64_tables[ 6 ] [ ( byte ) (Index >> 8 ) ] ^
+ crc64_tables[ 5 ] [ ( byte ) (Index >> 16 ) ] ^
+ crc64_tables[ 4 ] [ ( byte ) (Index >> 24 ) ] ^
+ crc64_tables[ 3 ] [ ( byte ) (Index >> 32 ) ] ^
+ crc64_tables[ 2 ] [ ( byte ) (Index >> 40 ) ] ^
+ crc64_tables[ 1 ] [ ( byte ) (Index >> 48 ) ] ^
+ crc64_tables[ 0 ] [ ( byte ) (Index >> 56 ) ] ;
+ }
+
+ for (Size%=8;Size>0;Size--,Data++) // Process left data.
+ StartCRC=crc64_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8);
+
+ return StartCRC;
+}
+
+
+#if 0
+static void TestCRC();
+struct TestCRCStruct {TestCRCStruct() {TestCRC();exit(0);}} GlobalTesCRC;
+
+void TestCRC()
+{
+ const uint FirstSize=300;
+ byte b[FirstSize];
+
+ if ((CRC32(0xffffffff,(byte*)"testtesttest",12)^0xffffffff)==0x44608e84)
+ mprintf(L"\nCRC32 test1 OK");
+ else
+ mprintf(L"\nCRC32 test1 FAILED");
+
+ if (CRC32(0,(byte*)"te\x80st",5)==0xB2E5C5AE)
+ mprintf(L"\nCRC32 test2 OK");
+ else
+ mprintf(L"\nCRC32 test2 FAILED");
+
+ for (uint I=0;I<14;I++) // Check for possible int sign extension.
+ b[I]=(byte)0x7f+I;
+ if ((CRC32(0xffffffff,b,14)^0xffffffff)==0x1DFA75DA)
+ mprintf(L"\nCRC32 test3 OK");
+ else
+ mprintf(L"\nCRC32 test3 FAILED");
+
+ for (uint I=0;I<FirstSize;I++)
+ b[I]=(byte)I;
+ uint r32=CRC32(0xffffffff,b,FirstSize);
+ for (uint I=FirstSize;I<1024;I++)
+ {
+ b[0]=(byte)I;
+ r32=CRC32(r32,b,1);
+ }
+ if ((r32^0xffffffff)==0xB70B4C26)
+ mprintf(L"\nCRC32 test4 OK");
+ else
+ mprintf(L"\nCRC32 test4 FAILED");
+
+ if ((CRC64(0xffffffffffffffff,(byte*)"testtesttest",12)^0xffffffffffffffff)==0x7B1C2D230EDEB436)
+ mprintf(L"\nCRC64 test1 OK");
+ else
+ mprintf(L"\nCRC64 test1 FAILED");
+
+ if (CRC64(0,(byte*)"te\x80st",5)==0xB5DBF9583A6EED4A)
+ mprintf(L"\nCRC64 test2 OK");
+ else
+ mprintf(L"\nCRC64 test2 FAILED");
+
+ for (uint I=0;I<14;I++) // Check for possible int sign extension.
+ b[I]=(byte)0x7f+I;
+ if ((CRC64(0xffffffffffffffff,b,14)^0xffffffffffffffff)==0xE019941C05B2820C)
+ mprintf(L"\nCRC64 test3 OK");
+ else
+ mprintf(L"\nCRC64 test3 FAILED");
+
+ for (uint I=0;I<FirstSize;I++)
+ b[I]=(byte)I;
+ uint64 r64=CRC64(0xffffffffffffffff,b,FirstSize);
+ for (uint I=FirstSize;I<1024;I++)
+ {
+ b[0]=(byte)I;
+ r64=CRC64(r64,b,1);
+ }
+ if ((r64^0xffffffffffffffff)==0xD51FB58DC789C400)
+ mprintf(L"\nCRC64 test4 OK");
+ else
+ mprintf(L"\nCRC64 test4 FAILED");
+
+ const size_t BufSize=0x100000;
+ byte *Buf=new byte[BufSize];
+ memset(Buf,0,BufSize);
+
+ clock_t StartTime=clock();
+ r32=0xffffffff;
+ const uint BufCount=5000;
+ for (uint I=0;I<BufCount;I++)
+ r32=CRC32(r32,Buf,BufSize);
+ if (r32!=0) // Otherwise compiler optimizer removes CRC calculation.
+ mprintf(L"\nCRC32 speed: %d MB/s",BufCount*1000/(clock()-StartTime));
+
+ StartTime=clock();
+ r64=0xffffffffffffffff;
+ for (uint I=0;I<BufCount;I++)
+ r64=CRC64(r64,Buf,BufSize);
+ if (r64!=0) // Otherwise compiler optimizer removes CRC calculation.
+ mprintf(L"\nCRC64 speed: %d MB/s",BufCount*1000/(clock()-StartTime));
+}
+#endif
+
#endif
diff --git a/unrar/unrar/crc.hpp b/unrar/unrar/crc.hpp
index a632a54..f573454 100644
--- a/unrar/unrar/crc.hpp
+++ b/unrar/unrar/crc.hpp
@@ -1,10 +1,19 @@
#ifndef _RAR_CRC_
#define _RAR_CRC_
-extern uint CRCTab[256];
+// This function is only to intialize external CRC tables. We do not need to
+// call it before calculating CRC32.
+void InitCRC32(uint *CRCTab);
-void InitCRC();
-uint CRC(uint StartCRC,const void *Addr,size_t Size);
-ushort OldCRC(ushort StartCRC,const void *Addr,size_t Size);
+uint CRC32(uint StartCRC,const void *Addr,size_t Size);
+
+#ifndef SFX_MODULE
+ushort Checksum14(ushort StartCRC,const void *Addr,size_t Size);
+#endif
+
+#if 0
+void InitCRC64(uint64 *CRCTab);
+uint64 CRC64(uint64 StartCRC,const void *Addr,size_t Size);
+#endif
#endif
diff --git a/unrar/unrar/crypt.cpp b/unrar/unrar/crypt.cpp
index 04a1d70..d14c78f 100644
--- a/unrar/unrar/crypt.cpp
+++ b/unrar/unrar/crypt.cpp
@@ -1,381 +1,128 @@
#include "rar.hpp"
#ifndef SFX_MODULE
-extern uint CRCTab[256];
+#include "crypt1.cpp"
+#include "crypt2.cpp"
#endif
+#include "crypt3.cpp"
+#include "crypt5.cpp"
-#define NROUNDS 32
-#define rol(x,n,xsize) (((x)<<(n)) | ((x)>>(xsize-(n))))
-#define ror(x,n,xsize) (((x)>>(n)) | ((x)<<(xsize-(n))))
-
-#define substLong(t) ( (uint)SubstTable[(uint)t&255] | \
- ((uint)SubstTable[(int)(t>> 8)&255]<< 8) | \
- ((uint)SubstTable[(int)(t>>16)&255]<<16) | \
- ((uint)SubstTable[(int)(t>>24)&255]<<24) )
-
-CryptKeyCacheItem CryptData::Cache[4];
-int CryptData::CachePos=0;
-
-
-#ifndef SFX_MODULE
-static byte InitSubstTable[256]={
- 215, 19,149, 35, 73,197,192,205,249, 28, 16,119, 48,221, 2, 42,
- 232, 1,177,233, 14, 88,219, 25,223,195,244, 90, 87,239,153,137,
- 255,199,147, 70, 92, 66,246, 13,216, 40, 62, 29,217,230, 86, 6,
- 71, 24,171,196,101,113,218,123, 93, 91,163,178,202, 67, 44,235,
- 107,250, 75,234, 49,167,125,211, 83,114,157,144, 32,193,143, 36,
- 158,124,247,187, 89,214,141, 47,121,228, 61,130,213,194,174,251,
- 97,110, 54,229,115, 57,152, 94,105,243,212, 55,209,245, 63, 11,
- 164,200, 31,156, 81,176,227, 21, 76, 99,139,188,127, 17,248, 51,
- 207,120,189,210, 8,226, 41, 72,183,203,135,165,166, 60, 98, 7,
- 122, 38,155,170, 69,172,252,238, 39,134, 59,128,236, 27,240, 80,
- 131, 3, 85,206,145, 79,154,142,159,220,201,133, 74, 64, 20,129,
- 224,185,138,103,173,182, 43, 34,254, 82,198,151,231,180, 58, 10,
- 118, 26,102, 12, 50,132, 22,191,136,111,162,179, 45, 4,148,108,
- 161, 56, 78,126,242,222, 15,175,146, 23, 33,241,181,190, 77,225,
- 0, 46,169,186, 68, 95,237, 65, 53,208,253,168, 9, 18,100, 52,
- 116,184,160, 96,109, 37, 30,106,140,104,150, 5,204,117,112, 84
-};
-#endif
-
-
-
-void CryptData::DecryptBlock(byte *Buf,int Size)
+CryptData::CryptData()
{
- rin.blockDecrypt(Buf,Size,Buf);
+ Method=CRYPT_NONE;
+ KDF3CachePos=0;
+ KDF5CachePos=0;
+ memset(CRCTab,0,sizeof(CRCTab));
}
-#ifndef SFX_MODULE
-void CryptData::EncryptBlock20(byte *Buf)
-{
- uint A,B,C,D,T,TA,TB;
-#if defined(BIG_ENDIAN) || !defined(PRESENT_INT32) || !defined(ALLOW_NOT_ALIGNED_INT)
- A=((uint)Buf[0]|((uint)Buf[1]<<8)|((uint)Buf[2]<<16)|((uint)Buf[3]<<24))^Key[0];
- B=((uint)Buf[4]|((uint)Buf[5]<<8)|((uint)Buf[6]<<16)|((uint)Buf[7]<<24))^Key[1];
- C=((uint)Buf[8]|((uint)Buf[9]<<8)|((uint)Buf[10]<<16)|((uint)Buf[11]<<24))^Key[2];
- D=((uint)Buf[12]|((uint)Buf[13]<<8)|((uint)Buf[14]<<16)|((uint)Buf[15]<<24))^Key[3];
-#else
- uint32 *BufPtr=(uint32 *)Buf;
- A=BufPtr[0]^Key[0];
- B=BufPtr[1]^Key[1];
- C=BufPtr[2]^Key[2];
- D=BufPtr[3]^Key[3];
-#endif
- for(int I=0;I<NROUNDS;I++)
- {
- T=((C+rol(D,11,32))^Key[I&3]);
- TA=A^substLong(T);
- T=((D^rol(C,17,32))+Key[I&3]);
- TB=B^substLong(T);
- A=C;
- B=D;
- C=TA;
- D=TB;
- }
-#if defined(BIG_ENDIAN) || !defined(PRESENT_INT32) || !defined(ALLOW_NOT_ALIGNED_INT)
- C^=Key[0];
- Buf[0]=(byte)C;
- Buf[1]=(byte)(C>>8);
- Buf[2]=(byte)(C>>16);
- Buf[3]=(byte)(C>>24);
- D^=Key[1];
- Buf[4]=(byte)D;
- Buf[5]=(byte)(D>>8);
- Buf[6]=(byte)(D>>16);
- Buf[7]=(byte)(D>>24);
- A^=Key[2];
- Buf[8]=(byte)A;
- Buf[9]=(byte)(A>>8);
- Buf[10]=(byte)(A>>16);
- Buf[11]=(byte)(A>>24);
- B^=Key[3];
- Buf[12]=(byte)B;
- Buf[13]=(byte)(B>>8);
- Buf[14]=(byte)(B>>16);
- Buf[15]=(byte)(B>>24);
-#else
- BufPtr[0]=C^Key[0];
- BufPtr[1]=D^Key[1];
- BufPtr[2]=A^Key[2];
- BufPtr[3]=B^Key[3];
-#endif
- UpdKeys(Buf);
-}
-void CryptData::DecryptBlock20(byte *Buf)
+void CryptData::DecryptBlock(byte *Buf,size_t Size)
{
- byte InBuf[16];
- uint A,B,C,D,T,TA,TB;
-#if defined(BIG_ENDIAN) || !defined(PRESENT_INT32) || !defined(ALLOW_NOT_ALIGNED_INT)
- A=((uint)Buf[0]|((uint)Buf[1]<<8)|((uint)Buf[2]<<16)|((uint)Buf[3]<<24))^Key[0];
- B=((uint)Buf[4]|((uint)Buf[5]<<8)|((uint)Buf[6]<<16)|((uint)Buf[7]<<24))^Key[1];
- C=((uint)Buf[8]|((uint)Buf[9]<<8)|((uint)Buf[10]<<16)|((uint)Buf[11]<<24))^Key[2];
- D=((uint)Buf[12]|((uint)Buf[13]<<8)|((uint)Buf[14]<<16)|((uint)Buf[15]<<24))^Key[3];
-#else
- uint32 *BufPtr=(uint32 *)Buf;
- A=BufPtr[0]^Key[0];
- B=BufPtr[1]^Key[1];
- C=BufPtr[2]^Key[2];
- D=BufPtr[3]^Key[3];
-#endif
- memcpy(InBuf,Buf,sizeof(InBuf));
- for(int I=NROUNDS-1;I>=0;I--)
+ switch(Method)
{
- T=((C+rol(D,11,32))^Key[I&3]);
- TA=A^substLong(T);
- T=((D^rol(C,17,32))+Key[I&3]);
- TB=B^substLong(T);
- A=C;
- B=D;
- C=TA;
- D=TB;
- }
-#if defined(BIG_ENDIAN) || !defined(PRESENT_INT32) || !defined(ALLOW_NOT_ALIGNED_INT)
- C^=Key[0];
- Buf[0]=(byte)C;
- Buf[1]=(byte)(C>>8);
- Buf[2]=(byte)(C>>16);
- Buf[3]=(byte)(C>>24);
- D^=Key[1];
- Buf[4]=(byte)D;
- Buf[5]=(byte)(D>>8);
- Buf[6]=(byte)(D>>16);
- Buf[7]=(byte)(D>>24);
- A^=Key[2];
- Buf[8]=(byte)A;
- Buf[9]=(byte)(A>>8);
- Buf[10]=(byte)(A>>16);
- Buf[11]=(byte)(A>>24);
- B^=Key[3];
- Buf[12]=(byte)B;
- Buf[13]=(byte)(B>>8);
- Buf[14]=(byte)(B>>16);
- Buf[15]=(byte)(B>>24);
-#else
- BufPtr[0]=C^Key[0];
- BufPtr[1]=D^Key[1];
- BufPtr[2]=A^Key[2];
- BufPtr[3]=B^Key[3];
+#ifndef SFX_MODULE
+ case CRYPT_RAR13:
+ Decrypt13(Buf,Size);
+ break;
+ case CRYPT_RAR15:
+ Crypt15(Buf,Size);
+ break;
+ case CRYPT_RAR20:
+ for (size_t I=0;I<Size;I+=CRYPT_BLOCK_SIZE)
+ DecryptBlock20(Buf+I);
+ break;
#endif
- UpdKeys(InBuf);
+ case CRYPT_RAR30:
+ case CRYPT_RAR50:
+ rin.blockDecrypt(Buf,Size,Buf);
+ break;
+ }
}
-void CryptData::UpdKeys(byte *Buf)
+bool CryptData::SetCryptKeys(bool Encrypt,CRYPT_METHOD Method,
+ SecPassword *Password,const byte *Salt,
+ const byte *InitV,uint Lg2Cnt,byte *HashKey,byte *PswCheck)
{
- for (int I=0;I<16;I+=4)
- {
- Key[0]^=CRCTab[Buf[I]];
- Key[1]^=CRCTab[Buf[I+1]];
- Key[2]^=CRCTab[Buf[I+2]];
- Key[3]^=CRCTab[Buf[I+3]];
- }
-}
+ if (Method==CRYPT_NONE || !Password->IsSet())
+ return false;
+ CryptData::Method=Method;
-void CryptData::Swap(byte *Ch1,byte *Ch2)
-{
- byte Ch=*Ch1;
- *Ch1=*Ch2;
- *Ch2=Ch;
-}
-#endif
+ wchar PwdW[MAXPASSWORD];
+ Password->Get(PwdW,ASIZE(PwdW));
+ PwdW[Min(MAXPASSWORD_RAR,MAXPASSWORD)-1]=0; // For compatibility with existing archives.
+ char PwdA[MAXPASSWORD];
+ WideToChar(PwdW,PwdA,ASIZE(PwdA));
+ PwdA[Min(MAXPASSWORD_RAR,MAXPASSWORD)-1]=0; // For compatibility with existing archives.
-void CryptData::SetCryptKeys(char *Password,byte *Salt,bool Encrypt,bool OldOnly,bool HandsOffHash)
-{
- if (*Password==0)
- return;
- if (OldOnly)
+ switch(Method)
{
#ifndef SFX_MODULE
- if (CRCTab[1]==0)
- InitCRC();
- byte Psw[MAXPASSWORD];
- SetOldKeys(Password);
- Key[0]=0xD3A3B879L;
- Key[1]=0x3F6D12F7L;
- Key[2]=0x7515A235L;
- Key[3]=0xA4E7F123L;
- memset(Psw,0,sizeof(Psw));
-#if defined(_WIN_32) && !defined(GUI)
- CharToOemBuff(Password,(char*)Psw,strlen(Password));
-#else
- strncpyz((char *)Psw,Password,ASIZE(Psw));
-#endif
- int PswLength=strlen(Password);
- memcpy(SubstTable,InitSubstTable,sizeof(SubstTable));
- for (int J=0;J<256;J++)
- for (int I=0;I<PswLength;I+=2)
- {
- uint N1=(byte)CRCTab[(Psw[I]-J)&0xff];
- uint N2=(byte)CRCTab[(Psw[I+1]+J)&0xff];
- for (int K=1;N1!=N2;N1=(N1+1)&0xff,K++)
- Swap(&SubstTable[N1],&SubstTable[(N1+I+K)&0xff]);
- }
- for (int I=0;I<PswLength;I+=16)
- EncryptBlock20(&Psw[I]);
+ case CRYPT_RAR13:
+ SetKey13(PwdA);
+ break;
+ case CRYPT_RAR15:
+ SetKey15(PwdA);
+ break;
+ case CRYPT_RAR20:
+ SetKey20(PwdA);
+ break;
#endif
- return;
- }
-
- bool Cached=false;
- for (int I=0;I<sizeof(Cache)/sizeof(Cache[0]);I++)
- if (strcmp(Cache[I].Password,Password)==0 &&
- (Salt==NULL && !Cache[I].SaltPresent || Salt!=NULL &&
- Cache[I].SaltPresent && memcmp(Cache[I].Salt,Salt,SALT_SIZE)==0) &&
- Cache[I].HandsOffHash==HandsOffHash)
- {
- memcpy(AESKey,Cache[I].AESKey,sizeof(AESKey));
- memcpy(AESInit,Cache[I].AESInit,sizeof(AESInit));
- Cached=true;
+ case CRYPT_RAR30:
+ SetKey30(Encrypt,Password,PwdW,Salt);
+ break;
+ case CRYPT_RAR50:
+ SetKey50(Encrypt,Password,PwdW,Salt,InitV,Lg2Cnt,HashKey,PswCheck);
break;
- }
-
- if (!Cached)
- {
- wchar PswW[MAXPASSWORD];
- CharToWide(Password,PswW,MAXPASSWORD-1);
- PswW[MAXPASSWORD-1]=0;
- byte RawPsw[2*MAXPASSWORD+SALT_SIZE];
- WideToRaw(PswW,RawPsw);
- int RawLength=2*strlenw(PswW);
- if (Salt!=NULL)
- {
- memcpy(RawPsw+RawLength,Salt,SALT_SIZE);
- RawLength+=SALT_SIZE;
- }
- hash_context c;
- hash_initial(&c);
-
- const int HashRounds=0x40000;
- for (int I=0;I<HashRounds;I++)
- {
- hash_process( &c, RawPsw, RawLength, HandsOffHash);
- byte PswNum[3];
- PswNum[0]=(byte)I;
- PswNum[1]=(byte)(I>>8);
- PswNum[2]=(byte)(I>>16);
- hash_process( &c, PswNum, 3, HandsOffHash);
- if (I%(HashRounds/16)==0)
- {
- hash_context tempc=c;
- uint32 digest[5];
- hash_final( &tempc, digest, HandsOffHash);
- AESInit[I/(HashRounds/16)]=(byte)digest[4];
- }
- }
- uint32 digest[5];
- hash_final( &c, digest, HandsOffHash);
- for (int I=0;I<4;I++)
- for (int J=0;J<4;J++)
- AESKey[I*4+J]=(byte)(digest[I]>>(J*8));
-
- strcpy(Cache[CachePos].Password,Password);
- if ((Cache[CachePos].SaltPresent=(Salt!=NULL))==true)
- memcpy(Cache[CachePos].Salt,Salt,SALT_SIZE);
- Cache[CachePos].HandsOffHash=HandsOffHash;
- memcpy(Cache[CachePos].AESKey,AESKey,sizeof(AESKey));
- memcpy(Cache[CachePos].AESInit,AESInit,sizeof(AESInit));
- CachePos=(CachePos+1)%(sizeof(Cache)/sizeof(Cache[0]));
}
- rin.init(Encrypt ? Rijndael::Encrypt : Rijndael::Decrypt,AESKey,AESInit);
+ cleandata(PwdA,sizeof(PwdA));
+ cleandata(PwdW,sizeof(PwdW));
+ return true;
}
-#ifndef SFX_MODULE
-void CryptData::SetOldKeys(char *Password)
+// Use the current system time to additionally randomize data.
+static void TimeRandomize(byte *RndBuf,size_t BufSize)
{
- uint PswCRC=CRC(0xffffffff,Password,strlen(Password));
- OldKey[0]=PswCRC&0xffff;
- OldKey[1]=(PswCRC>>16)&0xffff;
- OldKey[2]=OldKey[3]=0;
- PN1=PN2=PN3=0;
- byte Ch;
- while ((Ch=*Password)!=0)
+ static uint Count=0;
+ RarTime CurTime;
+ CurTime.SetCurrentTime();
+ uint64 Random=CurTime.GetWin()+clock();
+ for (size_t I=0;I<BufSize;I++)
{
- PN1+=Ch;
- PN2^=Ch;
- PN3+=Ch;
- PN3=(byte)rol(PN3,1,8);
- OldKey[2]^=Ch^CRCTab[Ch];
- OldKey[3]+=Ch+(CRCTab[Ch]>>16);
- Password++;
+ byte RndByte = byte (Random >> ( (I & 7) * 8 ));
+ RndBuf[I]=byte( (RndByte ^ I) + Count++);
}
}
-void CryptData::SetAV15Encryption()
-{
- OldKey[0]=0x4765;
- OldKey[1]=0x9021;
- OldKey[2]=0x7382;
- OldKey[3]=0x5215;
-}
-
-
-void CryptData::SetCmt13Encryption()
-{
- PN1=0;
- PN2=7;
- PN3=77;
-}
-void CryptData::Crypt(byte *Data,uint Count,int Method)
+// Fill buffer with random data.
+void GetRnd(byte *RndBuf,size_t BufSize)
{
- if (Method==OLD_DECODE)
- Decode13(Data,Count);
- else
- if (Method==OLD_ENCODE)
- Encode13(Data,Count);
- else
- Crypt15(Data,Count);
-}
-
-
-void CryptData::Encode13(byte *Data,uint Count)
-{
- while (Count--)
- {
- PN2+=PN3;
- PN1+=PN2;
- *Data+=PN1;
- Data++;
- }
-}
-
-
-void CryptData::Decode13(byte *Data,uint Count)
-{
- while (Count--)
+ bool Success=false;
+#if defined(_WIN_ALL)
+ HCRYPTPROV hProvider = 0;
+ if (CryptAcquireContext(&hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
{
- PN2+=PN3;
- PN1+=PN2;
- *Data-=PN1;
- Data++;
+ Success=CryptGenRandom(hProvider, (DWORD)BufSize, RndBuf) == TRUE;
+ CryptReleaseContext(hProvider, 0);
}
-}
-
-
-void CryptData::Crypt15(byte *Data,uint Count)
-{
- while (Count--)
+#elif defined(_UNIX)
+ FILE *rndf = fopen("/dev/urandom", "r");
+ if (rndf!=NULL)
{
- OldKey[0]+=0x1234;
- OldKey[1]^=CRCTab[(OldKey[0] & 0x1fe)>>1];
- OldKey[2]-=CRCTab[(OldKey[0] & 0x1fe)>>1]>>16;
- OldKey[0]^=OldKey[2];
- OldKey[3]=ror(OldKey[3]&0xffff,1,16)^OldKey[1];
- OldKey[3]=ror(OldKey[3]&0xffff,1,16);
- OldKey[0]^=OldKey[3];
- *Data^=(byte)(OldKey[0]>>8);
- Data++;
+ Success=fread(RndBuf, BufSize, 1, rndf) == BufSize;
+ fclose(rndf);
}
-}
#endif
-
-
+ // We use this code only as the last resort if code above failed.
+ if (!Success)
+ TimeRandomize(RndBuf,BufSize);
+}
diff --git a/unrar/unrar/crypt.hpp b/unrar/unrar/crypt.hpp
index e70aaa3..9dc0215 100644
--- a/unrar/unrar/crypt.hpp
+++ b/unrar/unrar/crypt.hpp
@@ -1,62 +1,171 @@
#ifndef _RAR_CRYPT_
#define _RAR_CRYPT_
-enum { OLD_DECODE=0,OLD_ENCODE=1,NEW_CRYPT=2 };
+enum CRYPT_METHOD {
+ CRYPT_NONE,CRYPT_RAR13,CRYPT_RAR15,CRYPT_RAR20,CRYPT_RAR30,CRYPT_RAR50
+};
+
+#define SIZE_SALT50 16
+#define SIZE_SALT30 8
+#define SIZE_INITV 16
+#define SIZE_PSWCHECK 8
+#define SIZE_PSWCHECK_CSUM 4
+
+#define CRYPT_BLOCK_SIZE 16
+#define CRYPT_BLOCK_MASK (CRYPT_BLOCK_SIZE-1) // 0xf
-struct CryptKeyCacheItem
+#define CRYPT5_KDF_LG2_COUNT 15 // LOG2 of PDKDF2 iteration count.
+#define CRYPT5_KDF_LG2_COUNT_MAX 24 // LOG2 of maximum accepted iteration count.
+#define CRYPT_VERSION 0 // Supported encryption version.
+
+
+class CryptData
{
-#ifndef _SFX_RTL_
- CryptKeyCacheItem()
+ struct KDF5CacheItem
{
- *Password=0;
- }
+ SecPassword Pwd;
+ byte Salt[SIZE_SALT50];
+ byte Key[32];
+ uint Lg2Count; // Log2 of PBKDF2 repetition count.
+ byte PswCheckValue[SHA256_DIGEST_SIZE];
+ byte HashKeyValue[SHA256_DIGEST_SIZE];
- ~CryptKeyCacheItem()
+ KDF5CacheItem() {Clean();}
+ ~KDF5CacheItem() {Clean();}
+
+ void Clean()
+ {
+ cleandata(Salt,sizeof(Salt));
+ cleandata(Key,sizeof(Key));
+ cleandata(&Lg2Count,sizeof(Lg2Count));
+ cleandata(PswCheckValue,sizeof(PswCheckValue));
+ cleandata(HashKeyValue,sizeof(HashKeyValue));
+ }
+ };
+
+ struct KDF3CacheItem
{
- memset(AESKey,0,sizeof(AESKey));
- memset(AESInit,0,sizeof(AESInit));
- memset(Password,0,sizeof(Password));
- }
-#endif
- byte AESKey[16],AESInit[16];
- char Password[MAXPASSWORD];
- bool SaltPresent;
- byte Salt[SALT_SIZE];
- bool HandsOffHash;
-};
+ SecPassword Pwd;
+ byte Salt[SIZE_SALT30];
+ byte Key[16];
+ byte Init[16];
+ bool SaltPresent;
+
+ KDF3CacheItem() {Clean();}
+ ~KDF3CacheItem() {Clean();}
+
+ void Clean()
+ {
+ cleandata(Salt,sizeof(Salt));
+ cleandata(Key,sizeof(Key));
+ cleandata(Init,sizeof(Init));
+ cleandata(&SaltPresent,sizeof(SaltPresent));
+ }
+ };
+
-class CryptData
-{
private:
- void Encode13(byte *Data,uint Count);
- void Decode13(byte *Data,uint Count);
- void Crypt15(byte *Data,uint Count);
- void UpdKeys(byte *Buf);
- void Swap(byte *Ch1,byte *Ch2);
- void SetOldKeys(char *Password);
+ void SetKey13(const char *Password);
+ void Decrypt13(byte *Data,size_t Count);
- Rijndael rin;
+ void SetKey15(const char *Password);
+ void Crypt15(byte *Data,size_t Count);
+
+ void SetKey20(const char *Password);
+ void Swap20(byte *Ch1,byte *Ch2);
+ void UpdKeys20(byte *Buf);
+ void EncryptBlock20(byte *Buf);
+ void DecryptBlock20(byte *Buf);
+
+ void SetKey30(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt);
+ void SetKey50(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt,const byte *InitV,uint Lg2Cnt,byte *HashKey,byte *PswCheck);
+
+ KDF3CacheItem KDF3Cache[4];
+ uint KDF3CachePos;
- byte SubstTable[256];
- uint Key[4];
- ushort OldKey[4];
- byte PN1,PN2,PN3;
+ KDF5CacheItem KDF5Cache[4];
+ uint KDF5CachePos;
- byte AESKey[16],AESInit[16];
+ CRYPT_METHOD Method;
- static CryptKeyCacheItem Cache[4];
- static int CachePos;
+ Rijndael rin;
+
+ uint CRCTab[256]; // For RAR 1.5 and RAR 2.0 encryption.
+
+ byte SubstTable20[256];
+ uint Key20[4];
+
+ byte Key13[3];
+ ushort Key15[4];
public:
- void SetCryptKeys(char *Password,byte *Salt,bool Encrypt,bool OldOnly,bool HandsOffHash);
+ CryptData();
+ bool SetCryptKeys(bool Encrypt,CRYPT_METHOD Method,SecPassword *Password,
+ const byte *Salt,const byte *InitV,uint Lg2Cnt,
+ byte *HashKey,byte *PswCheck);
void SetAV15Encryption();
void SetCmt13Encryption();
- void EncryptBlock20(byte *Buf);
- void DecryptBlock20(byte *Buf);
- void EncryptBlock(byte *Buf,int Size);
- void DecryptBlock(byte *Buf,int Size);
- void Crypt(byte *Data,uint Count,int Method);
- static void SetSalt(byte *Salt,int SaltSize);
+ void EncryptBlock(byte *Buf,size_t Size);
+ void DecryptBlock(byte *Buf,size_t Size);
+ static void SetSalt(byte *Salt,size_t SaltSize);
+};
+
+
+class CheckPassword
+{
+ public:
+ enum CONFIDENCE {CONFIDENCE_HIGH,CONFIDENCE_MEDIUM,CONFIDENCE_LOW};
+ virtual CONFIDENCE GetConfidence()=0;
+ virtual bool Check(SecPassword *Password)=0;
};
+class RarCheckPassword:public CheckPassword
+{
+ private:
+ CryptData *Crypt;
+ uint Lg2Count;
+ byte Salt[SIZE_SALT50];
+ byte InitV[SIZE_INITV];
+ byte PswCheck[SIZE_PSWCHECK];
+ public:
+ RarCheckPassword()
+ {
+ Crypt=NULL;
+ }
+ ~RarCheckPassword()
+ {
+ delete Crypt;
+ }
+ void Set(byte *Salt,byte *InitV,uint Lg2Count,byte *PswCheck)
+ {
+ if (Crypt==NULL)
+ Crypt=new CryptData;
+ memcpy(this->Salt,Salt,sizeof(this->Salt));
+ memcpy(this->InitV,InitV,sizeof(this->InitV));
+ this->Lg2Count=Lg2Count;
+ memcpy(this->PswCheck,PswCheck,sizeof(this->PswCheck));
+ }
+ bool IsSet() {return Crypt!=NULL;}
+
+ // RAR5 provides the higly reliable 64 bit password verification value.
+ CONFIDENCE GetConfidence() {return CONFIDENCE_HIGH;}
+
+ bool Check(SecPassword *Password)
+ {
+ byte PswCheck[SIZE_PSWCHECK];
+ Crypt->SetCryptKeys(false,CRYPT_RAR50,Password,Salt,InitV,Lg2Count,NULL,PswCheck);
+ return memcmp(PswCheck,this->PswCheck,sizeof(this->PswCheck))==0;
+ }
+};
+
+void GetRnd(byte *RndBuf,size_t BufSize);
+
+void hmac_sha256(const byte *Key,size_t KeyLength,const byte *Data,
+ size_t DataLength,byte *ResDigest);
+void pbkdf2(const byte *pass, size_t pass_len, const byte *salt,
+ size_t salt_len,byte *key, byte *Value1, byte *Value2,
+ uint rounds);
+
+void ConvertHashToMAC(HashValue *Value,byte *Key);
+
#endif
diff --git a/unrar/unrar/crypt1.cpp b/unrar/unrar/crypt1.cpp
new file mode 100644
index 0000000..1426393
--- /dev/null
+++ b/unrar/unrar/crypt1.cpp
@@ -0,0 +1,79 @@
+extern uint CRCTab[256];
+
+void CryptData::SetKey13(const char *Password)
+{
+ Key13[0]=Key13[1]=Key13[2]=0;
+ for (size_t I=0;Password[I]!=0;I++)
+ {
+ byte P=Password[I];
+ Key13[0]+=P;
+ Key13[1]^=P;
+ Key13[2]+=P;
+ Key13[2]=(byte)rotls(Key13[2],1,8);
+ }
+}
+
+
+void CryptData::SetKey15(const char *Password)
+{
+ InitCRC32(CRCTab);
+ uint PswCRC=CRC32(0xffffffff,Password,strlen(Password));
+ Key15[0]=PswCRC&0xffff;
+ Key15[1]=(PswCRC>>16)&0xffff;
+ Key15[2]=Key15[3]=0;
+ for (size_t I=0;Password[I]!=0;I++)
+ {
+ byte P=Password[I];
+ Key15[2]^=P^CRCTab[P];
+ Key15[3]+=P+(CRCTab[P]>>16);
+ }
+}
+
+
+void CryptData::SetAV15Encryption()
+{
+ InitCRC32(CRCTab);
+ Method=CRYPT_RAR15;
+ Key15[0]=0x4765;
+ Key15[1]=0x9021;
+ Key15[2]=0x7382;
+ Key15[3]=0x5215;
+}
+
+
+void CryptData::SetCmt13Encryption()
+{
+ Method=CRYPT_RAR13;
+ Key13[0]=0;
+ Key13[1]=7;
+ Key13[2]=77;
+}
+
+
+void CryptData::Decrypt13(byte *Data,size_t Count)
+{
+ while (Count--)
+ {
+ Key13[1]+=Key13[2];
+ Key13[0]+=Key13[1];
+ *Data-=Key13[0];
+ Data++;
+ }
+}
+
+
+void CryptData::Crypt15(byte *Data,size_t Count)
+{
+ while (Count--)
+ {
+ Key15[0]+=0x1234;
+ Key15[1]^=CRCTab[(Key15[0] & 0x1fe)>>1];
+ Key15[2]-=CRCTab[(Key15[0] & 0x1fe)>>1]>>16;
+ Key15[0]^=Key15[2];
+ Key15[3]=rotrs(Key15[3]&0xffff,1,16)^Key15[1];
+ Key15[3]=rotrs(Key15[3]&0xffff,1,16);
+ Key15[0]^=Key15[3];
+ *Data^=(byte)(Key15[0]>>8);
+ Data++;
+ }
+}
diff --git a/unrar/unrar/crypt2.cpp b/unrar/unrar/crypt2.cpp
new file mode 100644
index 0000000..5fa4a97
--- /dev/null
+++ b/unrar/unrar/crypt2.cpp
@@ -0,0 +1,133 @@
+#define NROUNDS 32
+
+#define substLong(t) ( (uint)SubstTable20[(uint)t&255] | \
+ ((uint)SubstTable20[(int)(t>> 8)&255]<< 8) | \
+ ((uint)SubstTable20[(int)(t>>16)&255]<<16) | \
+ ((uint)SubstTable20[(int)(t>>24)&255]<<24) )
+
+
+static byte InitSubstTable20[256]={
+ 215, 19,149, 35, 73,197,192,205,249, 28, 16,119, 48,221, 2, 42,
+ 232, 1,177,233, 14, 88,219, 25,223,195,244, 90, 87,239,153,137,
+ 255,199,147, 70, 92, 66,246, 13,216, 40, 62, 29,217,230, 86, 6,
+ 71, 24,171,196,101,113,218,123, 93, 91,163,178,202, 67, 44,235,
+ 107,250, 75,234, 49,167,125,211, 83,114,157,144, 32,193,143, 36,
+ 158,124,247,187, 89,214,141, 47,121,228, 61,130,213,194,174,251,
+ 97,110, 54,229,115, 57,152, 94,105,243,212, 55,209,245, 63, 11,
+ 164,200, 31,156, 81,176,227, 21, 76, 99,139,188,127, 17,248, 51,
+ 207,120,189,210, 8,226, 41, 72,183,203,135,165,166, 60, 98, 7,
+ 122, 38,155,170, 69,172,252,238, 39,134, 59,128,236, 27,240, 80,
+ 131, 3, 85,206,145, 79,154,142,159,220,201,133, 74, 64, 20,129,
+ 224,185,138,103,173,182, 43, 34,254, 82,198,151,231,180, 58, 10,
+ 118, 26,102, 12, 50,132, 22,191,136,111,162,179, 45, 4,148,108,
+ 161, 56, 78,126,242,222, 15,175,146, 23, 33,241,181,190, 77,225,
+ 0, 46,169,186, 68, 95,237, 65, 53,208,253,168, 9, 18,100, 52,
+ 116,184,160, 96,109, 37, 30,106,140,104,150, 5,204,117,112, 84
+};
+
+
+void CryptData::SetKey20(const char *Password)
+{
+ InitCRC32(CRCTab);
+
+ char Psw[MAXPASSWORD];
+ strncpyz(Psw,Password,ASIZE(Psw)); // We'll need to modify it below.
+ size_t PswLength=strlen(Psw);
+
+ Key20[0]=0xD3A3B879L;
+ Key20[1]=0x3F6D12F7L;
+ Key20[2]=0x7515A235L;
+ Key20[3]=0xA4E7F123L;
+
+ memcpy(SubstTable20,InitSubstTable20,sizeof(SubstTable20));
+ for (uint J=0;J<256;J++)
+ for (size_t I=0;I<PswLength;I+=2)
+ {
+ uint N1=(byte)CRCTab [ (byte(Password[I]) - J) &0xff];
+ uint N2=(byte)CRCTab [ (byte(Password[I+1]) + J) &0xff];
+ for (int K=1;N1!=N2;N1=(N1+1)&0xff,K++)
+ Swap20(&SubstTable20[N1],&SubstTable20[(N1+I+K)&0xff]);
+ }
+
+ // Incomplete last block of password must be zero padded.
+ if ((PswLength & CRYPT_BLOCK_MASK)!=0)
+ for (size_t I=PswLength;I<=(PswLength|CRYPT_BLOCK_MASK);I++)
+ Psw[I]=0;
+
+ for (size_t I=0;I<PswLength;I+=CRYPT_BLOCK_SIZE)
+ EncryptBlock20((byte *)Psw+I);
+}
+
+
+void CryptData::EncryptBlock20(byte *Buf)
+{
+ uint A,B,C,D,T,TA,TB;
+ A=RawGet4(Buf+0)^Key20[0];
+ B=RawGet4(Buf+4)^Key20[1];
+ C=RawGet4(Buf+8)^Key20[2];
+ D=RawGet4(Buf+12)^Key20[3];
+ for(int I=0;I<NROUNDS;I++)
+ {
+ T=((C+rotls(D,11,32))^Key20[I&3]);
+ TA=A^substLong(T);
+ T=((D^rotls(C,17,32))+Key20[I&3]);
+ TB=B^substLong(T);
+ A=C;
+ B=D;
+ C=TA;
+ D=TB;
+ }
+ RawPut4(C^Key20[0],Buf+0);
+ RawPut4(D^Key20[1],Buf+4);
+ RawPut4(A^Key20[2],Buf+8);
+ RawPut4(B^Key20[3],Buf+12);
+ UpdKeys20(Buf);
+}
+
+
+void CryptData::DecryptBlock20(byte *Buf)
+{
+ byte InBuf[16];
+ uint A,B,C,D,T,TA,TB;
+ A=RawGet4(Buf+0)^Key20[0];
+ B=RawGet4(Buf+4)^Key20[1];
+ C=RawGet4(Buf+8)^Key20[2];
+ D=RawGet4(Buf+12)^Key20[3];
+ memcpy(InBuf,Buf,sizeof(InBuf));
+ for(int I=NROUNDS-1;I>=0;I--)
+ {
+ T=((C+rotls(D,11,32))^Key20[I&3]);
+ TA=A^substLong(T);
+ T=((D^rotls(C,17,32))+Key20[I&3]);
+ TB=B^substLong(T);
+ A=C;
+ B=D;
+ C=TA;
+ D=TB;
+ }
+ RawPut4(C^Key20[0],Buf+0);
+ RawPut4(D^Key20[1],Buf+4);
+ RawPut4(A^Key20[2],Buf+8);
+ RawPut4(B^Key20[3],Buf+12);
+ UpdKeys20(InBuf);
+}
+
+
+void CryptData::UpdKeys20(byte *Buf)
+{
+ for (int I=0;I<16;I+=4)
+ {
+ Key20[0]^=CRCTab[Buf[I]];
+ Key20[1]^=CRCTab[Buf[I+1]];
+ Key20[2]^=CRCTab[Buf[I+2]];
+ Key20[3]^=CRCTab[Buf[I+3]];
+ }
+}
+
+
+void CryptData::Swap20(byte *Ch1,byte *Ch2)
+{
+ byte Ch=*Ch1;
+ *Ch1=*Ch2;
+ *Ch2=Ch;
+}
diff --git a/unrar/unrar/crypt3.cpp b/unrar/unrar/crypt3.cpp
new file mode 100644
index 0000000..e6e3a82
--- /dev/null
+++ b/unrar/unrar/crypt3.cpp
@@ -0,0 +1,69 @@
+void CryptData::SetKey30(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt)
+{
+ byte AESKey[16],AESInit[16];
+
+ bool Cached=false;
+ for (uint I=0;I<ASIZE(KDF3Cache);I++)
+ if (KDF3Cache[I].Pwd==*Password &&
+ (Salt==NULL && !KDF3Cache[I].SaltPresent || Salt!=NULL &&
+ KDF3Cache[I].SaltPresent && memcmp(KDF3Cache[I].Salt,Salt,SIZE_SALT30)==0))
+ {
+ memcpy(AESKey,KDF3Cache[I].Key,sizeof(AESKey));
+ SecHideData(AESKey,sizeof(AESKey),false,false);
+ memcpy(AESInit,KDF3Cache[I].Init,sizeof(AESInit));
+ Cached=true;
+ break;
+ }
+
+ if (!Cached)
+ {
+ byte RawPsw[2*MAXPASSWORD+SIZE_SALT30];
+ size_t PswLength=wcslen(PwdW);
+ size_t RawLength=2*PswLength;
+ WideToRaw(PwdW,PswLength,RawPsw,RawLength);
+ if (Salt!=NULL)
+ {
+ memcpy(RawPsw+RawLength,Salt,SIZE_SALT30);
+ RawLength+=SIZE_SALT30;
+ }
+ sha1_context c;
+ sha1_init(&c);
+
+ const uint HashRounds=0x40000;
+ for (uint I=0;I<HashRounds;I++)
+ {
+ sha1_process_rar29( &c, RawPsw, RawLength );
+ byte PswNum[3];
+ PswNum[0]=(byte)I;
+ PswNum[1]=(byte)(I>>8);
+ PswNum[2]=(byte)(I>>16);
+ sha1_process(&c, PswNum, 3);
+ if (I%(HashRounds/16)==0)
+ {
+ sha1_context tempc=c;
+ uint32 digest[5];
+ sha1_done( &tempc, digest );
+ AESInit[I/(HashRounds/16)]=(byte)digest[4];
+ }
+ }
+ uint32 digest[5];
+ sha1_done( &c, digest );
+ for (uint I=0;I<4;I++)
+ for (uint J=0;J<4;J++)
+ AESKey[I*4+J]=(byte)(digest[I]>>(J*8));
+
+ KDF3Cache[KDF3CachePos].Pwd=*Password;
+ if ((KDF3Cache[KDF3CachePos].SaltPresent=(Salt!=NULL))==true)
+ memcpy(KDF3Cache[KDF3CachePos].Salt,Salt,SIZE_SALT30);
+ memcpy(KDF3Cache[KDF3CachePos].Key,AESKey,sizeof(AESKey));
+ SecHideData(KDF3Cache[KDF3CachePos].Key,sizeof(KDF3Cache[KDF3CachePos].Key),true,false);
+ memcpy(KDF3Cache[KDF3CachePos].Init,AESInit,sizeof(AESInit));
+ KDF3CachePos=(KDF3CachePos+1)%ASIZE(KDF3Cache);
+
+ cleandata(RawPsw,sizeof(RawPsw));
+ }
+ rin.Init(Encrypt, AESKey, 128, AESInit);
+ cleandata(AESKey,sizeof(AESKey));
+ cleandata(AESInit,sizeof(AESInit));
+}
+
diff --git a/unrar/unrar/crypt5.cpp b/unrar/unrar/crypt5.cpp
new file mode 100644
index 0000000..bb9b2ba
--- /dev/null
+++ b/unrar/unrar/crypt5.cpp
@@ -0,0 +1,241 @@
+static void hmac_sha256(const byte *Key,size_t KeyLength,const byte *Data,
+ size_t DataLength,byte *ResDigest,
+ sha256_context *ICtxOpt,bool *SetIOpt,
+ sha256_context *RCtxOpt,bool *SetROpt)
+{
+ const size_t Sha256BlockSize=64; // As defined in RFC 4868.
+
+ byte KeyHash[SHA256_DIGEST_SIZE];
+ if (KeyLength > Sha256BlockSize) // Convert longer keys to key hash.
+ {
+ sha256_context KCtx;
+ sha256_init(&KCtx);
+ sha256_process(&KCtx, Key, KeyLength);
+ sha256_done(&KCtx, KeyHash);
+
+ Key = KeyHash;
+ KeyLength = SHA256_DIGEST_SIZE;
+ }
+
+ byte KeyBuf[Sha256BlockSize]; // Store the padded key here.
+ sha256_context ICtx;
+
+ if (ICtxOpt!=NULL && *SetIOpt)
+ ICtx=*ICtxOpt; // Use already calculated the first block context.
+ else
+ {
+ // This calculation is the same for all iterations with same password.
+ // So for PBKDF2 we can calculate it only for first block and then reuse
+ // to improve performance.
+
+ for (size_t I = 0; I < KeyLength; I++) // Use 0x36 padding for inner digest.
+ KeyBuf[I] = Key[I] ^ 0x36;
+ for (size_t I = KeyLength; I < Sha256BlockSize; I++)
+ KeyBuf[I] = 0x36;
+
+ sha256_init(&ICtx);
+ sha256_process(&ICtx, KeyBuf, Sha256BlockSize); // Hash padded key.
+ }
+
+ if (ICtxOpt!=NULL && !*SetIOpt) // Store constant context for further reuse.
+ {
+ *ICtxOpt=ICtx;
+ *SetIOpt=true;
+ }
+
+ sha256_process(&ICtx, Data, DataLength); // Hash data.
+
+ byte IDig[SHA256_DIGEST_SIZE]; // Internal digest for padded key and data.
+ sha256_done(&ICtx, IDig);
+
+ sha256_context RCtx;
+
+ if (RCtxOpt!=NULL && *SetROpt)
+ RCtx=*RCtxOpt; // Use already calculated first block context.
+ else
+ {
+ // This calculation is the same for all iterations with same password.
+ // So for PBKDF2 we can calculate it only for first block and then reuse
+ // to improve performance.
+
+ for (size_t I = 0; I < KeyLength; I++) // Use 0x5c for outer key padding.
+ KeyBuf[I] = Key[I] ^ 0x5c;
+ for (size_t I = KeyLength; I < Sha256BlockSize; I++)
+ KeyBuf[I] = 0x5c;
+
+ sha256_init(&RCtx);
+ sha256_process(&RCtx, KeyBuf, Sha256BlockSize); // Hash padded key.
+ }
+
+ if (RCtxOpt!=NULL && !*SetROpt) // Store constant context for further reuse.
+ {
+ *RCtxOpt=RCtx;
+ *SetROpt=true;
+ }
+
+ sha256_process(&RCtx, IDig, SHA256_DIGEST_SIZE); // Hash internal digest.
+
+ sha256_done(&RCtx, ResDigest);
+}
+
+
+// PBKDF2 for 32 byte key length. We generate the key for specified number
+// of iteration count also as two supplementary values (key for checksums
+// and password verification) for iterations+16 and iterations+32.
+void pbkdf2(const byte *Pwd, size_t PwdLength,
+ const byte *Salt, size_t SaltLength,
+ byte *Key, byte *V1, byte *V2, uint Count)
+{
+ const size_t MaxSalt=64;
+ byte SaltData[MaxSalt+4];
+ memcpy(SaltData, Salt, Min(SaltLength,MaxSalt));
+
+ SaltData[SaltLength + 0] = 0; // Block index appened to salt.
+ SaltData[SaltLength + 1] = 0; //
+ SaltData[SaltLength + 2] = 0; // Since we do not request the key width
+ SaltData[SaltLength + 3] = 1; // exceeding HMAC width, it is always 1.
+
+ // First iteration: HMAC of password, salt and block index (1).
+ byte U1[SHA256_DIGEST_SIZE];
+ hmac_sha256(Pwd, PwdLength, SaltData, SaltLength + 4, U1, NULL, NULL, NULL, NULL);
+ byte Fn[SHA256_DIGEST_SIZE]; // Current function value.
+ memcpy(Fn, U1, sizeof(Fn)); // Function at first iteration.
+
+ uint CurCount[] = { Count-1, 16, 16 };
+ byte *CurValue[] = { Key , V1, V2 };
+
+ sha256_context ICtxOpt,RCtxOpt;
+ bool SetIOpt=false,SetROpt=false;
+
+ byte U2[SHA256_DIGEST_SIZE];
+ for (uint I = 0; I < 3; I++) // For output key and 2 supplementary values.
+ {
+ for (uint J = 0; J < CurCount[I]; J++)
+ {
+ // U2 = PRF (P, U1).
+ hmac_sha256(Pwd, PwdLength, U1, sizeof(U1), U2, &ICtxOpt, &SetIOpt, &RCtxOpt, &SetROpt);
+ memcpy(U1, U2, sizeof(U1));
+ for (uint K = 0; K < sizeof(Fn); K++) // Function ^= U.
+ Fn[K] ^= U1[K];
+ }
+ memcpy(CurValue[I], Fn, SHA256_DIGEST_SIZE);
+ }
+
+ cleandata(SaltData, sizeof(SaltData));
+ cleandata(Fn, sizeof(Fn));
+ cleandata(U1, sizeof(U1));
+ cleandata(U2, sizeof(U2));
+}
+
+
+void CryptData::SetKey50(bool Encrypt,SecPassword *Password,const wchar *PwdW,
+ const byte *Salt,const byte *InitV,uint Lg2Cnt,byte *HashKey,
+ byte *PswCheck)
+{
+ if (Lg2Cnt>CRYPT5_KDF_LG2_COUNT_MAX)
+ {
+ // Initialize these fields to prevent uninitialized data access warnings
+ // by analyzing tools when accessing returned data.
+ if (HashKey!=nullptr)
+ memset(HashKey,0,SHA256_DIGEST_SIZE);
+ if (PswCheck!=nullptr)
+ memset(PswCheck,0,SIZE_PSWCHECK);
+ return;
+ }
+
+ byte Key[32],PswCheckValue[SHA256_DIGEST_SIZE],HashKeyValue[SHA256_DIGEST_SIZE];
+ bool Found=false;
+ for (uint I=0;I<ASIZE(KDF5Cache);I++)
+ {
+ KDF5CacheItem *Item=KDF5Cache+I;
+ if (Item->Pwd==*Password && Item->Lg2Count==Lg2Cnt &&
+ memcmp(Item->Salt,Salt,SIZE_SALT50)==0)
+ {
+ memcpy(Key,Item->Key,sizeof(Key));
+ SecHideData(Key,sizeof(Key),false,false);
+
+ memcpy(PswCheckValue,Item->PswCheckValue,sizeof(PswCheckValue));
+ memcpy(HashKeyValue,Item->HashKeyValue,sizeof(HashKeyValue));
+ Found=true;
+ break;
+ }
+ }
+
+ if (!Found)
+ {
+ char PwdUtf[MAXPASSWORD*4];
+ WideToUtf(PwdW,PwdUtf,ASIZE(PwdUtf));
+
+ pbkdf2((byte *)PwdUtf,strlen(PwdUtf),Salt,SIZE_SALT50,Key,HashKeyValue,PswCheckValue,(1<<Lg2Cnt));
+ cleandata(PwdUtf,sizeof(PwdUtf));
+
+ KDF5CacheItem *Item=KDF5Cache+(KDF5CachePos++ % ASIZE(KDF5Cache));
+ Item->Lg2Count=Lg2Cnt;
+ Item->Pwd=*Password;
+ memcpy(Item->Salt,Salt,SIZE_SALT50);
+ memcpy(Item->Key,Key,sizeof(Item->Key));
+ memcpy(Item->PswCheckValue,PswCheckValue,sizeof(PswCheckValue));
+ memcpy(Item->HashKeyValue,HashKeyValue,sizeof(HashKeyValue));
+ SecHideData(Item->Key,sizeof(Item->Key),true,false);
+ }
+ if (HashKey!=NULL)
+ memcpy(HashKey,HashKeyValue,SHA256_DIGEST_SIZE);
+ if (PswCheck!=NULL)
+ {
+ memset(PswCheck,0,SIZE_PSWCHECK);
+ for (uint I=0;I<SHA256_DIGEST_SIZE;I++)
+ PswCheck[I%SIZE_PSWCHECK]^=PswCheckValue[I];
+ cleandata(PswCheckValue,sizeof(PswCheckValue));
+ }
+
+ // NULL initialization vector is possible if we only need the password
+ // check value for archive encryption header.
+ if (InitV!=NULL)
+ rin.Init(Encrypt, Key, 256, InitV);
+
+ cleandata(Key,sizeof(Key));
+}
+
+
+void ConvertHashToMAC(HashValue *Value,byte *Key)
+{
+ if (Value->Type==HASH_CRC32)
+ {
+ byte RawCRC[4];
+ RawPut4(Value->CRC32,RawCRC);
+ byte Digest[SHA256_DIGEST_SIZE];
+ hmac_sha256(Key,SHA256_DIGEST_SIZE,RawCRC,sizeof(RawCRC),Digest,NULL,NULL,NULL,NULL);
+ Value->CRC32=0;
+ for (uint I=0;I<ASIZE(Digest);I++)
+ Value->CRC32^=Digest[I] << ((I & 3) * 8);
+ }
+ if (Value->Type==HASH_BLAKE2)
+ {
+ byte Digest[BLAKE2_DIGEST_SIZE];
+ hmac_sha256(Key,BLAKE2_DIGEST_SIZE,Value->Digest,sizeof(Value->Digest),Digest,NULL,NULL,NULL,NULL);
+ memcpy(Value->Digest,Digest,sizeof(Value->Digest));
+ }
+}
+
+
+#if 0
+static void TestPBKDF2();
+struct TestKDF {TestKDF() {TestPBKDF2();exit(0);}} GlobalTestKDF;
+
+void TestPBKDF2() // Test PBKDF2 HMAC-SHA256
+{
+ byte Key[32],V1[32],V2[32];
+
+ pbkdf2((byte *)"password", 8, (byte *)"salt", 4, Key, V1, V2, 1);
+ byte Res1[32]={0x12, 0x0f, 0xb6, 0xcf, 0xfc, 0xf8, 0xb3, 0x2c, 0x43, 0xe7, 0x22, 0x52, 0x56, 0xc4, 0xf8, 0x37, 0xa8, 0x65, 0x48, 0xc9, 0x2c, 0xcc, 0x35, 0x48, 0x08, 0x05, 0x98, 0x7c, 0xb7, 0x0b, 0xe1, 0x7b };
+ mprintf(L"\nPBKDF2 test1: %s", memcmp(Key,Res1,32)==0 ? L"OK":L"Failed");
+
+ pbkdf2((byte *)"password", 8, (byte *)"salt", 4, Key, V1, V2, 4096);
+ byte Res2[32]={0xc5, 0xe4, 0x78, 0xd5, 0x92, 0x88, 0xc8, 0x41, 0xaa, 0x53, 0x0d, 0xb6, 0x84, 0x5c, 0x4c, 0x8d, 0x96, 0x28, 0x93, 0xa0, 0x01, 0xce, 0x4e, 0x11, 0xa4, 0x96, 0x38, 0x73, 0xaa, 0x98, 0x13, 0x4a };
+ mprintf(L"\nPBKDF2 test2: %s", memcmp(Key,Res2,32)==0 ? L"OK":L"Failed");
+
+ pbkdf2((byte *)"just some long string pretending to be a password", 49, (byte *)"salt, salt, salt, a lot of salt", 31, Key, V1, V2, 65536);
+ byte Res3[32]={0x08, 0x0f, 0xa3, 0x1d, 0x42, 0x2d, 0xb0, 0x47, 0x83, 0x9b, 0xce, 0x3a, 0x3b, 0xce, 0x49, 0x51, 0xe2, 0x62, 0xb9, 0xff, 0x76, 0x2f, 0x57, 0xe9, 0xc4, 0x71, 0x96, 0xce, 0x4b, 0x6b, 0x6e, 0xbf};
+ mprintf(L"\nPBKDF2 test3: %s", memcmp(Key,Res3,32)==0 ? L"OK":L"Failed");
+}
+#endif
diff --git a/unrar/unrar/dll.cpp b/unrar/unrar/dll.cpp
index 18e62bd..178d559 100644
--- a/unrar/unrar/dll.cpp
+++ b/unrar/unrar/dll.cpp
@@ -1,17 +1,16 @@
#include "rar.hpp"
-#include "dll.hpp"
-static int RarErrorToDll(int ErrCode);
+static int RarErrorToDll(RAR_EXIT ErrCode);
struct DataSet
{
CommandData Cmd;
- CmdExtract Extract;
Archive Arc;
+ CmdExtract Extract;
int OpenMode;
int HeaderSize;
- DataSet():Arc(&Cmd) {};
+ DataSet():Arc(&Cmd),Extract(&Cmd) {};
};
@@ -27,124 +26,183 @@ HANDLE PASCAL RAROpenArchive(struct RAROpenArchiveData *r)
r->OpenResult=rx.OpenResult;
r->CmtSize=rx.CmtSize;
r->CmtState=rx.CmtState;
- return(hArc);
+ return hArc;
}
HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *r)
{
+ DataSet *Data=NULL;
try
{
+ ErrHandler.Clean();
+
r->OpenResult=0;
- DataSet *Data=new DataSet;
+ Data=new DataSet;
Data->Cmd.DllError=0;
Data->OpenMode=r->OpenMode;
- Data->Cmd.FileArgs->AddString("*");
+ Data->Cmd.FileArgs.AddString(L"*");
+ Data->Cmd.KeepBroken=(r->OpFlags&ROADOF_KEEPBROKEN)!=0;
- char an[NM];
- if (r->ArcName==NULL && r->ArcNameW!=NULL)
+ char AnsiArcName[NM];
+ *AnsiArcName=0;
+ if (r->ArcName!=NULL)
{
- WideToChar(r->ArcNameW,an,NM);
- r->ArcName=an;
+ strncpyz(AnsiArcName,r->ArcName,ASIZE(AnsiArcName));
+#ifdef _WIN_ALL
+ if (!AreFileApisANSI())
+ {
+ OemToCharBuffA(r->ArcName,AnsiArcName,ASIZE(AnsiArcName));
+ AnsiArcName[ASIZE(AnsiArcName)-1]=0;
+ }
+#endif
}
- Data->Cmd.AddArcName(r->ArcName,r->ArcNameW);
+ wchar ArcName[NM];
+ GetWideName(AnsiArcName,r->ArcNameW,ArcName,ASIZE(ArcName));
+
+ Data->Cmd.AddArcName(ArcName);
Data->Cmd.Overwrite=OVERWRITE_ALL;
Data->Cmd.VersionControl=1;
- if (!Data->Arc.Open(r->ArcName,r->ArcNameW))
+
+ Data->Cmd.Callback=r->Callback;
+ Data->Cmd.UserData=r->UserData;
+
+ // Open shared mode is added by request of dll users, who need to
+ // browse and unpack archives while downloading.
+ Data->Cmd.OpenShared = true;
+ if (!Data->Arc.Open(ArcName,FMF_OPENSHARED))
{
r->OpenResult=ERAR_EOPEN;
delete Data;
- return(NULL);
+ return NULL;
}
- if (!Data->Arc.IsArchive(false))
+ if (!Data->Arc.IsArchive(true))
{
- r->OpenResult=Data->Cmd.DllError!=0 ? Data->Cmd.DllError:ERAR_BAD_ARCHIVE;
+ if (Data->Cmd.DllError!=0)
+ r->OpenResult=Data->Cmd.DllError;
+ else
+ {
+ RAR_EXIT ErrCode=ErrHandler.GetErrorCode();
+ if (ErrCode!=RARX_SUCCESS && ErrCode!=RARX_WARNING)
+ r->OpenResult=RarErrorToDll(ErrCode);
+ else
+ r->OpenResult=ERAR_BAD_ARCHIVE;
+ }
delete Data;
- return(NULL);
+ return NULL;
}
- r->Flags=Data->Arc.NewMhd.Flags;
- Array<byte> CmtData;
- if (r->CmtBufSize!=0 && Data->Arc.GetComment(&CmtData,NULL))
+ r->Flags=0;
+
+ if (Data->Arc.Volume)
+ r->Flags|=ROADF_VOLUME;
+ if (Data->Arc.MainComment)
+ r->Flags|=ROADF_COMMENT;
+ if (Data->Arc.Locked)
+ r->Flags|=ROADF_LOCK;
+ if (Data->Arc.Solid)
+ r->Flags|=ROADF_SOLID;
+ if (Data->Arc.NewNumbering)
+ r->Flags|=ROADF_NEWNUMBERING;
+ if (Data->Arc.Signed)
+ r->Flags|=ROADF_SIGNED;
+ if (Data->Arc.Protected)
+ r->Flags|=ROADF_RECOVERY;
+ if (Data->Arc.Encrypted)
+ r->Flags|=ROADF_ENCHEADERS;
+ if (Data->Arc.FirstVolume)
+ r->Flags|=ROADF_FIRSTVOLUME;
+
+ Array<wchar> CmtDataW;
+ if (r->CmtBufSize!=0 && Data->Arc.GetComment(&CmtDataW))
{
- r->Flags|=2;
- int Size=CmtData.Size()+1;
- r->CmtState=Size>r->CmtBufSize ? ERAR_SMALL_BUF:1;
- r->CmtSize=Min(Size,r->CmtBufSize);
- memcpy(r->CmtBuf,&CmtData[0],r->CmtSize-1);
- if (Size<=r->CmtBufSize)
- r->CmtBuf[r->CmtSize-1]=0;
+ if (r->CmtBufW!=NULL)
+ {
+ CmtDataW.Push(0);
+ size_t Size=wcslen(&CmtDataW[0])+1;
+
+ r->CmtState=Size>r->CmtBufSize ? ERAR_SMALL_BUF:1;
+ r->CmtSize=(uint)Min(Size,r->CmtBufSize);
+ memcpy(r->CmtBufW,&CmtDataW[0],(r->CmtSize-1)*sizeof(*r->CmtBufW));
+ r->CmtBufW[r->CmtSize-1]=0;
+ }
+ else
+ if (r->CmtBuf!=NULL)
+ {
+ Array<char> CmtData(CmtDataW.Size()*4+1);
+ memset(&CmtData[0],0,CmtData.Size());
+ WideToChar(&CmtDataW[0],&CmtData[0],CmtData.Size()-1);
+ size_t Size=strlen(&CmtData[0])+1;
+
+ r->CmtState=Size>r->CmtBufSize ? ERAR_SMALL_BUF:1;
+ r->CmtSize=(uint)Min(Size,r->CmtBufSize);
+ memcpy(r->CmtBuf,&CmtData[0],r->CmtSize-1);
+ r->CmtBuf[r->CmtSize-1]=0;
+ }
}
else
r->CmtState=r->CmtSize=0;
- if (Data->Arc.Signed)
- r->Flags|=0x20;
- Data->Extract.ExtractArchiveInit(&Data->Cmd,Data->Arc);
- return((HANDLE)Data);
+ Data->Extract.ExtractArchiveInit(Data->Arc);
+ return (HANDLE)Data;
+ }
+ catch (RAR_EXIT ErrCode)
+ {
+ if (Data!=NULL && Data->Cmd.DllError!=0)
+ r->OpenResult=Data->Cmd.DllError;
+ else
+ r->OpenResult=RarErrorToDll(ErrCode);
+ if (Data != NULL)
+ delete Data;
+ return NULL;
}
- catch (int ErrCode)
+ catch (std::bad_alloc&) // Catch 'new' exception.
{
- r->OpenResult=RarErrorToDll(ErrCode);
- return(NULL);
+ r->OpenResult=ERAR_NO_MEMORY;
+ if (Data != NULL)
+ delete Data;
}
+ return NULL; // To make compilers happy.
}
int PASCAL RARCloseArchive(HANDLE hArcData)
{
DataSet *Data=(DataSet *)hArcData;
- bool Success=Data==NULL ? false:Data->Arc.Close();
- delete Data;
- return(Success ? 0:ERAR_ECLOSE);
-}
-
-
-int PASCAL RARReadHeader(HANDLE hArcData,struct RARHeaderData *D)
-{
- DataSet *Data=(DataSet *)hArcData;
try
{
- if ((Data->HeaderSize=Data->Arc.SearchBlock(FILE_HEAD))<=0)
- {
- if (Data->Arc.Volume && Data->Arc.GetHeaderType()==ENDARC_HEAD &&
- (Data->Arc.EndArcHead.Flags & EARC_NEXT_VOLUME))
- if (MergeArchive(Data->Arc,NULL,false,'L'))
- {
- Data->Extract.SignatureFound=false;
- Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET);
- return(RARReadHeader(hArcData,D));
- }
- else
- return(ERAR_EOPEN);
- return(Data->Arc.BrokenFileHeader ? ERAR_BAD_DATA:ERAR_END_ARCHIVE);
- }
- if (Data->OpenMode==RAR_OM_LIST && (Data->Arc.NewLhd.Flags & LHD_SPLIT_BEFORE))
- {
- int Code=RARProcessFile(hArcData,RAR_SKIP,NULL,NULL);
- if (Code==0)
- return(RARReadHeader(hArcData,D));
- else
- return(Code);
- }
- strncpyz(D->ArcName,Data->Arc.FileName,ASIZE(D->ArcName));
- strncpyz(D->FileName,Data->Arc.NewLhd.FileName,ASIZE(D->FileName));
- D->Flags=Data->Arc.NewLhd.Flags;
- D->PackSize=Data->Arc.NewLhd.PackSize;
- D->UnpSize=Data->Arc.NewLhd.UnpSize;
- D->HostOS=Data->Arc.NewLhd.HostOS;
- D->FileCRC=Data->Arc.NewLhd.FileCRC;
- D->FileTime=Data->Arc.NewLhd.FileTime;
- D->UnpVer=Data->Arc.NewLhd.UnpVer;
- D->Method=Data->Arc.NewLhd.Method;
- D->FileAttr=Data->Arc.NewLhd.FileAttr;
- D->CmtSize=0;
- D->CmtState=0;
+ bool Success=Data==NULL ? false:Data->Arc.Close();
+ delete Data;
+ return Success ? ERAR_SUCCESS : ERAR_ECLOSE;
}
- catch (int ErrCode)
+ catch (RAR_EXIT ErrCode)
{
- return(RarErrorToDll(ErrCode));
+ return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode);
}
- return(0);
+}
+
+
+int PASCAL RARReadHeader(HANDLE hArcData,struct RARHeaderData *D)
+{
+ struct RARHeaderDataEx X;
+ memset(&X,0,sizeof(X));
+
+ int Code=RARReadHeaderEx(hArcData,&X);
+
+ strncpyz(D->ArcName,X.ArcName,ASIZE(D->ArcName));
+ strncpyz(D->FileName,X.FileName,ASIZE(D->FileName));
+ D->Flags=X.Flags;
+ D->PackSize=X.PackSize;
+ D->UnpSize=X.UnpSize;
+ D->HostOS=X.HostOS;
+ D->FileCRC=X.FileCRC;
+ D->FileTime=X.FileTime;
+ D->UnpVer=X.UnpVer;
+ D->Method=X.Method;
+ D->FileAttr=X.FileAttr;
+ D->CmtSize=0;
+ D->CmtState=0;
+
+ return Code;
}
@@ -153,65 +211,114 @@ int PASCAL RARReadHeaderEx(HANDLE hArcData,struct RARHeaderDataEx *D)
DataSet *Data=(DataSet *)hArcData;
try
{
- if ((Data->HeaderSize=Data->Arc.SearchBlock(FILE_HEAD))<=0)
+ if ((Data->HeaderSize=(int)Data->Arc.SearchBlock(HEAD_FILE))<=0)
{
- if (Data->Arc.Volume && Data->Arc.GetHeaderType()==ENDARC_HEAD &&
- (Data->Arc.EndArcHead.Flags & EARC_NEXT_VOLUME))
+ if (Data->Arc.Volume && Data->Arc.GetHeaderType()==HEAD_ENDARC &&
+ Data->Arc.EndArcHead.NextVolume)
if (MergeArchive(Data->Arc,NULL,false,'L'))
{
- Data->Extract.SignatureFound=false;
Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET);
- return(RARReadHeaderEx(hArcData,D));
+ return RARReadHeaderEx(hArcData,D);
}
else
- return(ERAR_EOPEN);
- return(Data->Arc.BrokenFileHeader ? ERAR_BAD_DATA:ERAR_END_ARCHIVE);
+ return ERAR_EOPEN;
+
+ if (Data->Arc.BrokenHeader)
+ return ERAR_BAD_DATA;
+
+ // Might be necessary if RARSetPassword is still called instead of
+ // open callback for RAR5 archives and if password is invalid.
+ if (Data->Arc.FailedHeaderDecryption)
+ return ERAR_BAD_PASSWORD;
+
+ return ERAR_END_ARCHIVE;
}
- if (Data->OpenMode==RAR_OM_LIST && (Data->Arc.NewLhd.Flags & LHD_SPLIT_BEFORE))
+ FileHeader *hd=&Data->Arc.FileHead;
+ if (Data->OpenMode==RAR_OM_LIST && hd->SplitBefore)
{
int Code=RARProcessFile(hArcData,RAR_SKIP,NULL,NULL);
if (Code==0)
- return(RARReadHeaderEx(hArcData,D));
+ return RARReadHeaderEx(hArcData,D);
else
- return(Code);
+ return Code;
}
- strncpyz(D->ArcName,Data->Arc.FileName,ASIZE(D->ArcName));
- if (*Data->Arc.FileNameW)
- strncpyw(D->ArcNameW,Data->Arc.FileNameW,sizeof(D->ArcNameW));
- else
- CharToWide(Data->Arc.FileName,D->ArcNameW);
- strncpyz(D->FileName,Data->Arc.NewLhd.FileName,ASIZE(D->FileName));
- if (*Data->Arc.NewLhd.FileNameW)
- strncpyw(D->FileNameW,Data->Arc.NewLhd.FileNameW,sizeof(D->FileNameW));
- else
- {
-#ifdef _WIN_32
- char AnsiName[NM];
- OemToChar(Data->Arc.NewLhd.FileName,AnsiName);
- CharToWide(AnsiName,D->FileNameW);
-#else
- CharToWide(Data->Arc.NewLhd.FileName,D->FileNameW);
+ wcsncpyz(D->ArcNameW,Data->Arc.FileName,ASIZE(D->ArcNameW));
+ WideToChar(D->ArcNameW,D->ArcName,ASIZE(D->ArcName));
+
+ wcsncpyz(D->FileNameW,hd->FileName,ASIZE(D->FileNameW));
+ WideToChar(D->FileNameW,D->FileName,ASIZE(D->FileName));
+#ifdef _WIN_ALL
+ CharToOemA(D->FileName,D->FileName);
#endif
- }
- D->Flags=Data->Arc.NewLhd.Flags;
- D->PackSize=Data->Arc.NewLhd.PackSize;
- D->PackSizeHigh=Data->Arc.NewLhd.HighPackSize;
- D->UnpSize=Data->Arc.NewLhd.UnpSize;
- D->UnpSizeHigh=Data->Arc.NewLhd.HighUnpSize;
- D->HostOS=Data->Arc.NewLhd.HostOS;
- D->FileCRC=Data->Arc.NewLhd.FileCRC;
- D->FileTime=Data->Arc.NewLhd.FileTime;
- D->UnpVer=Data->Arc.NewLhd.UnpVer;
- D->Method=Data->Arc.NewLhd.Method;
- D->FileAttr=Data->Arc.NewLhd.FileAttr;
+
+ D->Flags=0;
+ if (hd->SplitBefore)
+ D->Flags|=RHDF_SPLITBEFORE;
+ if (hd->SplitAfter)
+ D->Flags|=RHDF_SPLITAFTER;
+ if (hd->Encrypted)
+ D->Flags|=RHDF_ENCRYPTED;
+ if (hd->Solid)
+ D->Flags|=RHDF_SOLID;
+ if (hd->Dir)
+ D->Flags|=RHDF_DIRECTORY;
+
+ D->PackSize=uint(hd->PackSize & 0xffffffff);
+ D->PackSizeHigh=uint(hd->PackSize>>32);
+ D->UnpSize=uint(hd->UnpSize & 0xffffffff);
+ D->UnpSizeHigh=uint(hd->UnpSize>>32);
+ D->HostOS=hd->HSType==HSYS_WINDOWS ? HOST_WIN32:HOST_UNIX;
+ D->UnpVer=Data->Arc.FileHead.UnpVer;
+ D->FileCRC=hd->FileHash.CRC32;
+ D->FileTime=hd->mtime.GetDos();
+
+ uint64 MRaw=hd->mtime.GetWin();
+ D->MtimeLow=(uint)MRaw;
+ D->MtimeHigh=(uint)(MRaw>>32);
+ uint64 CRaw=hd->ctime.GetWin();
+ D->CtimeLow=(uint)CRaw;
+ D->CtimeHigh=(uint)(CRaw>>32);
+ uint64 ARaw=hd->atime.GetWin();
+ D->AtimeLow=(uint)ARaw;
+ D->AtimeHigh=(uint)(ARaw>>32);
+
+ D->Method=hd->Method+0x30;
+ D->FileAttr=hd->FileAttr;
D->CmtSize=0;
D->CmtState=0;
+
+ D->DictSize=uint(hd->WinSize/1024);
+
+ switch (hd->FileHash.Type)
+ {
+ case HASH_RAR14:
+ case HASH_CRC32:
+ D->HashType=RAR_HASH_CRC32;
+ break;
+ case HASH_BLAKE2:
+ D->HashType=RAR_HASH_BLAKE2;
+ memcpy(D->Hash,hd->FileHash.Digest,BLAKE2_DIGEST_SIZE);
+ break;
+ default:
+ D->HashType=RAR_HASH_NONE;
+ break;
+ }
+
+ D->RedirType=hd->RedirType;
+ // RedirNameSize sanity check is useful in case some developer
+ // did not initialize Reserved area with 0 as required in docs.
+ // We have taken 'Redir*' fields from Reserved area. We may remove
+ // this RedirNameSize check sometimes later.
+ if (hd->RedirType!=FSREDIR_NONE && D->RedirName!=NULL &&
+ D->RedirNameSize>0 && D->RedirNameSize<100000)
+ wcsncpyz(D->RedirName,hd->RedirName,D->RedirNameSize);
+ D->DirTarget=hd->DirTarget;
}
- catch (int ErrCode)
+ catch (RAR_EXIT ErrCode)
{
- return(RarErrorToDll(ErrCode));
+ return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode);
}
- return(0);
+ return ERAR_SUCCESS;
}
@@ -224,88 +331,101 @@ int PASCAL ProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestNa
if (Data->OpenMode==RAR_OM_LIST || Data->OpenMode==RAR_OM_LIST_INCSPLIT ||
Operation==RAR_SKIP && !Data->Arc.Solid)
{
- if (Data->Arc.Volume &&
- Data->Arc.GetHeaderType()==FILE_HEAD &&
- (Data->Arc.NewLhd.Flags & LHD_SPLIT_AFTER)!=0)
+ if (Data->Arc.Volume && Data->Arc.GetHeaderType()==HEAD_FILE &&
+ Data->Arc.FileHead.SplitAfter)
if (MergeArchive(Data->Arc,NULL,false,'L'))
{
- Data->Extract.SignatureFound=false;
Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET);
- return(0);
+ return ERAR_SUCCESS;
}
else
- return(ERAR_EOPEN);
+ return ERAR_EOPEN;
Data->Arc.SeekToNext();
}
else
{
Data->Cmd.DllOpMode=Operation;
- if (DestPath!=NULL || DestName!=NULL)
+ *Data->Cmd.ExtrPath=0;
+ *Data->Cmd.DllDestName=0;
+
+ if (DestPath!=NULL)
{
-#ifdef _WIN_32
- OemToChar(NullToEmpty(DestPath),Data->Cmd.ExtrPath);
-#else
- strcpy(Data->Cmd.ExtrPath,NullToEmpty(DestPath));
-#endif
- AddEndSlash(Data->Cmd.ExtrPath);
-#ifdef _WIN_32
- OemToChar(NullToEmpty(DestName),Data->Cmd.DllDestName);
-#else
- strcpy(Data->Cmd.DllDestName,NullToEmpty(DestName));
+ char ExtrPathA[NM];
+ strncpyz(ExtrPathA,DestPath,ASIZE(ExtrPathA)-2);
+#ifdef _WIN_ALL
+ // We must not apply OemToCharBuffA directly to DestPath,
+ // because we do not know DestPath length and OemToCharBuffA
+ // does not stop at 0.
+ OemToCharA(ExtrPathA,ExtrPathA);
#endif
+ CharToWide(ExtrPathA,Data->Cmd.ExtrPath,ASIZE(Data->Cmd.ExtrPath));
+ AddEndSlash(Data->Cmd.ExtrPath,ASIZE(Data->Cmd.ExtrPath));
}
- else
+ if (DestName!=NULL)
{
- *Data->Cmd.ExtrPath=0;
- *Data->Cmd.DllDestName=0;
+ char DestNameA[NM];
+ strncpyz(DestNameA,DestName,ASIZE(DestNameA)-2);
+#ifdef _WIN_ALL
+ // We must not apply OemToCharBuffA directly to DestName,
+ // because we do not know DestName length and OemToCharBuffA
+ // does not stop at 0.
+ OemToCharA(DestNameA,DestNameA);
+#endif
+ CharToWide(DestNameA,Data->Cmd.DllDestName,ASIZE(Data->Cmd.DllDestName));
}
- if (DestPathW!=NULL || DestNameW!=NULL)
+ if (DestPathW!=NULL)
{
- strncpyw(Data->Cmd.ExtrPathW,NullToEmpty(DestPathW),NM-2);
- AddEndSlash(Data->Cmd.ExtrPathW);
- strncpyw(Data->Cmd.DllDestNameW,NullToEmpty(DestNameW),NM-1);
-
- if (*Data->Cmd.DllDestNameW!=0 && *Data->Cmd.DllDestName==0)
- WideToChar(Data->Cmd.DllDestNameW,Data->Cmd.DllDestName);
- }
- else
- {
- *Data->Cmd.ExtrPathW=0;
- *Data->Cmd.DllDestNameW=0;
+ wcsncpyz(Data->Cmd.ExtrPath,DestPathW,ASIZE(Data->Cmd.ExtrPath));
+ AddEndSlash(Data->Cmd.ExtrPath,ASIZE(Data->Cmd.ExtrPath));
}
- strcpy(Data->Cmd.Command,Operation==RAR_EXTRACT ? "X":"T");
+ if (DestNameW!=NULL)
+ wcsncpyz(Data->Cmd.DllDestName,DestNameW,ASIZE(Data->Cmd.DllDestName));
+
+ wcsncpyz(Data->Cmd.Command,Operation==RAR_EXTRACT ? L"X":L"T",ASIZE(Data->Cmd.Command));
Data->Cmd.Test=Operation!=RAR_EXTRACT;
bool Repeat=false;
- Data->Extract.ExtractCurrentFile(&Data->Cmd,Data->Arc,Data->HeaderSize,Repeat);
-
- while (Data->Arc.ReadHeader()!=0 && Data->Arc.GetHeaderType()==NEWSUB_HEAD)
+ Data->Extract.ExtractCurrentFile(Data->Arc,Data->HeaderSize,Repeat);
+
+ // Now we process extra file information if any.
+ //
+ // Archive can be closed if we process volumes, next volume is missing
+ // and current one is already removed or deleted. So we need to check
+ // if archive is still open to avoid calling file operations on
+ // the invalid file handle. Some of our file operations like Seek()
+ // process such invalid handle correctly, some not.
+ while (Data->Arc.IsOpened() && Data->Arc.ReadHeader()!=0 &&
+ Data->Arc.GetHeaderType()==HEAD_SERVICE)
{
- Data->Extract.ExtractCurrentFile(&Data->Cmd,Data->Arc,Data->HeaderSize,Repeat);
+ Data->Extract.ExtractCurrentFile(Data->Arc,Data->HeaderSize,Repeat);
Data->Arc.SeekToNext();
}
Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET);
}
}
- catch (int ErrCode)
+ catch (std::bad_alloc&)
{
- return(RarErrorToDll(ErrCode));
+ return ERAR_NO_MEMORY;
}
- return(Data->Cmd.DllError);
+ catch (RAR_EXIT ErrCode)
+ {
+ return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode);
+ }
+ return Data->Cmd.DllError;
}
int PASCAL RARProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestName)
{
- return(ProcessFile(hArcData,Operation,DestPath,DestName,NULL,NULL));
+ return ProcessFile(hArcData,Operation,DestPath,DestName,NULL,NULL);
}
int PASCAL RARProcessFileW(HANDLE hArcData,int Operation,wchar *DestPath,wchar *DestName)
{
- return(ProcessFile(hArcData,Operation,NULL,NULL,DestPath,DestName));
+ return ProcessFile(hArcData,Operation,NULL,NULL,DestPath,DestName);
}
@@ -330,40 +450,47 @@ void PASCAL RARSetProcessDataProc(HANDLE hArcData,PROCESSDATAPROC ProcessDataPro
Data->Cmd.ProcessDataProc=ProcessDataProc;
}
-#ifndef NOCRYPT
+
void PASCAL RARSetPassword(HANDLE hArcData,char *Password)
{
+#ifndef RAR_NOCRYPT
DataSet *Data=(DataSet *)hArcData;
- strncpyz(Data->Cmd.Password,Password,ASIZE(Data->Cmd.Password));
-}
+ wchar PasswordW[MAXPASSWORD];
+ GetWideName(Password,NULL,PasswordW,ASIZE(PasswordW));
+ Data->Cmd.Password.Set(PasswordW);
+ cleandata(PasswordW,sizeof(PasswordW));
#endif
+}
int PASCAL RARGetDllVersion()
{
- return(RAR_DLL_VERSION);
+ return RAR_DLL_VERSION;
}
-static int RarErrorToDll(int ErrCode)
+static int RarErrorToDll(RAR_EXIT ErrCode)
{
switch(ErrCode)
{
- case FATAL_ERROR:
- return(ERAR_EREAD);
- case CRC_ERROR:
- return(ERAR_BAD_DATA);
- case WRITE_ERROR:
- return(ERAR_EWRITE);
- case OPEN_ERROR:
- return(ERAR_EOPEN);
- case CREATE_ERROR:
- return(ERAR_ECREATE);
- case MEMORY_ERROR:
- return(ERAR_NO_MEMORY);
- case SUCCESS:
- return(0);
+ case RARX_FATAL:
+ case RARX_READ:
+ return ERAR_EREAD;
+ case RARX_CRC:
+ return ERAR_BAD_DATA;
+ case RARX_WRITE:
+ return ERAR_EWRITE;
+ case RARX_OPEN:
+ return ERAR_EOPEN;
+ case RARX_CREATE:
+ return ERAR_ECREATE;
+ case RARX_MEMORY:
+ return ERAR_NO_MEMORY;
+ case RARX_BADPWD:
+ return ERAR_BAD_PASSWORD;
+ case RARX_SUCCESS:
+ return ERAR_SUCCESS; // 0.
default:
- return(ERAR_UNKNOWN);
+ return ERAR_UNKNOWN;
}
}
diff --git a/unrar/unrar/dll.def b/unrar/unrar/dll.def
index 660f69b..3c9a2c8 100644
--- a/unrar/unrar/dll.def
+++ b/unrar/unrar/dll.def
@@ -5,6 +5,7 @@ EXPORTS
RARReadHeader
RARReadHeaderEx
RARProcessFile
+ RARProcessFileW
RARSetCallback
RARSetChangeVolProc
RARSetProcessDataProc
diff --git a/unrar/unrar/dll.hpp b/unrar/unrar/dll.hpp
index c7cf622..c785ff1 100644
--- a/unrar/unrar/dll.hpp
+++ b/unrar/unrar/dll.hpp
@@ -1,9 +1,9 @@
#ifndef _UNRAR_DLL_
#define _UNRAR_DLL_
-/* Added by Tomas Bzatek - GCC 4.3 compatibility */
-#define LPARAM LONG
+#pragma pack(push, 1)
+#define ERAR_SUCCESS 0
#define ERAR_END_ARCHIVE 10
#define ERAR_NO_MEMORY 11
#define ERAR_BAD_DATA 12
@@ -17,6 +17,8 @@
#define ERAR_SMALL_BUF 20
#define ERAR_UNKNOWN 21
#define ERAR_MISSING_PASSWORD 22
+#define ERAR_EREFERENCE 23
+#define ERAR_BAD_PASSWORD 24
#define RAR_OM_LIST 0
#define RAR_OM_EXTRACT 1
@@ -29,16 +31,29 @@
#define RAR_VOL_ASK 0
#define RAR_VOL_NOTIFY 1
-#define RAR_DLL_VERSION 4
+#define RAR_DLL_VERSION 8
+
+#define RAR_HASH_NONE 0
+#define RAR_HASH_CRC32 1
+#define RAR_HASH_BLAKE2 2
+
#ifdef _UNIX
#define CALLBACK
#define PASCAL
#define LONG long
#define HANDLE void *
+#define LPARAM long
#define UINT unsigned int
#endif
+#define RHDF_SPLITBEFORE 0x01
+#define RHDF_SPLITAFTER 0x02
+#define RHDF_ENCRYPTED 0x04
+#define RHDF_SOLID 0x10
+#define RHDF_DIRECTORY 0x20
+
+
struct RARHeaderData
{
char ArcName[260];
@@ -80,7 +95,20 @@ struct RARHeaderDataEx
unsigned int CmtBufSize;
unsigned int CmtSize;
unsigned int CmtState;
- unsigned int Reserved[1024];
+ unsigned int DictSize;
+ unsigned int HashType;
+ char Hash[32];
+ unsigned int RedirType;
+ wchar_t *RedirName;
+ unsigned int RedirNameSize;
+ unsigned int DirTarget;
+ unsigned int MtimeLow;
+ unsigned int MtimeHigh;
+ unsigned int CtimeLow;
+ unsigned int CtimeHigh;
+ unsigned int AtimeLow;
+ unsigned int AtimeHigh;
+ unsigned int Reserved[988];
};
@@ -95,26 +123,43 @@ struct RAROpenArchiveData
unsigned int CmtState;
};
+typedef int (CALLBACK *UNRARCALLBACK)(UINT msg,LPARAM UserData,LPARAM P1,LPARAM P2);
+
+#define ROADF_VOLUME 0x0001
+#define ROADF_COMMENT 0x0002
+#define ROADF_LOCK 0x0004
+#define ROADF_SOLID 0x0008
+#define ROADF_NEWNUMBERING 0x0010
+#define ROADF_SIGNED 0x0020
+#define ROADF_RECOVERY 0x0040
+#define ROADF_ENCHEADERS 0x0080
+#define ROADF_FIRSTVOLUME 0x0100
+
+#define ROADOF_KEEPBROKEN 0x0001
+
struct RAROpenArchiveDataEx
{
char *ArcName;
wchar_t *ArcNameW;
- unsigned int OpenMode;
- unsigned int OpenResult;
+ unsigned int OpenMode;
+ unsigned int OpenResult;
char *CmtBuf;
- unsigned int CmtBufSize;
- unsigned int CmtSize;
- unsigned int CmtState;
- unsigned int Flags;
- unsigned int Reserved[32];
+ unsigned int CmtBufSize;
+ unsigned int CmtSize;
+ unsigned int CmtState;
+ unsigned int Flags;
+ UNRARCALLBACK Callback;
+ LPARAM UserData;
+ unsigned int OpFlags;
+ wchar_t *CmtBufW;
+ unsigned int Reserved[25];
};
enum UNRARCALLBACK_MESSAGES {
- UCM_CHANGEVOLUME,UCM_PROCESSDATA,UCM_NEEDPASSWORD
+ UCM_CHANGEVOLUME,UCM_PROCESSDATA,UCM_NEEDPASSWORD,UCM_CHANGEVOLUMEW,
+ UCM_NEEDPASSWORDW
};
-typedef int (CALLBACK *UNRARCALLBACK)(UINT msg,LPARAM UserData,LPARAM P1,LPARAM P2);
-
typedef int (PASCAL *CHANGEVOLPROC)(char *ArcName,int Mode);
typedef int (PASCAL *PROCESSDATAPROC)(unsigned char *Addr,int Size);
@@ -139,4 +184,6 @@ int PASCAL RARGetDllVersion();
}
#endif
+#pragma pack(pop)
+
#endif
diff --git a/unrar/unrar/dll.rc b/unrar/unrar/dll.rc
new file mode 100644
index 0000000..b7f0778
--- /dev/null
+++ b/unrar/unrar/dll.rc
@@ -0,0 +1,28 @@
+#include <windows.h>
+#include <commctrl.h>
+
+VS_VERSION_INFO VERSIONINFO
+FILEVERSION 6, 24, 100, 1007
+PRODUCTVERSION 6, 24, 100, 1007
+FILEOS VOS__WINDOWS32
+FILETYPE VFT_APP
+{
+ BLOCK "StringFileInfo"
+ {
+ BLOCK "040904E4"
+ {
+ VALUE "CompanyName", "Alexander Roshal\0"
+ VALUE "ProductName", "RAR decompression library\0"
+ VALUE "FileDescription", "RAR decompression library\0"
+ VALUE "FileVersion", "6.24.0\0"
+ VALUE "ProductVersion", "6.24.0\0"
+ VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2023\0"
+ VALUE "OriginalFilename", "Unrar.dll\0"
+ }
+ }
+ BLOCK "VarFileInfo"
+ {
+ VALUE "Translation", 0x0409, 0x04E4
+ }
+}
+
diff --git a/unrar/unrar/dll_nocrypt.def b/unrar/unrar/dll_nocrypt.def
new file mode 100644
index 0000000..d473e97
--- /dev/null
+++ b/unrar/unrar/dll_nocrypt.def
@@ -0,0 +1,13 @@
+EXPORTS
+ RAROpenArchive
+ RAROpenArchiveEx
+ RARCloseArchive
+ RARReadHeader
+ RARReadHeaderEx
+ RARProcessFile
+ RARProcessFileW
+ RARSetCallback
+ RARSetChangeVolProc
+ RARSetProcessDataProc
+; RARSetPassword
+ RARGetDllVersion
diff --git a/unrar/unrar/encname.cpp b/unrar/unrar/encname.cpp
index 6f57cd9..e1ba1ed 100644
--- a/unrar/unrar/encname.cpp
+++ b/unrar/unrar/encname.cpp
@@ -11,11 +11,11 @@ EncodeFileName::EncodeFileName()
-void EncodeFileName::Decode(char *Name,byte *EncName,int EncSize,wchar *NameW,
- int MaxDecSize)
+void EncodeFileName::Decode(char *Name,size_t NameSize,byte *EncName,size_t EncSize,
+ wchar *NameW,size_t MaxDecSize)
{
- int EncPos=0,DecPos=0;
- byte HighByte=EncName[EncPos++];
+ size_t EncPos=0,DecPos=0;
+ byte HighByte=EncPos<EncSize ? EncName[EncPos++] : 0;
while (EncPos<EncSize && DecPos<MaxDecSize)
{
if (FlagBits==0)
@@ -26,26 +26,36 @@ void EncodeFileName::Decode(char *Name,byte *EncName,int EncSize,wchar *NameW,
switch(Flags>>6)
{
case 0:
+ if (EncPos>=EncSize)
+ break;
NameW[DecPos++]=EncName[EncPos++];
break;
case 1:
+ if (EncPos>=EncSize)
+ break;
NameW[DecPos++]=EncName[EncPos++]+(HighByte<<8);
break;
case 2:
+ if (EncPos+1>=EncSize)
+ break;
NameW[DecPos++]=EncName[EncPos]+(EncName[EncPos+1]<<8);
EncPos+=2;
break;
case 3:
{
+ if (EncPos>=EncSize)
+ break;
int Length=EncName[EncPos++];
- if (Length & 0x80)
+ if ((Length & 0x80)!=0)
{
+ if (EncPos>=EncSize)
+ break;
byte Correction=EncName[EncPos++];
- for (Length=(Length&0x7f)+2;Length>0 && DecPos<MaxDecSize;Length--,DecPos++)
+ for (Length=(Length&0x7f)+2;Length>0 && DecPos<MaxDecSize && DecPos<NameSize;Length--,DecPos++)
NameW[DecPos]=((Name[DecPos]+Correction)&0xff)+(HighByte<<8);
}
else
- for (Length+=2;Length>0 && DecPos<MaxDecSize;Length--,DecPos++)
+ for (Length+=2;Length>0 && DecPos<MaxDecSize && DecPos<NameSize;Length--,DecPos++)
NameW[DecPos]=Name[DecPos];
}
break;
diff --git a/unrar/unrar/encname.hpp b/unrar/unrar/encname.hpp
index 586f421..c6b4ed4 100644
--- a/unrar/unrar/encname.hpp
+++ b/unrar/unrar/encname.hpp
@@ -4,17 +4,16 @@
class EncodeFileName
{
private:
- void AddFlags(int Value);
+ void AddFlags(byte Value,byte *EncName);
- byte *EncName;
byte Flags;
- int FlagBits;
- int FlagsPos;
- int DestSize;
+ uint FlagBits;
+ size_t FlagsPos;
+ size_t DestSize;
public:
EncodeFileName();
- int Encode(char *Name,wchar *NameW,byte *EncName);
- void Decode(char *Name,byte *EncName,int EncSize,wchar *NameW,int MaxDecSize);
+ size_t Encode(char *Name,wchar *NameW,byte *EncName);
+ void Decode(char *Name,size_t NameSize,byte *EncName,size_t EncSize,wchar *NameW,size_t MaxDecSize);
};
#endif
diff --git a/unrar/unrar/errhnd.cpp b/unrar/unrar/errhnd.cpp
index 2797f63..97193e5 100644
--- a/unrar/unrar/errhnd.cpp
+++ b/unrar/unrar/errhnd.cpp
@@ -1,8 +1,5 @@
#include "rar.hpp"
-
-static bool UserBreak;
-
ErrorHandler::ErrorHandler()
{
Clean();
@@ -11,250 +8,257 @@ ErrorHandler::ErrorHandler()
void ErrorHandler::Clean()
{
- ExitCode=SUCCESS;
+ ExitCode=RARX_SUCCESS;
ErrCount=0;
EnableBreak=true;
Silent=false;
- DoShutdown=false;
+ UserBreak=false;
+ MainExit=false;
+ DisableShutdown=false;
+ ReadErrIgnoreAll=false;
}
void ErrorHandler::MemoryError()
{
MemoryErrorMsg();
- Throw(MEMORY_ERROR);
+ Exit(RARX_MEMORY);
}
-void ErrorHandler::OpenError(const char *FileName)
+void ErrorHandler::OpenError(const wchar *FileName)
{
#ifndef SILENT
OpenErrorMsg(FileName);
- Throw(OPEN_ERROR);
+ Exit(RARX_OPEN);
#endif
}
-void ErrorHandler::CloseError(const char *FileName)
+void ErrorHandler::CloseError(const wchar *FileName)
{
-#ifndef SILENT
if (!UserBreak)
{
- ErrMsg(NULL,St(MErrFClose),FileName);
+ uiMsg(UIERROR_FILECLOSE,FileName);
SysErrMsg();
}
-#endif
-#if !defined(SILENT) || defined(RARDLL)
- Throw(FATAL_ERROR);
-#endif
+ // We must not call Exit and throw an exception here, because this function
+ // is called from File object destructor and can be invoked when stack
+ // unwinding while handling another exception. Throwing a new exception
+ // when stack unwinding is prohibited and terminates a program.
+ // If necessary, we can check std::uncaught_exception() before throw.
+ SetErrorCode(RARX_FATAL);
}
-void ErrorHandler::ReadError(const char *FileName)
+void ErrorHandler::ReadError(const wchar *FileName)
{
#ifndef SILENT
- ReadErrorMsg(NULL,FileName);
+ ReadErrorMsg(FileName);
#endif
#if !defined(SILENT) || defined(RARDLL)
- Throw(FATAL_ERROR);
+ Exit(RARX_READ);
#endif
}
-bool ErrorHandler::AskRepeatRead(const char *FileName)
+void ErrorHandler::AskRepeatRead(const wchar *FileName,bool &Ignore,bool &Retry,bool &Quit)
{
-#if !defined(SILENT) && !defined(SFX_MODULE) && !defined(_WIN_CE)
+ SetErrorCode(RARX_READ);
+#if !defined(SILENT) && !defined(SFX_MODULE)
if (!Silent)
{
- mprintf("\n");
- Log(NULL,St(MErrRead),FileName);
- return(Ask(St(MRetryAbort))==1);
+ uiMsg(UIERROR_FILEREAD,UINULL,FileName);
+ SysErrMsg();
+ if (ReadErrIgnoreAll)
+ Ignore=true;
+ else
+ {
+ bool All=false;
+ uiAskRepeatRead(FileName,Ignore,All,Retry,Quit);
+ if (All)
+ ReadErrIgnoreAll=Ignore=true;
+ if (Quit) // Disable shutdown if user select Quit in read error prompt.
+ DisableShutdown=true;
+ }
+ return;
}
#endif
- return(false);
+ Ignore=true; // Saving the file part for -y or -inul or "Ignore all" choice.
}
-void ErrorHandler::WriteError(const char *ArcName,const char *FileName)
+void ErrorHandler::WriteError(const wchar *ArcName,const wchar *FileName)
{
#ifndef SILENT
WriteErrorMsg(ArcName,FileName);
#endif
#if !defined(SILENT) || defined(RARDLL)
- Throw(WRITE_ERROR);
+ Exit(RARX_WRITE);
#endif
}
-#ifdef _WIN_32
-void ErrorHandler::WriteErrorFAT(const char *FileName)
+#ifdef _WIN_ALL
+void ErrorHandler::WriteErrorFAT(const wchar *FileName)
{
-#if !defined(SILENT) && !defined(SFX_MODULE)
SysErrMsg();
- ErrMsg(NULL,St(MNTFSRequired),FileName);
-#endif
+ uiMsg(UIERROR_NTFSREQUIRED,FileName);
#if !defined(SILENT) && !defined(SFX_MODULE) || defined(RARDLL)
- Throw(WRITE_ERROR);
+ Exit(RARX_WRITE);
#endif
}
#endif
-bool ErrorHandler::AskRepeatWrite(const char *FileName)
+bool ErrorHandler::AskRepeatWrite(const wchar *FileName,bool DiskFull)
{
-#if !defined(SILENT) && !defined(_WIN_CE)
+#ifndef SILENT
if (!Silent)
{
- mprintf("\n");
- Log(NULL,St(MErrWrite),FileName);
- return(Ask(St(MRetryAbort))==1);
+ // We do not display "repeat write" prompt in Android, so we do not
+ // need the matching system error message.
+ SysErrMsg();
+ bool Repeat=uiAskRepeatWrite(FileName,DiskFull);
+ if (!Repeat) // Disable shutdown if user pressed Cancel in error dialog.
+ DisableShutdown=true;
+ return Repeat;
}
#endif
- return(false);
+ return false;
}
-void ErrorHandler::SeekError(const char *FileName)
+void ErrorHandler::SeekError(const wchar *FileName)
{
-#ifndef SILENT
if (!UserBreak)
{
- ErrMsg(NULL,St(MErrSeek),FileName);
+ uiMsg(UIERROR_FILESEEK,FileName);
SysErrMsg();
}
-#endif
#if !defined(SILENT) || defined(RARDLL)
- Throw(FATAL_ERROR);
+ Exit(RARX_FATAL);
#endif
}
-void ErrorHandler::GeneralErrMsg(const char *Msg)
+void ErrorHandler::GeneralErrMsg(const wchar *fmt,...)
{
-#ifndef SILENT
- Log(NULL,"%s",Msg);
+ va_list arglist;
+ va_start(arglist,fmt);
+ wchar Msg[1024];
+ vswprintf(Msg,ASIZE(Msg),fmt,arglist);
+ uiMsg(UIERROR_GENERALERRMSG,Msg);
SysErrMsg();
-#endif
+ va_end(arglist);
}
void ErrorHandler::MemoryErrorMsg()
{
-#ifndef SILENT
- ErrMsg(NULL,St(MErrOutMem));
-#endif
+ uiMsg(UIERROR_MEMORY);
+ SetErrorCode(RARX_MEMORY);
}
-void ErrorHandler::OpenErrorMsg(const char *FileName)
+void ErrorHandler::OpenErrorMsg(const wchar *FileName)
{
OpenErrorMsg(NULL,FileName);
}
-void ErrorHandler::OpenErrorMsg(const char *ArcName,const char *FileName)
+void ErrorHandler::OpenErrorMsg(const wchar *ArcName,const wchar *FileName)
{
-#ifndef SILENT
- Log(ArcName && *ArcName ? ArcName:NULL,St(MCannotOpen),FileName);
- Alarm();
+ uiMsg(UIERROR_FILEOPEN,ArcName,FileName);
SysErrMsg();
-#endif
+ SetErrorCode(RARX_OPEN);
+
+ // Keep GUI responsive if many files cannot be opened when archiving.
+ // Call after SysErrMsg to avoid modifying the error code and SysErrMsg text.
+ Wait();
}
-void ErrorHandler::CreateErrorMsg(const char *FileName)
+void ErrorHandler::CreateErrorMsg(const wchar *FileName)
{
CreateErrorMsg(NULL,FileName);
}
-void ErrorHandler::CreateErrorMsg(const char *ArcName,const char *FileName)
+void ErrorHandler::CreateErrorMsg(const wchar *ArcName,const wchar *FileName)
{
-#ifndef SILENT
- Log(ArcName && *ArcName ? ArcName:NULL,St(MCannotCreate),FileName);
- Alarm();
-#if defined(_WIN_32) && !defined(_WIN_CE) && !defined(SFX_MODULE) && defined(MAXPATH)
- if (GetLastError()==ERROR_PATH_NOT_FOUND)
- {
- int NameLength=strlen(FileName);
- if (!IsFullPath(FileName))
- {
- char CurDir[NM];
- GetCurrentDirectory(sizeof(CurDir),CurDir);
- NameLength+=strlen(CurDir)+1;
- }
- if (NameLength>MAXPATH)
- {
- Log(ArcName && *ArcName ? ArcName:NULL,St(MMaxPathLimit),MAXPATH);
- }
- }
-#endif
+ uiMsg(UIERROR_FILECREATE,ArcName,FileName);
SysErrMsg();
-#endif
+ SetErrorCode(RARX_CREATE);
}
-void ErrorHandler::ReadErrorMsg(const char *ArcName,const char *FileName)
+void ErrorHandler::ReadErrorMsg(const wchar *FileName)
{
-#ifndef SILENT
- ErrMsg(ArcName,St(MErrRead),FileName);
+ ReadErrorMsg(NULL,FileName);
+}
+
+
+void ErrorHandler::ReadErrorMsg(const wchar *ArcName,const wchar *FileName)
+{
+ uiMsg(UIERROR_FILEREAD,ArcName,FileName);
SysErrMsg();
-#endif
+ SetErrorCode(RARX_READ);
}
-void ErrorHandler::WriteErrorMsg(const char *ArcName,const char *FileName)
+void ErrorHandler::WriteErrorMsg(const wchar *ArcName,const wchar *FileName)
{
-#ifndef SILENT
- ErrMsg(ArcName,St(MErrWrite),FileName);
+ uiMsg(UIERROR_FILEWRITE,ArcName,FileName);
SysErrMsg();
-#endif
+ SetErrorCode(RARX_WRITE);
}
-void ErrorHandler::Exit(int ExitCode)
+void ErrorHandler::ArcBrokenMsg(const wchar *ArcName)
{
-#ifndef SFX_MODULE
- Alarm();
-#endif
- Throw(ExitCode);
+ uiMsg(UIERROR_ARCBROKEN,ArcName);
+ SetErrorCode(RARX_CRC);
}
-#ifndef GUI
-void ErrorHandler::ErrMsg(const char *ArcName,const char *fmt,...)
+void ErrorHandler::ChecksumFailedMsg(const wchar *ArcName,const wchar *FileName)
{
- safebuf char Msg[NM+1024];
- va_list argptr;
- va_start(argptr,fmt);
- vsprintf(Msg,fmt,argptr);
- va_end(argptr);
-#ifdef _WIN_32
- if (UserBreak)
- Sleep(5000);
-#endif
- Alarm();
- if (*Msg)
- {
- Log(ArcName,"\n%s",Msg);
- mprintf("\n%s\n",St(MProgAborted));
- }
+ uiMsg(UIERROR_CHECKSUM,ArcName,FileName);
+ SetErrorCode(RARX_CRC);
}
-#endif
-void ErrorHandler::SetErrorCode(int Code)
+void ErrorHandler::UnknownMethodMsg(const wchar *ArcName,const wchar *FileName)
+{
+ uiMsg(UIERROR_UNKNOWNMETHOD,ArcName,FileName);
+ ErrHandler.SetErrorCode(RARX_FATAL);
+}
+
+
+void ErrorHandler::Exit(RAR_EXIT ExitCode)
+{
+ uiAlarm(UIALARM_ERROR);
+ Throw(ExitCode);
+}
+
+
+void ErrorHandler::SetErrorCode(RAR_EXIT Code)
{
switch(Code)
{
- case WARNING:
- case USER_BREAK:
- if (ExitCode==SUCCESS)
+ case RARX_WARNING:
+ case RARX_USERBREAK:
+ if (ExitCode==RARX_SUCCESS)
+ ExitCode=Code;
+ break;
+ case RARX_CRC:
+ if (ExitCode!=RARX_BADPWD)
ExitCode=Code;
break;
- case FATAL_ERROR:
- if (ExitCode==SUCCESS || ExitCode==WARNING)
- ExitCode=FATAL_ERROR;
+ case RARX_FATAL:
+ if (ExitCode==RARX_SUCCESS || ExitCode==RARX_WARNING)
+ ExitCode=RARX_FATAL;
break;
default:
ExitCode=Code;
@@ -264,8 +268,7 @@ void ErrorHandler::SetErrorCode(int Code)
}
-#if !defined(GUI) && !defined(_SFX_RTL_)
-#ifdef _WIN_32
+#ifdef _WIN_ALL
BOOL __stdcall ProcessSignal(DWORD SigType)
#else
#if defined(__sun)
@@ -274,102 +277,147 @@ extern "C"
void _stdfunction ProcessSignal(int SigType)
#endif
{
-#ifdef _WIN_32
+#ifdef _WIN_ALL
+ // When a console application is run as a service, this allows the service
+ // to continue running after the user logs off.
if (SigType==CTRL_LOGOFF_EVENT)
- return(TRUE);
+ return TRUE;
#endif
- UserBreak=true;
+
+ ErrHandler.UserBreak=true;
+ ErrHandler.SetDisableShutdown();
mprintf(St(MBreak));
- for (int I=0;!File::RemoveCreated() && I<3;I++)
- {
-#ifdef _WIN_32
+
+#ifdef _WIN_ALL
+ // Let the main thread to handle 'throw' and destroy file objects.
+ for (uint I=0;!ErrHandler.MainExit && I<50;I++)
Sleep(100);
-#endif
- }
-#if defined(USE_RC) && !defined(SFX_MODULE) && !defined(_WIN_CE)
+#if defined(USE_RC) && !defined(SFX_MODULE) && !defined(RARDLL)
ExtRes.UnloadDLL();
#endif
- exit(USER_BREAK);
-#if defined(_WIN_32) && !defined(_MSC_VER)
- // never reached, just to avoid a compiler warning
- return(TRUE);
+ exit(RARX_USERBREAK);
#endif
-}
+
+#ifdef _UNIX
+ static uint BreakCount=0;
+ // User continues to press Ctrl+C, exit immediately without cleanup.
+ if (++BreakCount>1)
+ exit(RARX_USERBREAK);
+ // Otherwise return from signal handler and let Wait() function to close
+ // files and quit. We cannot use the same approach as in Windows,
+ // because Unix signal handler can block execution of our main code.
+#endif
+
+#if defined(_WIN_ALL) && !defined(_MSC_VER)
+ // Never reached, just to avoid a compiler warning
+ return TRUE;
#endif
+}
void ErrorHandler::SetSignalHandlers(bool Enable)
{
EnableBreak=Enable;
-#if !defined(GUI) && !defined(_SFX_RTL_)
-#ifdef _WIN_32
+#ifdef _WIN_ALL
SetConsoleCtrlHandler(Enable ? ProcessSignal:NULL,TRUE);
-// signal(SIGBREAK,Enable ? ProcessSignal:SIG_IGN);
#else
signal(SIGINT,Enable ? ProcessSignal:SIG_IGN);
signal(SIGTERM,Enable ? ProcessSignal:SIG_IGN);
#endif
-#endif
}
-void ErrorHandler::Throw(int Code)
+void ErrorHandler::Throw(RAR_EXIT Code)
{
- if (Code==USER_BREAK && !EnableBreak)
+ if (Code==RARX_USERBREAK && !EnableBreak)
return;
- ErrHandler.SetErrorCode(Code);
-#ifdef ALLOW_EXCEPTIONS
+#if !defined(SILENT)
+ // Do not write "aborted" when just displaying online help.
+ if (Code!=RARX_SUCCESS && Code!=RARX_USERERROR)
+ mprintf(L"\n%s\n",St(MProgAborted));
+#endif
+ SetErrorCode(Code);
throw Code;
-#else
- File::RemoveCreated();
- exit(Code);
+}
+
+
+bool ErrorHandler::GetSysErrMsg(wchar *Msg,size_t Size)
+{
+#ifndef SILENT
+#ifdef _WIN_ALL
+ int ErrType=GetLastError();
+ if (ErrType!=0)
+ return FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,ErrType,MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
+ Msg,(DWORD)Size,NULL)!=0;
+#endif
+
+#if defined(_UNIX) || defined(_EMX)
+ if (errno!=0)
+ {
+ char *err=strerror(errno);
+ if (err!=NULL)
+ {
+ CharToWide(err,Msg,Size);
+ return true;
+ }
+ }
+#endif
#endif
+ return false;
}
void ErrorHandler::SysErrMsg()
{
-#if !defined(SFX_MODULE) && !defined(SILENT)
-#ifdef _WIN_32
- #define STRCHR strchr
- #define ERRCHAR char
- ERRCHAR *lpMsgBuf=NULL;
- int ErrType=GetLastError();
- if (ErrType!=0 && FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
- NULL,ErrType,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPTSTR)&lpMsgBuf,0,NULL))
+#ifndef SILENT
+ wchar Msg[1024];
+ if (!GetSysErrMsg(Msg,ASIZE(Msg)))
+ return;
+#ifdef _WIN_ALL
+ wchar *CurMsg=Msg;
+ while (CurMsg!=NULL) // Print string with \r\n as several strings to multiple lines.
{
- ERRCHAR *CurMsg=lpMsgBuf;
- while (CurMsg!=NULL)
+ while (*CurMsg=='\r' || *CurMsg=='\n')
+ CurMsg++;
+ if (*CurMsg==0)
+ break;
+ wchar *EndMsg=wcschr(CurMsg,'\r');
+ if (EndMsg==NULL)
+ EndMsg=wcschr(CurMsg,'\n');
+ if (EndMsg!=NULL)
{
- while (*CurMsg=='\r' || *CurMsg=='\n')
- CurMsg++;
- if (*CurMsg==0)
- break;
- ERRCHAR *EndMsg=STRCHR(CurMsg,'\r');
- if (EndMsg==NULL)
- EndMsg=STRCHR(CurMsg,'\n');
- if (EndMsg!=NULL)
- {
- *EndMsg=0;
- EndMsg++;
- }
- Log(NULL,"\n%s",CurMsg);
- CurMsg=EndMsg;
+ *EndMsg=0;
+ EndMsg++;
}
+ uiMsg(UIERROR_SYSERRMSG,CurMsg);
+ CurMsg=EndMsg;
}
- LocalFree( lpMsgBuf );
#endif
#if defined(_UNIX) || defined(_EMX)
- char *err=strerror(errno);
- if (err!=NULL)
- Log(NULL,"\n%s",err);
+ uiMsg(UIERROR_SYSERRMSG,Msg);
#endif
#endif
}
+int ErrorHandler::GetSystemErrorCode()
+{
+#ifdef _WIN_ALL
+ return GetLastError();
+#else
+ return errno;
+#endif
+}
+void ErrorHandler::SetSystemErrorCode(int Code)
+{
+#ifdef _WIN_ALL
+ SetLastError(Code);
+#else
+ errno=Code;
+#endif
+}
diff --git a/unrar/unrar/errhnd.hpp b/unrar/unrar/errhnd.hpp
index 950a62c..06f4f61 100644
--- a/unrar/unrar/errhnd.hpp
+++ b/unrar/unrar/errhnd.hpp
@@ -1,63 +1,75 @@
#ifndef _RAR_ERRHANDLER_
#define _RAR_ERRHANDLER_
-#if (defined(GUI) || !defined(_WIN_32)) && !defined(SFX_MODULE) && !defined(_WIN_CE) || defined(RARDLL)
-#define ALLOW_EXCEPTIONS
-#endif
-
-
-
-#define rarmalloc malloc
-#define rarcalloc calloc
-#define rarrealloc realloc
-#define rarfree free
-#define rarstrdup strdup
-#define rarstrdupw strdupw
-
-
+enum RAR_EXIT // RAR exit code.
+{
+ RARX_SUCCESS = 0,
+ RARX_WARNING = 1,
+ RARX_FATAL = 2,
+ RARX_CRC = 3,
+ RARX_LOCK = 4,
+ RARX_WRITE = 5,
+ RARX_OPEN = 6,
+ RARX_USERERROR = 7,
+ RARX_MEMORY = 8,
+ RARX_CREATE = 9,
+ RARX_NOFILES = 10,
+ RARX_BADPWD = 11,
+ RARX_READ = 12,
+ RARX_USERBREAK = 255
+};
-enum { SUCCESS,WARNING,FATAL_ERROR,CRC_ERROR,LOCK_ERROR,WRITE_ERROR,
- OPEN_ERROR,USER_ERROR,MEMORY_ERROR,CREATE_ERROR,USER_BREAK=255};
class ErrorHandler
{
private:
- void ErrMsg(const char *ArcName,const char *fmt,...);
-
- int ExitCode;
- int ErrCount;
+ RAR_EXIT ExitCode;
+ uint ErrCount;
bool EnableBreak;
bool Silent;
- bool DoShutdown;
+ bool DisableShutdown; // Shutdown is not suitable after last error.
+ bool ReadErrIgnoreAll;
public:
ErrorHandler();
void Clean();
void MemoryError();
- void OpenError(const char *FileName);
- void CloseError(const char *FileName);
- void ReadError(const char *FileName);
- bool AskRepeatRead(const char *FileName);
- void WriteError(const char *ArcName,const char *FileName);
- void WriteErrorFAT(const char *FileName);
- bool AskRepeatWrite(const char *FileName);
- void SeekError(const char *FileName);
- void GeneralErrMsg(const char *Msg);
+ void OpenError(const wchar *FileName);
+ void CloseError(const wchar *FileName);
+ void ReadError(const wchar *FileName);
+ void AskRepeatRead(const wchar *FileName,bool &Ignore,bool &Retry,bool &Quit);
+ void WriteError(const wchar *ArcName,const wchar *FileName);
+ void WriteErrorFAT(const wchar *FileName);
+ bool AskRepeatWrite(const wchar *FileName,bool DiskFull);
+ void SeekError(const wchar *FileName);
+ void GeneralErrMsg(const wchar *fmt,...);
void MemoryErrorMsg();
- void OpenErrorMsg(const char *FileName);
- void OpenErrorMsg(const char *ArcName,const char *FileName);
- void CreateErrorMsg(const char *FileName);
- void CreateErrorMsg(const char *ArcName,const char *FileName);
- void ReadErrorMsg(const char *ArcName,const char *FileName);
- void WriteErrorMsg(const char *ArcName,const char *FileName);
- void Exit(int ExitCode);
- void SetErrorCode(int Code);
- int GetErrorCode() {return(ExitCode);}
- int GetErrorCount() {return(ErrCount);}
+ void OpenErrorMsg(const wchar *FileName);
+ void OpenErrorMsg(const wchar *ArcName,const wchar *FileName);
+ void CreateErrorMsg(const wchar *FileName);
+ void CreateErrorMsg(const wchar *ArcName,const wchar *FileName);
+ void ReadErrorMsg(const wchar *FileName);
+ void ReadErrorMsg(const wchar *ArcName,const wchar *FileName);
+ void WriteErrorMsg(const wchar *ArcName,const wchar *FileName);
+ void ArcBrokenMsg(const wchar *ArcName);
+ void ChecksumFailedMsg(const wchar *ArcName,const wchar *FileName);
+ void UnknownMethodMsg(const wchar *ArcName,const wchar *FileName);
+ void Exit(RAR_EXIT ExitCode);
+ void SetErrorCode(RAR_EXIT Code);
+ RAR_EXIT GetErrorCode() {return ExitCode;}
+ uint GetErrorCount() {return ErrCount;}
void SetSignalHandlers(bool Enable);
- void Throw(int Code);
- void SetSilent(bool Mode) {Silent=Mode;};
- void SetShutdown(bool Mode) {DoShutdown=Mode;};
+ void Throw(RAR_EXIT Code);
+ void SetSilent(bool Mode) {Silent=Mode;}
+ bool GetSysErrMsg(wchar *Msg,size_t Size);
void SysErrMsg();
+ int GetSystemErrorCode();
+ void SetSystemErrorCode(int Code);
+ void SetDisableShutdown() {DisableShutdown=true;}
+ bool IsShutdownEnabled() {return !DisableShutdown;}
+
+ bool UserBreak; // Ctrl+Break is pressed.
+ bool MainExit; // main() is completed.
};
+
#endif
diff --git a/unrar/unrar/extinfo.cpp b/unrar/unrar/extinfo.cpp
index 13239fe..d61747e 100644
--- a/unrar/unrar/extinfo.cpp
+++ b/unrar/unrar/extinfo.cpp
@@ -1,76 +1,190 @@
#include "rar.hpp"
-#ifdef _WIN_32
-#include "win32acl.cpp"
+#include "hardlinks.cpp"
#include "win32stm.cpp"
+
+#ifdef _WIN_ALL
+#include "win32acl.cpp"
+#include "win32lnk.cpp"
#endif
-#ifdef _BEOS
-#include "beosea.cpp"
-#endif
-#if defined(_EMX) && !defined(_DJGPP)
-#include "os2ea.cpp"
-#endif
+
#ifdef _UNIX
#include "uowners.cpp"
+#ifdef SAVE_LINKS
+#include "ulinks.cpp"
+#endif
#endif
+// RAR2 service header extra records.
#ifndef SFX_MODULE
-void SetExtraInfo(CommandData *Cmd,Archive &Arc,char *Name,wchar *NameW)
+void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name)
{
+#ifdef _WIN_ALL
+ if (Cmd->Test)
+ return;
switch(Arc.SubBlockHead.SubType)
{
-#if defined(_EMX) && !defined(_DJGPP)
- case EA_HEAD:
- if (Cmd->ProcessEA)
- ExtractOS2EA(Arc,Name);
- break;
-#endif
-#ifdef _UNIX
- case UO_HEAD:
- if (Cmd->ProcessOwners)
- ExtractUnixOwner(Arc,Name);
- break;
-#endif
-#ifdef _BEOS
- case BEEA_HEAD:
- if (Cmd->ProcessEA)
- ExtractBeEA(Arc,Name);
- break;
-#endif
-#ifdef _WIN_32
case NTACL_HEAD:
if (Cmd->ProcessOwners)
- ExtractACL(Arc,Name,NameW);
+ ExtractACL20(Arc,Name);
break;
case STREAM_HEAD:
- ExtractStreams(Arc,Name,NameW);
+ ExtractStreams20(Arc,Name);
break;
-#endif
}
+#endif
}
#endif
-void SetExtraInfoNew(CommandData *Cmd,Archive &Arc,char *Name,wchar *NameW)
+// RAR3 and RAR5 service header extra records.
+void SetExtraInfo(CommandData *Cmd,Archive &Arc,wchar *Name)
{
-#if defined(_EMX) && !defined(_DJGPP)
- if (Cmd->ProcessEA && Arc.SubHead.CmpName(SUBHEAD_TYPE_OS2EA))
- ExtractOS2EANew(Arc,Name);
+#ifdef _UNIX
+ if (!Cmd->Test && Cmd->ProcessOwners && Arc.Format==RARFMT15 &&
+ Arc.SubHead.CmpName(SUBHEAD_TYPE_UOWNER))
+ ExtractUnixOwner30(Arc,Name);
#endif
+#ifdef _WIN_ALL
+ if (!Cmd->Test && Cmd->ProcessOwners && Arc.SubHead.CmpName(SUBHEAD_TYPE_ACL))
+ ExtractACL(Arc,Name);
+ if (Arc.SubHead.CmpName(SUBHEAD_TYPE_STREAM))
+ ExtractStreams(Arc,Name,Cmd->Test);
+#endif
+}
+
+
+// Extra data stored directly in file header.
+void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,wchar *Name)
+{
#ifdef _UNIX
- if (Cmd->ProcessOwners && Arc.SubHead.CmpName(SUBHEAD_TYPE_UOWNER))
- ExtractUnixOwnerNew(Arc,Name);
+ if (Cmd->ProcessOwners && Arc.Format==RARFMT50 && Arc.FileHead.UnixOwnerSet)
+ SetUnixOwner(Arc,Name);
#endif
-#ifdef _BEOS
- if (Cmd->ProcessEA && Arc.SubHead.CmpName(SUBHEAD_TYPE_UOWNER))
- ExtractUnixOwnerNew(Arc,Name);
+}
+
+
+
+
+// Calculate a number of path components except \. and \..
+static int CalcAllowedDepth(const wchar *Name)
+{
+ int AllowedDepth=0;
+ while (*Name!=0)
+ {
+ if (IsPathDiv(Name[0]) && Name[1]!=0 && !IsPathDiv(Name[1]))
+ {
+ bool Dot=Name[1]=='.' && (IsPathDiv(Name[2]) || Name[2]==0);
+ bool Dot2=Name[1]=='.' && Name[2]=='.' && (IsPathDiv(Name[3]) || Name[3]==0);
+ if (!Dot && !Dot2)
+ AllowedDepth++;
+ else
+ if (Dot2)
+ AllowedDepth--;
+ }
+ Name++;
+ }
+ return AllowedDepth < 0 ? 0 : AllowedDepth;
+}
+
+
+// Check if all existing path components are directories and not links.
+static bool LinkInPath(const wchar *Name)
+{
+ wchar Path[NM];
+ if (wcslen(Name)>=ASIZE(Path))
+ return true; // It should not be that long, skip.
+ wcsncpyz(Path,Name,ASIZE(Path));
+ for (wchar *s=Path+wcslen(Path)-1;s>Path;s--)
+ if (IsPathDiv(*s))
+ {
+ *s=0;
+ FindData FD;
+ if (FindFile::FastFind(Path,&FD,true) && (FD.IsLink || !FD.IsDir))
+ return true;
+ }
+ return false;
+}
+
+
+bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *PrepSrcName,const wchar *TargetName)
+{
+ // Catch root dir based /path/file paths also as stuff like \\?\.
+ // Do not check PrepSrcName here, it can be root based if destination path
+ // is a root based.
+ if (IsFullRootPath(SrcName) || IsFullRootPath(TargetName))
+ return false;
+
+ // Number of ".." in link target.
+ int UpLevels=0;
+ for (int Pos=0;*TargetName!=0;Pos++)
+ {
+ bool Dot2=TargetName[0]=='.' && TargetName[1]=='.' &&
+ (IsPathDiv(TargetName[2]) || TargetName[2]==0) &&
+ (Pos==0 || IsPathDiv(*(TargetName-1)));
+ if (Dot2)
+ UpLevels++;
+ TargetName++;
+ }
+ // If link target includes "..", it must not have another links in its
+ // source path, because they can bypass our safety check. For example,
+ // suppose we extracted "lnk1" -> "." first and "lnk1/lnk2" -> ".." next
+ // or "dir/lnk1" -> ".." first, "dir/lnk1/lnk2" -> ".." next and
+ // file "dir/lnk1/lnk2/poc.txt" last.
+ // Do not confuse with link chains in target, this is in link source path.
+ // It is important for Windows too, though this check can be omitted
+ // if LinksToDirs is invoked in Windows as well.
+ if (UpLevels>0 && LinkInPath(PrepSrcName))
+ return false;
+
+ // We could check just prepared src name, but for extra safety
+ // we check both original (as from archive header) and prepared
+ // (after applying the destination path and -ep switches) names.
+
+ int AllowedDepth=CalcAllowedDepth(SrcName); // Original name depth.
+
+ // Remove the destination path from prepared name if any. We should not
+ // count the destination path depth, because the link target must point
+ // inside of this path, not outside of it.
+ size_t ExtrPathLength=wcslen(Cmd->ExtrPath);
+ if (ExtrPathLength>0 && wcsncmp(PrepSrcName,Cmd->ExtrPath,ExtrPathLength)==0)
+ {
+ PrepSrcName+=ExtrPathLength;
+ while (IsPathDiv(*PrepSrcName))
+ PrepSrcName++;
+ }
+ int PrepAllowedDepth=CalcAllowedDepth(PrepSrcName);
+
+ return AllowedDepth>=UpLevels && PrepAllowedDepth>=UpLevels;
+}
+
+
+bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName,bool &UpLink)
+{
+ // Returning true in Uplink indicates that link target might include ".."
+ // and enables additional checks. It is ok to falsely return true here,
+ // as it implies only the minor performance penalty. But we shall always
+ // return true for links with ".." in target for security reason.
+
+ UpLink=true; // Assume the target might include potentially unsafe "..".
+#if defined(SAVE_LINKS) && defined(_UNIX) || defined(_WIN_ALL)
+ if (Arc.Format==RARFMT50) // For RAR5 archives we can check RedirName for both Unix and Windows.
+ UpLink=wcsstr(Arc.FileHead.RedirName,L"..")!=NULL;
#endif
-#ifdef _WIN_32
- if (Cmd->ProcessOwners && Arc.SubHead.CmpName(SUBHEAD_TYPE_ACL))
- ExtractACLNew(Arc,Name,NameW);
- if (Arc.SubHead.CmpName(SUBHEAD_TYPE_STREAM))
- ExtractStreamsNew(Arc,Name,NameW);
+
+#if defined(SAVE_LINKS) && defined(_UNIX)
+ // For RAR 3.x archives we process links even in test mode to skip link data.
+ if (Arc.Format==RARFMT15)
+ return ExtractUnixLink30(Cmd,DataIO,Arc,LinkName,UpLink);
+ if (Arc.Format==RARFMT50)
+ return ExtractUnixLink50(Cmd,LinkName,&Arc.FileHead);
+#elif defined(_WIN_ALL)
+ // RAR 5.0 archives store link information in file header, so there is
+ // no need to additionally test it if we do not create a file.
+ if (Arc.Format==RARFMT50)
+ return CreateReparsePoint(Cmd,LinkName,&Arc.FileHead);
#endif
+ return false;
}
diff --git a/unrar/unrar/extinfo.hpp b/unrar/unrar/extinfo.hpp
index db7cea5..da82ec3 100644
--- a/unrar/unrar/extinfo.hpp
+++ b/unrar/unrar/extinfo.hpp
@@ -1,8 +1,23 @@
#ifndef _RAR_EXTINFO_
#define _RAR_EXTINFO_
+bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *PrepSrcName,const wchar *TargetName);
+bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName,bool &UpLink);
+#ifdef _UNIX
+void SetUnixOwner(Archive &Arc,const wchar *FileName);
+#endif
+
+bool ExtractHardlink(CommandData *Cmd,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize);
+
+void GetStreamNameNTFS(Archive &Arc,wchar *StreamName,size_t MaxSize);
+
+#ifdef _WIN_ALL
+bool SetPrivilege(LPCTSTR PrivName);
+#endif
+
+void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name);
+void SetExtraInfo(CommandData *Cmd,Archive &Arc,wchar *Name);
+void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,wchar *Name);
-void SetExtraInfo(CommandData *Cmd,Archive &Arc,char *Name,wchar *NameW);
-void SetExtraInfoNew(CommandData *Cmd,Archive &Arc,char *Name,wchar *NameW);
#endif
diff --git a/unrar/unrar/extract.cpp b/unrar/unrar/extract.cpp
index 712dc90..f2eb166 100644
--- a/unrar/unrar/extract.cpp
+++ b/unrar/unrar/extract.cpp
@@ -1,77 +1,125 @@
#include "rar.hpp"
-CmdExtract::CmdExtract()
+CmdExtract::CmdExtract(CommandData *Cmd)
{
+ CmdExtract::Cmd=Cmd;
+
+ *ArcName=0;
+ *DestFileName=0;
+
+ ArcAnalyzed=false;
+ Analyze=new AnalyzeData;
+ memset(Analyze,0,sizeof(*Analyze));
+
TotalFileCount=0;
- *Password=0;
+
+ // Common for all archives involved. Set here instead of DoExtract()
+ // to use in unrar.dll too.
+ // We enable it by default in Unix to care about the case when several
+ // archives are unpacked to same directory with several independent RAR runs.
+ // Worst case performance penalty for a lot of small files seems to be ~3%.
+ // 2023.09.15: Windows performance impact seems to be negligible,
+ // less than 0.5% when extracting mix of small files and folders.
+ // So for extra security we enabled it for Windows too, even though
+ // unlike Unix, Windows doesn't expand lnk1 in symlink targets like
+ // "lnk1/../dir", but converts such path to "dir".
+ ConvertSymlinkPaths=true;
+
Unp=new Unpack(&DataIO);
- Unp->Init(NULL);
+#ifdef RAR_SMP
+ Unp->SetThreads(Cmd->Threads);
+#endif
}
CmdExtract::~CmdExtract()
{
+ FreeAnalyzeData();
delete Unp;
- memset(Password,0,sizeof(Password));
+ delete Analyze;
+}
+
+
+void CmdExtract::FreeAnalyzeData()
+{
+ for (size_t I=0;I<RefList.Size();I++)
+ {
+ // We can have undeleted temporary reference source here if extraction
+ // was interrupted early or if user refused to overwrite prompt.
+ if (RefList[I].TmpName!=NULL)
+ DelFile(RefList[I].TmpName);
+ free(RefList[I].RefName);
+ free(RefList[I].TmpName);
+ }
+ RefList.Reset();
+
+ memset(Analyze,0,sizeof(*Analyze));
}
-void CmdExtract::DoExtract(CommandData *Cmd)
+void CmdExtract::DoExtract()
{
+#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
+ Fat32=NotFat32=false;
+#endif
PasswordCancelled=false;
- DataIO.SetCurrentCommand(*Cmd->Command);
+ DataIO.SetCurrentCommand(Cmd->Command[0]);
- struct FindData FD;
- while (Cmd->GetArcName(ArcName,ArcNameW,sizeof(ArcName)))
- if (FindFile::FastFind(ArcName,ArcNameW,&FD))
- DataIO.TotalArcSize+=FD.Size;
+ if (*Cmd->UseStdin==0)
+ {
+ FindData FD;
+ while (Cmd->GetArcName(ArcName,ASIZE(ArcName)))
+ if (FindFile::FastFind(ArcName,&FD))
+ DataIO.TotalArcSize+=FD.Size;
+ }
- Cmd->ArcNames->Rewind();
- while (Cmd->GetArcName(ArcName,ArcNameW,sizeof(ArcName)))
+ Cmd->ArcNames.Rewind();
+ while (Cmd->GetArcName(ArcName,ASIZE(ArcName)))
{
+ if (Cmd->ManualPassword)
+ Cmd->Password.Clean(); // Clean user entered password before processing next archive.
+
+ ReconstructDone=false; // Must be reset here, not in ExtractArchiveInit().
+ UseExactVolName=false; // Must be reset here, not in ExtractArchiveInit().
while (true)
{
- char PrevCmdPassword[MAXPASSWORD];
- strcpy(PrevCmdPassword,Cmd->Password);
-
- EXTRACT_ARC_CODE Code=ExtractArchive(Cmd);
-
- // restore Cmd->Password which could be changed in IsArchive() call
- // for next header encrypted archive
- strcpy(Cmd->Password,PrevCmdPassword);
-
+ EXTRACT_ARC_CODE Code=ExtractArchive();
if (Code!=EXTRACT_ARC_REPEAT)
break;
}
- if (FindFile::FastFind(ArcName,ArcNameW,&FD))
- DataIO.ProcessedArcSize+=FD.Size;
+ DataIO.ProcessedArcSize+=DataIO.LastArcSize;
}
- if (TotalFileCount==0 && *Cmd->Command!='I')
+ // Clean user entered password. Not really required, just for extra safety.
+ if (Cmd->ManualPassword)
+ Cmd->Password.Clean();
+
+ if (TotalFileCount==0 && Cmd->Command[0]!='I' &&
+ ErrHandler.GetErrorCode()!=RARX_BADPWD) // Not in case of wrong archive password.
{
if (!PasswordCancelled)
- {
- mprintf(St(MExtrNoFiles));
- }
- ErrHandler.SetErrorCode(WARNING);
+ uiMsg(UIERROR_NOFILESTOEXTRACT,ArcName);
+
+ // Other error codes may explain a reason of "no files extracted" clearer,
+ // so set it only if no other errors found (wrong mask set by user).
+ if (ErrHandler.GetErrorCode()==RARX_SUCCESS)
+ ErrHandler.SetErrorCode(RARX_NOFILES);
}
-#ifndef GUI
else
if (!Cmd->DisableDone)
- if (*Cmd->Command=='I')
+ if (Cmd->Command[0]=='I')
mprintf(St(MDone));
else
if (ErrHandler.GetErrorCount()==0)
mprintf(St(MExtrAllOk));
else
mprintf(St(MExtrTotalErr),ErrHandler.GetErrorCount());
-#endif
}
-void CmdExtract::ExtractArchiveInit(CommandData *Cmd,Archive &Arc)
+void CmdExtract::ExtractArchiveInit(Archive &Arc)
{
- DataIO.UnpArcSize=Arc.FileLength();
+ DataIO.AdjustTotalArcSize(&Arc);
FileCount=0;
MatchedArgs=0;
@@ -79,476 +127,415 @@ void CmdExtract::ExtractArchiveInit(CommandData *Cmd,Archive &Arc)
FirstFile=true;
#endif
- if (*Cmd->Password!=0)
- strcpy(Password,Cmd->Password);
- PasswordAll=(*Cmd->Password!=0);
+ GlobalPassword=Cmd->Password.IsSet() || uiIsGlobalPasswordSet();
DataIO.UnpVolume=false;
- PrevExtracted=false;
- SignatureFound=false;
+ PrevProcessed=false;
AllMatchesExact=true;
- ReconstructDone=false;
+ AnySolidDataUnpackedWell=false;
+
+ ArcAnalyzed=false;
StartTime.SetCurrentTime();
+
+ LastCheckedSymlink.clear();
}
-EXTRACT_ARC_CODE CmdExtract::ExtractArchive(CommandData *Cmd)
+EXTRACT_ARC_CODE CmdExtract::ExtractArchive()
{
Archive Arc(Cmd);
- if (!Arc.WOpen(ArcName,ArcNameW))
+ if (*Cmd->UseStdin!=0)
{
- ErrHandler.SetErrorCode(OPEN_ERROR);
- return(EXTRACT_ARC_NEXT);
+ Arc.SetHandleType(FILE_HANDLESTD);
+#ifdef USE_QOPEN
+ Arc.SetProhibitQOpen(true);
+#endif
+ }
+ else
+ {
+#if defined(_WIN_ALL) && !defined(SFX_MODULE) // WinRAR GUI code also resets the cache.
+ if (*Cmd->Command=='T' || Cmd->Test)
+ ResetFileCache(ArcName); // Reset the file cache when testing an archive.
+#endif
+ if (!Arc.WOpen(ArcName))
+ return EXTRACT_ARC_NEXT;
}
if (!Arc.IsArchive(true))
{
-#ifndef GUI
+#if !defined(SFX_MODULE) && !defined(RARDLL)
+ if (CmpExt(ArcName,L"rev"))
+ {
+ wchar FirstVolName[NM];
+ VolNameToFirstName(ArcName,FirstVolName,ASIZE(FirstVolName),true);
+
+ // If several volume names from same volume set are specified
+ // and current volume is not first in set and first volume is present
+ // and specified too, let's skip the current volume.
+ if (wcsicomp(ArcName,FirstVolName)!=0 && FileExist(FirstVolName) &&
+ Cmd->ArcNames.Search(FirstVolName,false))
+ return EXTRACT_ARC_NEXT;
+ RecVolumesTest(Cmd,NULL,ArcName);
+ TotalFileCount++; // Suppress "No files to extract" message.
+ return EXTRACT_ARC_NEXT;
+ }
+#endif
+
mprintf(St(MNotRAR),ArcName);
+
+#ifndef SFX_MODULE
+ if (CmpExt(ArcName,L"rar"))
#endif
- if (CmpExt(ArcName,"rar"))
- ErrHandler.SetErrorCode(WARNING);
- return(EXTRACT_ARC_NEXT);
+ ErrHandler.SetErrorCode(RARX_WARNING);
+ return EXTRACT_ARC_NEXT;
}
- // archive with corrupt encrypted header can be closed in IsArchive() call
- if (!Arc.IsOpened())
- return(EXTRACT_ARC_NEXT);
+ if (Arc.FailedHeaderDecryption) // Bad archive password.
+ return EXTRACT_ARC_NEXT;
#ifndef SFX_MODULE
- if (Arc.Volume && Arc.NotFirstVolume)
+ if (Arc.Volume && !Arc.FirstVolume && !UseExactVolName)
{
- char FirstVolName[NM];
- VolNameToFirstName(ArcName,FirstVolName,(Arc.NewMhd.Flags & MHD_NEWNUMBERING));
+ wchar FirstVolName[NM];
+ VolNameToFirstName(ArcName,FirstVolName,ASIZE(FirstVolName),Arc.NewNumbering);
// If several volume names from same volume set are specified
// and current volume is not first in set and first volume is present
// and specified too, let's skip the current volume.
- if (stricomp(ArcName,FirstVolName)!=0 && FileExist(FirstVolName) &&
- Cmd->ArcNames->Search(FirstVolName,NULL,false))
- return(EXTRACT_ARC_NEXT);
+ if (wcsicomp(ArcName,FirstVolName)!=0 && FileExist(FirstVolName) &&
+ Cmd->ArcNames.Search(FirstVolName,false))
+ return EXTRACT_ARC_NEXT;
+ }
+#endif
+
+ Arc.ViewComment(); // Must be before possible EXTRACT_ARC_REPEAT.
+
+ int64 VolumeSetSize=0; // Total size of volumes after the current volume.
+
+#ifndef SFX_MODULE
+ if (!ArcAnalyzed && *Cmd->UseStdin==0)
+ {
+ AnalyzeArchive(Arc.FileName,Arc.Volume,Arc.NewNumbering);
+ ArcAnalyzed=true; // Avoid repeated analysis on EXTRACT_ARC_REPEAT.
}
#endif
if (Arc.Volume)
{
+#ifndef SFX_MODULE
+ // Try to speed up extraction for independent solid volumes by starting
+ // extraction from non-first volume if we can.
+ if (*Analyze->StartName!=0)
+ {
+ wcsncpyz(ArcName,Analyze->StartName,ASIZE(ArcName));
+ *Analyze->StartName=0;
+
+ UseExactVolName=true;
+ return EXTRACT_ARC_REPEAT;
+ }
+#endif
+
// Calculate the total size of all accessible volumes.
// This size is necessary to display the correct total progress indicator.
- char NextName[NM];
- wchar NextNameW[NM];
-
- strcpy(NextName,Arc.FileName);
- strcpyw(NextNameW,Arc.FileNameW);
+ wchar NextName[NM];
+ wcsncpyz(NextName,Arc.FileName,ASIZE(NextName));
while (true)
{
// First volume is already added to DataIO.TotalArcSize
// in initial TotalArcSize calculation in DoExtract.
// So we skip it and start from second volume.
- NextVolumeName(NextName,NextNameW,ASIZE(NextName),(Arc.NewMhd.Flags & MHD_NEWNUMBERING)==0 || Arc.OldFormat);
- struct FindData FD;
- if (FindFile::FastFind(NextName,NextNameW,&FD))
- DataIO.TotalArcSize+=FD.Size;
+ NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering);
+ FindData FD;
+ if (FindFile::FastFind(NextName,&FD))
+ VolumeSetSize+=FD.Size;
else
break;
}
+ DataIO.TotalArcSize+=VolumeSetSize;
}
- ExtractArchiveInit(Cmd,Arc);
+ ExtractArchiveInit(Arc);
if (*Cmd->Command=='T' || *Cmd->Command=='I')
Cmd->Test=true;
-#ifndef GUI
+
if (*Cmd->Command=='I')
+ {
Cmd->DisablePercentage=true;
+ }
else
- if (Cmd->Test)
- mprintf(St(MExtrTest),ArcName);
- else
- mprintf(St(MExtracting),ArcName);
-#endif
+ uiStartArchiveExtract(!Cmd->Test,ArcName);
- Arc.ViewComment();
-
- // RAR can close a corrupt encrypted archive
- if (!Arc.IsOpened())
- return(EXTRACT_ARC_NEXT);
+#ifndef SFX_MODULE
+ if (Analyze->StartPos!=0)
+ {
+ Arc.Seek(Analyze->StartPos,SEEK_SET);
+ Analyze->StartPos=0;
+ }
+#endif
while (1)
{
- int Size=Arc.ReadHeader();
+ size_t Size=Arc.ReadHeader();
+
+
bool Repeat=false;
- if (!ExtractCurrentFile(Cmd,Arc,Size,Repeat))
+ if (!ExtractCurrentFile(Arc,Size,Repeat))
if (Repeat)
{
- return(EXTRACT_ARC_REPEAT);
+ // If we started extraction from not first volume and need to
+ // restart it from first, we must set DataIO.TotalArcSize to size
+ // of new first volume to display the total progress correctly.
+ FindData NewArc;
+ if (FindFile::FastFind(ArcName,&NewArc))
+ DataIO.TotalArcSize=NewArc.Size;
+ return EXTRACT_ARC_REPEAT;
}
else
break;
}
- return(EXTRACT_ARC_NEXT);
+
+#if !defined(SFX_MODULE) && !defined(RARDLL)
+ if (Cmd->Test && Arc.Volume)
+ RecVolumesTest(Cmd,&Arc,ArcName);
+#endif
+
+ return EXTRACT_ARC_NEXT;
}
-bool CmdExtract::ExtractCurrentFile(CommandData *Cmd,Archive &Arc,int HeaderSize,bool &Repeat)
+bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
{
- char Command=*Cmd->Command;
- if (HeaderSize<=0)
+ wchar Command=Cmd->Command[0];
+ if (HeaderSize==0)
if (DataIO.UnpVolume)
{
#ifdef NOVOLUME
- return(false);
+ return false;
#else
+ // Supposing we unpack an old RAR volume without the end of archive
+ // record and last file is not split between volumes.
if (!MergeArchive(Arc,&DataIO,false,Command))
{
- ErrHandler.SetErrorCode(WARNING);
- return(false);
+ ErrHandler.SetErrorCode(RARX_WARNING);
+ return false;
}
- SignatureFound=false;
#endif
}
else
- return(false);
- int HeadType=Arc.GetHeaderType();
- if (HeadType!=FILE_HEAD)
+ return false;
+
+ HEADER_TYPE HeaderType=Arc.GetHeaderType();
+ if (HeaderType==HEAD_FILE)
{
- if (HeadType==AV_HEAD || HeadType==SIGN_HEAD)
- SignatureFound=true;
-#if !defined(SFX_MODULE) && !defined(_WIN_CE)
- if (HeadType==SUB_HEAD && PrevExtracted)
- SetExtraInfo(Cmd,Arc,DestFileName,*DestFileNameW ? DestFileNameW:NULL);
-#endif
- if (HeadType==NEWSUB_HEAD)
- {
- if (Arc.SubHead.CmpName(SUBHEAD_TYPE_AV))
- SignatureFound=true;
-#if !defined(NOSUBBLOCKS) && !defined(_WIN_CE)
- if (PrevExtracted)
- SetExtraInfoNew(Cmd,Arc,DestFileName,*DestFileNameW ? DestFileNameW:NULL);
+ // Unlike Arc.FileName, ArcName might store an old volume name here.
+ if (Analyze->EndPos!=0 && Analyze->EndPos==Arc.CurBlockPos &&
+ (*Analyze->EndName==0 || wcscmp(Analyze->EndName,Arc.FileName)==0))
+ return false;
+ }
+ else
+ {
+#ifndef SFX_MODULE
+ if (Arc.Format==RARFMT15 && HeaderType==HEAD3_OLDSERVICE && PrevProcessed)
+ SetExtraInfo20(Cmd,Arc,DestFileName);
#endif
- }
- if (HeadType==ENDARC_HEAD)
- if (Arc.EndArcHead.Flags & EARC_NEXT_VOLUME)
+ if (HeaderType==HEAD_SERVICE && PrevProcessed)
+ SetExtraInfo(Cmd,Arc,DestFileName);
+ if (HeaderType==HEAD_ENDARC)
+ if (Arc.EndArcHead.NextVolume)
{
-#ifndef NOVOLUME
+#ifdef NOVOLUME
+ return false;
+#else
if (!MergeArchive(Arc,&DataIO,false,Command))
{
- ErrHandler.SetErrorCode(WARNING);
- return(false);
+ ErrHandler.SetErrorCode(RARX_WARNING);
+ return false;
}
- SignatureFound=false;
-#endif
Arc.Seek(Arc.CurBlockPos,SEEK_SET);
- return(true);
+ return true;
+#endif
}
else
- return(false);
+ return false;
Arc.SeekToNext();
- return(true);
+ return true;
}
- PrevExtracted=false;
-
- if (SignatureFound ||
- !Cmd->Recurse && MatchedArgs>=Cmd->FileArgs->ItemsCount() &&
- AllMatchesExact)
- return(false);
-
- char ArcFileName[NM];
- IntToExt(Arc.NewLhd.FileName,Arc.NewLhd.FileName);
- strcpy(ArcFileName,Arc.NewLhd.FileName);
-
- wchar ArcFileNameW[NM];
- *ArcFileNameW=0;
+ PrevProcessed=false;
+
+ // We can get negative sizes in corrupt archive and it is unacceptable
+ // for size comparisons in ComprDataIO::UnpRead, where we cast sizes
+ // to size_t and can exceed another read or available size. We could fix it
+ // when reading an archive. But we prefer to do it here, because this
+ // function is called directly in unrar.dll, so we fix bad parameters
+ // passed to dll. Also we want to see real negative sizes in the listing
+ // of corrupt archive. To prevent uninitialized data access perform
+ // these checks after rejecting zero length and non-file headers above.
+ if (Arc.FileHead.PackSize<0)
+ Arc.FileHead.PackSize=0;
+ if (Arc.FileHead.UnpSize<0)
+ Arc.FileHead.UnpSize=0;
+
+ // 2022.03.20: We might remove this check in the future.
+ // It duplicates Analyze->EndPos and Analyze->EndName in all cases except
+ // volumes on removable media.
+ if (!Cmd->Recurse && MatchedArgs>=Cmd->FileArgs.ItemsCount() && AllMatchesExact)
+ return false;
int MatchType=MATCH_WILDSUBPATH;
bool EqualNames=false;
- int MatchNumber=Cmd->IsProcessFile(Arc.NewLhd,&EqualNames,MatchType);
- bool ExactMatch=MatchNumber!=0;
-#if !defined(SFX_MODULE) && !defined(_WIN_CE)
+ wchar MatchedArg[NM];
+ int MatchNumber=Cmd->IsProcessFile(Arc.FileHead,&EqualNames,MatchType,0,MatchedArg,ASIZE(MatchedArg));
+ bool MatchFound=MatchNumber!=0;
+#ifndef SFX_MODULE
if (Cmd->ExclPath==EXCL_BASEPATH)
{
- *Cmd->ArcPath=0;
- if (ExactMatch)
- {
- Cmd->FileArgs->Rewind();
- if (Cmd->FileArgs->GetString(Cmd->ArcPath,NULL,sizeof(Cmd->ArcPath),MatchNumber-1))
- *PointToName(Cmd->ArcPath)=0;
- }
+ wcsncpyz(Cmd->ArcPath,MatchedArg,ASIZE(Cmd->ArcPath));
+ *PointToName(Cmd->ArcPath)=0;
+ if (IsWildcard(Cmd->ArcPath)) // Cannot correctly process path*\* masks here.
+ *Cmd->ArcPath=0;
}
#endif
- if (ExactMatch && !EqualNames)
+ if (MatchFound && !EqualNames)
AllMatchesExact=false;
-#ifdef UNICODE_SUPPORTED
- bool WideName=(Arc.NewLhd.Flags & LHD_UNICODE) && UnicodeEnabled();
-#else
- bool WideName=false;
-#endif
+ Arc.ConvertAttributes();
-#ifdef _APPLE
- if (WideName)
+#if !defined(SFX_MODULE) && !defined(RARDLL)
+ if (Arc.FileHead.SplitBefore && FirstFile && !UseExactVolName)
{
- WideToUtf(Arc.NewLhd.FileNameW,ArcFileName,sizeof(ArcFileName));
- WideName=false;
- }
-#endif
-
- wchar *DestNameW=WideName ? DestFileNameW:NULL;
+ wchar CurVolName[NM];
+ wcsncpyz(CurVolName,ArcName,ASIZE(CurVolName));
+ GetFirstVolIfFullSet(ArcName,Arc.NewNumbering,ArcName,ASIZE(ArcName));
-#ifdef UNICODE_SUPPORTED
- if (WideName)
- {
- ConvertPath(Arc.NewLhd.FileNameW,ArcFileNameW);
- char Name[NM];
- if (WideToChar(ArcFileNameW,Name) && IsNameUsable(Name))
- strcpy(ArcFileName,Name);
+ if (wcsicomp(ArcName,CurVolName)!=0 && FileExist(ArcName))
+ {
+ wcsncpyz(Cmd->ArcName,ArcName,ASIZE(ArcName)); // For GUI "Delete archive after extraction".
+ // If first volume name does not match the current name and if such
+ // volume name really exists, let's unpack from this first volume.
+ Repeat=true;
+ return false;
+ }
+#ifndef RARDLL
+ if (!ReconstructDone)
+ {
+ ReconstructDone=true;
+ if (RecVolumesRestore(Cmd,Arc.FileName,true))
+ {
+ Repeat=true;
+ return false;
+ }
+ }
+#endif
+ wcsncpyz(ArcName,CurVolName,ASIZE(ArcName));
}
#endif
- ConvertPath(ArcFileName,ArcFileName);
-
- if (Arc.IsArcLabel())
- return(true);
+ wchar ArcFileName[NM];
+ ConvertPath(Arc.FileHead.FileName,ArcFileName,ASIZE(ArcFileName));
- if (Arc.NewLhd.Flags & LHD_VERSION)
+ if (Arc.FileHead.Version)
{
if (Cmd->VersionControl!=1 && !EqualNames)
{
if (Cmd->VersionControl==0)
- ExactMatch=false;
- int Version=ParseVersionFileName(ArcFileName,ArcFileNameW,false);
+ MatchFound=false;
+ int Version=ParseVersionFileName(ArcFileName,false);
if (Cmd->VersionControl-1==Version)
- ParseVersionFileName(ArcFileName,ArcFileNameW,true);
+ ParseVersionFileName(ArcFileName,true);
else
- ExactMatch=false;
+ MatchFound=false;
}
}
else
if (!Arc.IsArcDir() && Cmd->VersionControl>1)
- ExactMatch=false;
-
- Arc.ConvertAttributes();
+ MatchFound=false;
-#ifndef SFX_MODULE
- if ((Arc.NewLhd.Flags & (LHD_SPLIT_BEFORE/*|LHD_SOLID*/)) && FirstFile)
- {
- char CurVolName[NM];
- strcpy(CurVolName,ArcName);
-
- VolNameToFirstName(ArcName,ArcName,(Arc.NewMhd.Flags & MHD_NEWNUMBERING));
- if (stricomp(ArcName,CurVolName)!=0 && FileExist(ArcName))
- {
- *ArcNameW=0;
- Repeat=true;
- return(false);
- }
-#if !defined(RARDLL) && !defined(_WIN_CE)
- if (!ReconstructDone)
- {
- ReconstructDone=true;
-
- RecVolumes RecVol;
- if (RecVol.Restore(Cmd,Arc.FileName,Arc.FileNameW,true))
- {
- Repeat=true;
- return(false);
- }
- }
-#endif
- strcpy(ArcName,CurVolName);
- }
-#endif
- DataIO.UnpVolume=(Arc.NewLhd.Flags & LHD_SPLIT_AFTER);
+ DataIO.UnpVolume=Arc.FileHead.SplitAfter;
DataIO.NextVolumeMissing=false;
- Arc.Seek(Arc.NextBlockPos-Arc.NewLhd.FullPackSize,SEEK_SET);
+ Arc.Seek(Arc.NextBlockPos-Arc.FileHead.PackSize,SEEK_SET);
- bool TestMode=false;
bool ExtrFile=false;
bool SkipSolid=false;
#ifndef SFX_MODULE
- if (FirstFile && (ExactMatch || Arc.Solid) && (Arc.NewLhd.Flags & (LHD_SPLIT_BEFORE/*|LHD_SOLID*/))!=0)
+ if (FirstFile && (MatchFound || Arc.Solid) && Arc.FileHead.SplitBefore)
{
- if (ExactMatch)
+ if (MatchFound)
{
- Log(Arc.FileName,St(MUnpCannotMerge),ArcFileName);
+ uiMsg(UIERROR_NEEDPREVVOL,Arc.FileName,ArcFileName);
#ifdef RARDLL
Cmd->DllError=ERAR_BAD_DATA;
#endif
- ErrHandler.SetErrorCode(OPEN_ERROR);
+ ErrHandler.SetErrorCode(RARX_OPEN);
}
- ExactMatch=false;
+ MatchFound=false;
}
FirstFile=false;
#endif
- if (ExactMatch || (SkipSolid=Arc.Solid)!=0)
- {
- if ((Arc.NewLhd.Flags & LHD_PASSWORD)!=0)
-#ifndef RARDLL
- if (*Password==0)
-#endif
+ bool RefTarget=false;
+ if (!MatchFound)
+ for (size_t I=0;I<RefList.Size();I++)
+ if (wcscmp(ArcFileName,RefList[I].RefName)==0)
{
-#ifdef RARDLL
- if (*Cmd->Password==0)
- if (Cmd->Callback==NULL ||
- Cmd->Callback(UCM_NEEDPASSWORD,Cmd->UserData,(LPARAM)Cmd->Password,sizeof(Cmd->Password))==-1)
- return(false);
- strcpy(Password,Cmd->Password);
-
-#else
- if (!GetPassword(PASSWORD_FILE,ArcFileName,Password,sizeof(Password)))
+ ExtractRef *MatchedRef=&RefList[I];
+
+ if (!Cmd->Test) // While harmless, it is useless for 't'.
{
- PasswordCancelled=true;
- return(false);
+ // If reference source isn't selected, but target is selected,
+ // we unpack the source under the temporary name and then rename
+ // or copy it to target name. We do not unpack it under the target
+ // name immediately, because the same source can be used by multiple
+ // targets and it is possible that first target isn't unpacked
+ // for some reason. Also targets might have associated service blocks
+ // like ACLs. All this would complicate processing a lot.
+ wcsncpyz(DestFileName,*Cmd->TempPath!=0 ? Cmd->TempPath:Cmd->ExtrPath,ASIZE(DestFileName));
+ AddEndSlash(DestFileName,ASIZE(DestFileName));
+ wcsncatz(DestFileName,L"__tmp_reference_source_",ASIZE(DestFileName));
+ MkTemp(DestFileName,ASIZE(DestFileName));
+ MatchedRef->TmpName=wcsdup(DestFileName);
}
-#endif
+ RefTarget=true; // Need it even for 't' to test the reference source.
+ break;
}
-#if !defined(GUI) && !defined(SILENT)
- else
- if (!PasswordAll && (!Arc.Solid || Arc.NewLhd.UnpVer>=20 && (Arc.NewLhd.Flags & LHD_SOLID)==0))
- {
- eprintf(St(MUseCurPsw),ArcFileName);
- switch(Cmd->AllYes ? 1:Ask(St(MYesNoAll)))
- {
- case -1:
- ErrHandler.Exit(USER_BREAK);
- case 2:
- if (!GetPassword(PASSWORD_FILE,ArcFileName,Password,sizeof(Password)))
- {
- return(false);
- }
- break;
- case 3:
- PasswordAll=true;
- break;
- }
- }
-#endif
-
-#ifndef SFX_MODULE
- if (*Cmd->ExtrPath==0 && *Cmd->ExtrPathW!=0)
- WideToChar(Cmd->ExtrPathW,DestFileName);
- else
-#endif
- strcpy(DestFileName,Cmd->ExtrPath);
-
-
-#ifndef SFX_MODULE
- if (Cmd->AppendArcNameToPath)
- {
- strcat(DestFileName,PointToName(Arc.FirstVolumeName));
- SetExt(DestFileName,NULL);
- AddEndSlash(DestFileName);
- }
-#endif
-
- char *ExtrName=ArcFileName;
-
- bool EmptyName=false;
-#ifndef SFX_MODULE
- int Length=strlen(Cmd->ArcPath);
- if (Length>1 && IsPathDiv(Cmd->ArcPath[Length-1]) &&
- strlen(ArcFileName)==Length-1)
- Length--;
- if (Length>0 && strnicomp(Cmd->ArcPath,ArcFileName,Length)==0)
- {
- ExtrName+=Length;
- while (*ExtrName==CPATHDIVIDER)
- ExtrName++;
- if (*ExtrName==0)
- EmptyName=true;
- }
-#endif
-
- bool AbsPaths=Cmd->ExclPath==EXCL_ABSPATH && Command=='X' && IsDriveDiv(':');
- if (AbsPaths)
- *DestFileName=0;
-
- if (Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH)
- strcat(DestFileName,PointToName(ExtrName));
+
+ if (Arc.FileHead.Encrypted && Cmd->SkipEncrypted)
+ if (Arc.Solid)
+ return false; // Abort the entire extraction for solid archive.
else
- strcat(DestFileName,ExtrName);
-
- char DiskLetter=etoupper(DestFileName[0]);
-
- if (AbsPaths && DestFileName[1]=='_' && IsPathDiv(DestFileName[2]) &&
- DiskLetter>='A' && DiskLetter<='Z')
- DestFileName[1]=':';
-
-#ifndef SFX_MODULE
- if (!WideName && *Cmd->ExtrPathW!=0)
- {
- DestNameW=DestFileNameW;
- WideName=true;
- CharToWide(ArcFileName,ArcFileNameW);
- }
-#endif
-
- if (WideName)
- {
- if (*Cmd->ExtrPathW!=0)
- strcpyw(DestFileNameW,Cmd->ExtrPathW);
- else
- CharToWide(Cmd->ExtrPath,DestFileNameW);
-
-#ifndef SFX_MODULE
- if (Cmd->AppendArcNameToPath)
- {
- wchar FileNameW[NM];
- if (*Arc.FirstVolumeNameW!=0)
- strcpyw(FileNameW,Arc.FirstVolumeNameW);
- else
- CharToWide(Arc.FirstVolumeName,FileNameW);
- strcatw(DestFileNameW,PointToName(FileNameW));
- SetExt(DestFileNameW,NULL);
- AddEndSlash(DestFileNameW);
- }
-#endif
- wchar *ExtrNameW=ArcFileNameW;
-#ifndef SFX_MODULE
- if (Length>0)
- {
- wchar ArcPathW[NM];
- GetWideName(Cmd->ArcPath,Cmd->ArcPathW,ArcPathW);
- Length=strlenw(ArcPathW);
- }
- ExtrNameW+=Length;
- while (*ExtrNameW==CPATHDIVIDER)
- ExtrNameW++;
-#endif
-
- if (AbsPaths)
- *DestFileNameW=0;
-
- if (Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH)
- strcatw(DestFileNameW,PointToName(ExtrNameW));
- else
- strcatw(DestFileNameW,ExtrNameW);
+ MatchFound=false; // Skip only the current file for non-solid archive.
+
+ if (MatchFound || RefTarget || (SkipSolid=Arc.Solid)!=0)
+ {
+ // First common call of uiStartFileExtract. It is done before overwrite
+ // prompts, so if SkipSolid state is changed below, we'll need to make
+ // additional uiStartFileExtract calls with updated parameters.
+ if (!uiStartFileExtract(ArcFileName,!Cmd->Test,Cmd->Test && Command!='I',SkipSolid))
+ return false;
- if (AbsPaths && DestFileNameW[1]=='_' && IsPathDiv(DestFileNameW[2]))
- DestFileNameW[1]=':';
- }
- else
- *DestFileNameW=0;
+ if (!RefTarget)
+ ExtrPrepareName(Arc,ArcFileName,DestFileName,ASIZE(DestFileName));
- ExtrFile=!SkipSolid && !EmptyName && (Arc.NewLhd.Flags & LHD_SPLIT_BEFORE)==0;
+ // DestFileName can be set empty in case of excessive -ap switch.
+ ExtrFile=!SkipSolid && *DestFileName!=0 && !Arc.FileHead.SplitBefore;
if ((Cmd->FreshFiles || Cmd->UpdateFiles) && (Command=='E' || Command=='X'))
{
- struct FindData FD;
- if (FindFile::FastFind(DestFileName,DestNameW,&FD))
+ FindData FD;
+ if (FindFile::FastFind(DestFileName,&FD))
{
- if (FD.mtime >= Arc.NewLhd.mtime)
+ if (FD.mtime >= Arc.FileHead.mtime)
{
// If directory already exists and its modification time is newer
// than start of extraction, it is likely it was created
@@ -565,195 +552,169 @@ bool CmdExtract::ExtractCurrentFile(CommandData *Cmd,Archive &Arc,int HeaderSize
ExtrFile=false;
}
- // skip encrypted file if no password is specified
- if ((Arc.NewLhd.Flags & LHD_PASSWORD)!=0 && *Password==0)
+ if (!CheckUnpVer(Arc,ArcFileName))
{
- ErrHandler.SetErrorCode(WARNING);
-#ifdef RARDLL
- Cmd->DllError=ERAR_MISSING_PASSWORD;
-#endif
- ExtrFile=false;
- }
-
-#ifdef RARDLL
- if (*Cmd->DllDestName)
- {
- strncpyz(DestFileName,Cmd->DllDestName,ASIZE(DestFileName));
- *DestFileNameW=0;
- if (Cmd->DllOpMode!=RAR_EXTRACT)
- ExtrFile=false;
- }
- if (*Cmd->DllDestNameW)
- {
- strncpyzw(DestFileNameW,Cmd->DllDestNameW,ASIZE(DestFileNameW));
- DestNameW=DestFileNameW;
- if (Cmd->DllOpMode!=RAR_EXTRACT)
- ExtrFile=false;
- }
-#endif
-
-#ifdef SFX_MODULE
- if ((Arc.NewLhd.UnpVer!=UNP_VER && Arc.NewLhd.UnpVer!=29) &&
- Arc.NewLhd.Method!=0x30)
-#else
- if (Arc.NewLhd.UnpVer<13 || Arc.NewLhd.UnpVer>UNP_VER)
-#endif
- {
-#ifndef SILENT
- Log(Arc.FileName,St(MUnknownMeth),ArcFileName);
-#ifndef SFX_MODULE
- Log(Arc.FileName,St(MVerRequired),Arc.NewLhd.UnpVer/10,Arc.NewLhd.UnpVer%10);
-#endif
-#endif
- ExtrFile=false;
- ErrHandler.SetErrorCode(WARNING);
+ ErrHandler.SetErrorCode(RARX_FATAL);
#ifdef RARDLL
Cmd->DllError=ERAR_UNKNOWN_FORMAT;
#endif
+ Arc.SeekToNext();
+ return !Arc.Solid; // Can try extracting next file only in non-solid archive.
}
- File CurFile;
+ if (Arc.FileHead.Encrypted)
+ {
+ RarCheckPassword CheckPwd;
+ if (Arc.Format==RARFMT50 && Arc.FileHead.UsePswCheck && !Arc.BrokenHeader)
+ CheckPwd.Set(Arc.FileHead.Salt,Arc.FileHead.InitV,Arc.FileHead.Lg2Count,Arc.FileHead.PswCheck);
- if (!IsLink(Arc.NewLhd.FileAttr))
- if (Arc.IsArcDir())
+ while (true) // Repeat the password prompt for wrong and empty passwords.
{
- if (!ExtrFile || Command=='P' || Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH)
- return(true);
- if (SkipSolid)
- {
-#ifndef GUI
- mprintf(St(MExtrSkipFile),ArcFileName);
-#endif
- return(true);
- }
- TotalFileCount++;
- if (Cmd->Test)
+ // Stop archive extracting if user cancelled a password prompt.
+#ifdef RARDLL
+ if (!ExtrDllGetPassword())
{
-#ifndef GUI
- mprintf(St(MExtrTestFile),ArcFileName);
- mprintf(" %s",St(MOk));
-#endif
- return(true);
+ Cmd->DllError=ERAR_MISSING_PASSWORD;
+ return false;
}
- MKDIR_CODE MDCode=MakeDir(DestFileName,DestNameW,Arc.NewLhd.FileAttr);
- bool DirExist=false;
- if (MDCode!=MKDIR_SUCCESS)
+#else
+ if (!ExtrGetPassword(Arc,ArcFileName,CheckPwd.IsSet() ? &CheckPwd:NULL))
{
- DirExist=FileExist(DestFileName,DestNameW);
- if (DirExist && !IsDir(GetFileAttr(DestFileName,DestNameW)))
- {
- bool UserReject;
- FileCreate(Cmd,NULL,DestFileName,DestNameW,Cmd->Overwrite,&UserReject,Arc.NewLhd.FullUnpSize,Arc.NewLhd.FileTime);
- DirExist=false;
- }
- CreatePath(DestFileName,DestNameW,true);
- MDCode=MakeDir(DestFileName,DestNameW,Arc.NewLhd.FileAttr);
+ PasswordCancelled=true;
+ return false;
}
- if (MDCode==MKDIR_SUCCESS)
- {
-#ifndef GUI
- mprintf(St(MCreatDir),DestFileName);
- mprintf(" %s",St(MOk));
#endif
- PrevExtracted=true;
- }
- else
- if (DirExist)
+
+ // Set a password before creating the file, so we can skip creating
+ // in case of wrong password.
+ SecPassword FilePassword=Cmd->Password;
+ #if defined(_WIN_ALL) && !defined(SFX_MODULE)
+ ConvertDosPassword(Arc,FilePassword);
+ #endif
+
+ byte PswCheck[SIZE_PSWCHECK];
+ DataIO.SetEncryption(false,Arc.FileHead.CryptMethod,&FilePassword,
+ Arc.FileHead.SaltSet ? Arc.FileHead.Salt:NULL,
+ Arc.FileHead.InitV,Arc.FileHead.Lg2Count,
+ Arc.FileHead.HashKey,PswCheck);
+
+ // If header is damaged, we cannot rely on password check value,
+ // because it can be damaged too.
+ if (Arc.FileHead.UsePswCheck && !Arc.BrokenHeader &&
+ memcmp(Arc.FileHead.PswCheck,PswCheck,SIZE_PSWCHECK)!=0)
+ {
+ if (GlobalPassword) // For -p<pwd> or Ctrl+P to avoid the infinite loop.
{
- SetFileAttr(DestFileName,DestNameW,Arc.NewLhd.FileAttr);
- PrevExtracted=true;
+ // This message is used by Android GUI to reset cached passwords.
+ // Update appropriate code if changed.
+ uiMsg(UIERROR_BADPSW,Arc.FileName,ArcFileName);
}
- else
+ else // For passwords entered manually.
{
- Log(Arc.FileName,St(MExtrErrMkDir),DestFileName);
- ErrHandler.SysErrMsg();
-#ifdef RARDLL
- Cmd->DllError=ERAR_ECREATE;
-#endif
- ErrHandler.SetErrorCode(CREATE_ERROR);
+ // This message is used by Android GUI and Windows GUI and SFX to
+ // reset cached passwords. Update appropriate code if changed.
+ uiMsg(UIWAIT_BADPSW,Arc.FileName,ArcFileName);
+ Cmd->Password.Clean();
+
+ // Avoid new requests for unrar.dll to prevent the infinite loop
+ // if app always returns the same password.
+ #ifndef RARDLL
+ continue; // Request a password again.
+ #endif
}
- if (PrevExtracted)
- {
-#if defined(_WIN_32) && !defined(_WIN_CE) && !defined(SFX_MODULE)
- if (Cmd->SetCompressedAttr &&
- (Arc.NewLhd.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0 && WinNT())
- SetFileCompression(DestFileName,DestNameW,true);
-#endif
- SetDirTime(DestFileName,DestNameW,
- Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.NewLhd.mtime,
- Cmd->xctime==EXTTIME_NONE ? NULL:&Arc.NewLhd.ctime,
- Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.NewLhd.atime);
+ #ifdef RARDLL
+ // If we already have ERAR_EOPEN as result of missing volume,
+ // we should not replace it with less precise ERAR_BAD_PASSWORD.
+ if (Cmd->DllError!=ERAR_EOPEN)
+ Cmd->DllError=ERAR_BAD_PASSWORD;
+ #endif
+ ErrHandler.SetErrorCode(RARX_BADPWD);
+ ExtrFile=false;
}
- return(true);
+ break;
}
- else
- {
- if (Cmd->Test && ExtrFile)
- TestMode=true;
-#if !defined(GUI) && !defined(SFX_MODULE)
- if (Command=='P' && ExtrFile)
- CurFile.SetHandleType(FILE_HANDLESTD);
-#endif
- if ((Command=='E' || Command=='X') && ExtrFile && !Cmd->Test)
- {
- bool UserReject;
- if (!FileCreate(Cmd,&CurFile,DestFileName,DestNameW,Cmd->Overwrite,&UserReject,Arc.NewLhd.FullUnpSize,Arc.NewLhd.FileTime))
- {
- ExtrFile=false;
- if (!UserReject)
- {
- ErrHandler.CreateErrorMsg(Arc.FileName,DestFileName);
- ErrHandler.SetErrorCode(CREATE_ERROR);
+ }
+ else
+ DataIO.SetEncryption(false,CRYPT_NONE,NULL,NULL,NULL,0,NULL,NULL);
+
#ifdef RARDLL
- Cmd->DllError=ERAR_ECREATE;
+ if (*Cmd->DllDestName!=0)
+ wcsncpyz(DestFileName,Cmd->DllDestName,ASIZE(DestFileName));
#endif
- if (!IsNameUsable(DestFileName))
- {
- Log(Arc.FileName,St(MCorrectingName));
- char OrigName[sizeof(DestFileName)];
- strncpyz(OrigName,DestFileName,ASIZE(OrigName));
- MakeNameUsable(DestFileName,true);
- CreatePath(DestFileName,NULL,true);
- if (FileCreate(Cmd,&CurFile,DestFileName,NULL,Cmd->Overwrite,&UserReject,Arc.NewLhd.FullUnpSize,Arc.NewLhd.FileTime))
- {
-#ifndef SFX_MODULE
- Log(Arc.FileName,St(MRenaming),OrigName,DestFileName);
-#endif
- ExtrFile=true;
- }
- else
- ErrHandler.CreateErrorMsg(Arc.FileName,DestFileName);
- }
- }
- }
- }
+ if (ExtrFile && Command!='P' && !Cmd->Test && !Cmd->AbsoluteLinks &&
+ ConvertSymlinkPaths)
+ ExtrFile=LinksToDirs(DestFileName,Cmd->ExtrPath,LastCheckedSymlink);
+
+ File CurFile;
+
+ bool LinkEntry=Arc.FileHead.RedirType!=FSREDIR_NONE;
+ if (LinkEntry && (Arc.FileHead.RedirType!=FSREDIR_FILECOPY))
+ {
+ if (ExtrFile && Command!='P' && !Cmd->Test)
+ {
+ // Overwrite prompt for symbolic and hard links and when we move
+ // a temporary file to the file reference instead of copying it.
+ bool UserReject=false;
+ if (FileExist(DestFileName) && !UserReject)
+ FileCreate(Cmd,NULL,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime);
+ if (UserReject)
+ ExtrFile=false;
+ }
+ }
+ else
+ if (Arc.IsArcDir())
+ {
+ if (!ExtrFile || Command=='P' || Command=='I' || Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH)
+ return true;
+ TotalFileCount++;
+ ExtrCreateDir(Arc,ArcFileName);
+ // It is important to not increment MatchedArgs here, so we extract
+ // dir with its entire contents and not dir record only even if
+ // dir record precedes files.
+ return true;
}
+ else
+ if (ExtrFile) // Create files and file copies (FSREDIR_FILECOPY).
+ ExtrFile=ExtrCreateFile(Arc,CurFile);
if (!ExtrFile && Arc.Solid)
{
SkipSolid=true;
- TestMode=true;
ExtrFile=true;
+
+ // We changed SkipSolid, so we need to call uiStartFileExtract
+ // with "Skip" parameter to change the operation status
+ // from "extracting" to "skipping". For example, it can be necessary
+ // if user answered "No" to overwrite prompt when unpacking
+ // a solid archive.
+ if (!uiStartFileExtract(ArcFileName,false,false,true))
+ return false;
}
if (ExtrFile)
{
+ // Set it in test mode, so we also test subheaders such as NTFS streams
+ // after tested file.
+ if (Cmd->Test)
+ PrevProcessed=true;
+
+ bool TestMode=Cmd->Test || SkipSolid; // Unpack to memory, not to disk.
+
if (!SkipSolid)
{
if (!TestMode && Command!='P' && CurFile.IsDevice())
{
- Log(Arc.FileName,St(MInvalidName),DestFileName);
+ uiMsg(UIERROR_INVALIDNAME,Arc.FileName,DestFileName);
ErrHandler.WriteError(Arc.FileName,DestFileName);
}
TotalFileCount++;
}
FileCount++;
-#ifndef GUI
- if (Command!='I')
+ if (Command!='I' && !Cmd->DisableNames)
if (SkipSolid)
mprintf(St(MExtrSkipFile),ArcFileName);
else
- switch(Cmd->Test ? 'T':Command)
+ switch(Cmd->Test ? 'T':Command) // "Test" can be also enabled by -t switch.
{
case 'T':
mprintf(St(MExtrTestFile),ArcFileName);
@@ -768,146 +729,937 @@ bool CmdExtract::ExtractCurrentFile(CommandData *Cmd,Archive &Arc,int HeaderSize
mprintf(St(MExtrFile),DestFileName);
break;
}
- if (!Cmd->DisablePercentage)
- mprintf(" ");
-#endif
+ if (!Cmd->DisablePercentage && !Cmd->DisableNames)
+ mprintf(L" ");
+ if (Cmd->DisableNames)
+ uiEolAfterMsg(); // Avoid erasing preceding messages by percentage indicator in -idn mode.
+
DataIO.CurUnpRead=0;
DataIO.CurUnpWrite=0;
- DataIO.UnpFileCRC=Arc.OldFormat ? 0 : 0xffffffff;
- DataIO.PackedCRC=0xffffffff;
- DataIO.SetEncryption(
- (Arc.NewLhd.Flags & LHD_PASSWORD) ? Arc.NewLhd.UnpVer:0,Password,
- (Arc.NewLhd.Flags & LHD_SALT) ? Arc.NewLhd.Salt:NULL,false,
- Arc.NewLhd.UnpVer>=36);
- DataIO.SetPackedSizeToRead(Arc.NewLhd.FullPackSize);
+ DataIO.UnpHash.Init(Arc.FileHead.FileHash.Type,Cmd->Threads);
+ DataIO.PackedDataHash.Init(Arc.FileHead.FileHash.Type,Cmd->Threads);
+ DataIO.SetPackedSizeToRead(Arc.FileHead.PackSize);
DataIO.SetFiles(&Arc,&CurFile);
DataIO.SetTestMode(TestMode);
DataIO.SetSkipUnpCRC(SkipSolid);
-#ifndef _WIN_CE
- if (!TestMode && !Arc.BrokenFileHeader &&
- (Arc.NewLhd.FullPackSize<<11)>Arc.NewLhd.FullUnpSize &&
- (Arc.NewLhd.FullUnpSize<100000000 || Arc.FileLength()>Arc.NewLhd.FullPackSize))
- CurFile.Prealloc(Arc.NewLhd.FullUnpSize);
+
+#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
+ if (!TestMode && !Arc.BrokenHeader &&
+ Arc.FileHead.UnpSize>0xffffffff && (Fat32 || !NotFat32))
+ {
+ if (!Fat32) // Not detected yet.
+ NotFat32=!(Fat32=IsFAT(Cmd->ExtrPath));
+ if (Fat32)
+ uiMsg(UIMSG_FAT32SIZE); // Inform user about FAT32 size limit.
+ }
#endif
+ uint64 Preallocated=0;
+ if (!TestMode && !Arc.BrokenHeader && Arc.FileHead.UnpSize>1000000 &&
+ Arc.FileHead.PackSize*1024>Arc.FileHead.UnpSize && Arc.IsSeekable() &&
+ (Arc.FileHead.UnpSize<100000000 || Arc.FileLength()>Arc.FileHead.PackSize))
+ {
+ CurFile.Prealloc(Arc.FileHead.UnpSize);
+ Preallocated=Arc.FileHead.UnpSize;
+ }
CurFile.SetAllowDelete(!Cmd->KeepBroken);
- bool LinkCreateMode=!Cmd->Test && !SkipSolid;
- if (ExtractLink(DataIO,Arc,DestFileName,DataIO.UnpFileCRC,LinkCreateMode))
- PrevExtracted=LinkCreateMode;
+ bool FileCreateMode=!TestMode && !SkipSolid && Command!='P';
+ bool ShowChecksum=true; // Display checksum verification result.
+
+ bool LinkSuccess=true; // Assume success for test mode.
+ if (LinkEntry)
+ {
+ FILE_SYSTEM_REDIRECT Type=Arc.FileHead.RedirType;
+
+ if (Type==FSREDIR_HARDLINK || Type==FSREDIR_FILECOPY)
+ {
+ wchar RedirName[NM];
+
+ // 2022.11.15: Might be needed when unpacking WinRAR 5.0 links with
+ // Unix RAR. WinRAR 5.0 used \ path separators here, when beginning
+ // from 5.10 even Windows version uses / internally and converts
+ // them to \ when reading FHEXTRA_REDIR.
+ // We must perform this conversion before ConvertPath call,
+ // so paths mixing different slashes like \dir1/dir2\file are
+ // processed correctly.
+ SlashToNative(Arc.FileHead.RedirName,RedirName,ASIZE(RedirName));
+
+ ConvertPath(RedirName,RedirName,ASIZE(RedirName));
+
+ wchar NameExisting[NM];
+ ExtrPrepareName(Arc,RedirName,NameExisting,ASIZE(NameExisting));
+ if (FileCreateMode && *NameExisting!=0) // *NameExisting can be 0 in case of excessive -ap switch.
+ if (Type==FSREDIR_HARDLINK)
+ LinkSuccess=ExtractHardlink(Cmd,DestFileName,NameExisting,ASIZE(NameExisting));
+ else
+ LinkSuccess=ExtractFileCopy(CurFile,Arc.FileName,RedirName,DestFileName,NameExisting,ASIZE(NameExisting),Arc.FileHead.UnpSize);
+ }
+ else
+ if (Type==FSREDIR_UNIXSYMLINK || Type==FSREDIR_WINSYMLINK || Type==FSREDIR_JUNCTION)
+ {
+ if (FileCreateMode)
+ {
+ bool UpLink;
+ LinkSuccess=ExtractSymlink(Cmd,DataIO,Arc,DestFileName,UpLink);
+
+ // Unix symlink can have its own owner data.
+ if (LinkSuccess)
+ SetFileHeaderExtra(Cmd,Arc,DestFileName);
+
+ ConvertSymlinkPaths|=LinkSuccess && UpLink;
+
+ // We do not actually need to reset the cache here if we cache
+ // only the single last checked path, because at this point
+ // it will always contain the link own path and link can't
+ // overwrite its parent folder. But if we ever decide to cache
+ // several already checked paths, we'll need to reset them here.
+ // Otherwise if no files were created in one of such paths,
+ // let's say because of file create error, it might be possible
+ // to overwrite the path with link and avoid checks. We keep this
+ // code here as a reminder in case of possible modifications.
+ LastCheckedSymlink.clear(); // Reset cache for safety reason.
+ }
+ }
+ else
+ {
+ uiMsg(UIERROR_UNKNOWNEXTRA,Arc.FileName,ArcFileName);
+ LinkSuccess=false;
+ }
+
+ if (!LinkSuccess || Arc.Format==RARFMT15 && !FileCreateMode)
+ {
+ // RAR 5.x links have a valid data checksum even in case of
+ // failure, because they do not store any data.
+ // We do not want to display "OK" in this case.
+ // For 4.x symlinks we verify the checksum only when extracting,
+ // but not when testing an archive.
+ ShowChecksum=false;
+ }
+ PrevProcessed=FileCreateMode && LinkSuccess;
+ }
else
- if ((Arc.NewLhd.Flags & LHD_SPLIT_BEFORE)==0)
- if (Arc.NewLhd.Method==0x30)
- UnstoreFile(DataIO,Arc.NewLhd.FullUnpSize);
+ if (!Arc.FileHead.SplitBefore)
+ if (Arc.FileHead.Method==0)
+ UnstoreFile(DataIO,Arc.FileHead.UnpSize);
else
{
- Unp->SetDestSize(Arc.NewLhd.FullUnpSize);
+ Unp->Init(Arc.FileHead.WinSize,Arc.FileHead.Solid);
+ Unp->SetDestSize(Arc.FileHead.UnpSize);
#ifndef SFX_MODULE
- if (Arc.NewLhd.UnpVer<=15)
+ // RAR 1.3 - 1.5 archives do not set per file solid flag.
+ if (Arc.Format!=RARFMT50 && Arc.FileHead.UnpVer<=15)
Unp->DoUnpack(15,FileCount>1 && Arc.Solid);
else
#endif
- Unp->DoUnpack(Arc.NewLhd.UnpVer,Arc.NewLhd.Flags & LHD_SOLID);
+ Unp->DoUnpack(Arc.FileHead.UnpVer,Arc.FileHead.Solid);
}
- if (Arc.IsOpened())
- Arc.SeekToNext();
+ Arc.SeekToNext();
+ // We check for "split after" flag to detect partially extracted files
+ // from incomplete volume sets. For them file header contains packed
+ // data hash, which must not be compared against unpacked data hash
+ // to prevent accidental match. Moreover, for -m0 volumes packed data
+ // hash would match truncated unpacked data hash and lead to fake "OK"
+ // in incomplete volume set.
+ bool ValidCRC=!Arc.FileHead.SplitAfter && DataIO.UnpHash.Cmp(&Arc.FileHead.FileHash,Arc.FileHead.UseHashKey ? Arc.FileHead.HashKey:NULL);
+
+ // We set AnySolidDataUnpackedWell to true if we found at least one
+ // valid non-zero solid file in preceding solid stream. If it is true
+ // and if current encrypted file is broken, we do not need to hint
+ // about a wrong password and can report CRC error only.
+ if (!Arc.FileHead.Solid)
+ AnySolidDataUnpackedWell=false; // Reset the flag, because non-solid file is found.
+ else
+ if (Arc.FileHead.Method!=0 && Arc.FileHead.UnpSize>0 && ValidCRC)
+ AnySolidDataUnpackedWell=true;
+
bool BrokenFile=false;
- if (!SkipSolid)
+
+ // Checksum is not calculated in skip solid mode for performance reason.
+ if (!SkipSolid && ShowChecksum)
{
- if (Arc.OldFormat && UINT32(DataIO.UnpFileCRC)==UINT32(Arc.NewLhd.FileCRC) ||
- !Arc.OldFormat && UINT32(DataIO.UnpFileCRC)==UINT32(Arc.NewLhd.FileCRC^0xffffffff))
+ if (ValidCRC)
{
-#ifndef GUI
- if (Command!='P' && Command!='I')
- mprintf("%s%s ",Cmd->DisablePercentage ? " ":"\b\b\b\b\b ",St(MOk));
-#endif
+ if (Command!='P' && Command!='I' && !Cmd->DisableNames)
+ mprintf(L"%s%s ",Cmd->DisablePercentage ? L" ":L"\b\b\b\b\b ",
+ Arc.FileHead.FileHash.Type==HASH_NONE ? L" ?":St(MOk));
}
else
{
- char *BadArcName=/*(Arc.NewLhd.Flags & LHD_SPLIT_BEFORE) ? NULL:*/Arc.FileName;
- if (Arc.NewLhd.Flags & LHD_PASSWORD)
- {
- Log(BadArcName,St(MEncrBadCRC),ArcFileName);
- }
+ if (Arc.FileHead.Encrypted && (!Arc.FileHead.UsePswCheck ||
+ Arc.BrokenHeader) && !AnySolidDataUnpackedWell)
+ uiMsg(UIERROR_CHECKSUMENC,Arc.FileName,ArcFileName);
else
- {
- Log(BadArcName,St(MCRCFailed),ArcFileName);
- }
+ uiMsg(UIERROR_CHECKSUM,Arc.FileName,ArcFileName);
BrokenFile=true;
- ErrHandler.SetErrorCode(CRC_ERROR);
+ ErrHandler.SetErrorCode(RARX_CRC);
#ifdef RARDLL
- Cmd->DllError=ERAR_BAD_DATA;
+ // If we already have ERAR_EOPEN as result of missing volume
+ // or ERAR_BAD_PASSWORD for RAR5 wrong password,
+ // we should not replace it with less precise ERAR_BAD_DATA.
+ if (Cmd->DllError!=ERAR_EOPEN && Cmd->DllError!=ERAR_BAD_PASSWORD)
+ Cmd->DllError=ERAR_BAD_DATA;
#endif
- Alarm();
}
}
-#ifndef GUI
else
- mprintf("\b\b\b\b\b ");
-#endif
+ {
+ // We check SkipSolid to remove percent for skipped solid files only.
+ // We must not apply these \b to links with ShowChecksum==false
+ // and their possible error messages.
+ if (SkipSolid)
+ mprintf(L"\b\b\b\b\b ");
+ }
+
+ // If we successfully unpacked a hard link, we wish to set its file
+ // attributes. Hard link shares file metadata with link target,
+ // so we do not need to set link time or owner. But when we overwrite
+ // an existing link, we can call PrepareToDelete(), which affects
+ // link target attributes as well. So we set link attributes to restore
+ // both target and link attributes if PrepareToDelete() changed them.
+ bool SetAttrOnly=LinkEntry && Arc.FileHead.RedirType==FSREDIR_HARDLINK && LinkSuccess;
if (!TestMode && (Command=='X' || Command=='E') &&
- !IsLink(Arc.NewLhd.FileAttr))
+ (!LinkEntry || SetAttrOnly || Arc.FileHead.RedirType==FSREDIR_FILECOPY && LinkSuccess) &&
+ (!BrokenFile || Cmd->KeepBroken))
{
-#if defined(_WIN_32) || defined(_EMX)
- if (Cmd->ClearArc)
- Arc.NewLhd.FileAttr&=~FA_ARCH;
-/*
- else
- Arc.NewLhd.FileAttr|=FA_ARCH; //set archive bit for unpacked files (file is not backed up)
-*/
-#endif
- if (!BrokenFile || Cmd->KeepBroken)
+ // Below we use DestFileName instead of CurFile.FileName,
+ // so we can set file attributes also for hard links, which do not
+ // have the open CurFile. These strings are the same for other items.
+
+ if (!SetAttrOnly)
{
- if (BrokenFile)
+ // We could preallocate more space that really written to broken file
+ // or file with crafted header.
+ if (Preallocated>0 && (BrokenFile || DataIO.CurUnpWrite!=Preallocated))
CurFile.Truncate();
- CurFile.SetOpenFileStat(
- Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.NewLhd.mtime,
- Cmd->xctime==EXTTIME_NONE ? NULL:&Arc.NewLhd.ctime,
- Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.NewLhd.atime);
+
+
+ CurFile.SetOpenFileTime(
+ Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime,
+ Cmd->xctime==EXTTIME_NONE ? NULL:&Arc.FileHead.ctime,
+ Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime);
CurFile.Close();
-#if defined(_WIN_32) && !defined(_WIN_CE) && !defined(SFX_MODULE)
- if (Cmd->SetCompressedAttr &&
- (Arc.NewLhd.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0 && WinNT())
- SetFileCompression(CurFile.FileName,CurFile.FileNameW,true);
-#endif
- CurFile.SetCloseFileStat(
- Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.NewLhd.mtime,
- Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.NewLhd.atime,
- Arc.NewLhd.FileAttr);
- PrevExtracted=true;
+
+ SetFileHeaderExtra(Cmd,Arc,DestFileName);
+
+ CurFile.SetCloseFileTime(
+ Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime,
+ Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime);
}
+
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
+ if (Cmd->SetCompressedAttr &&
+ (Arc.FileHead.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0)
+ SetFileCompression(DestFileName,true);
+ if (Cmd->ClearArc)
+ Arc.FileHead.FileAttr&=~FILE_ATTRIBUTE_ARCHIVE;
+#endif
+ if (!Cmd->IgnoreGeneralAttr && !SetFileAttr(DestFileName,Arc.FileHead.FileAttr))
+ {
+ uiMsg(UIERROR_FILEATTR,Arc.FileName,DestFileName);
+ // Android cannot set file attributes and while UIERROR_FILEATTR
+ // above is handled by Android RAR silently, this call would cause
+ // "Operation not permitted" message for every unpacked file.
+ ErrHandler.SysErrMsg();
+ }
+
+ PrevProcessed=true;
}
}
}
- if (ExactMatch)
+ // It is important to increment it for files, but not dirs. So we extract
+ // dir with its entire contents, not just dir record only even if dir
+ // record precedes files.
+ if (MatchFound)
MatchedArgs++;
- if (DataIO.NextVolumeMissing || !Arc.IsOpened())
- return(false);
+ if (DataIO.NextVolumeMissing)
+ return false;
if (!ExtrFile)
if (!Arc.Solid)
Arc.SeekToNext();
else
if (!SkipSolid)
- return(false);
- return(true);
+ return false;
+ return true;
}
-void CmdExtract::UnstoreFile(ComprDataIO &DataIO,Int64 DestUnpSize)
+void CmdExtract::UnstoreFile(ComprDataIO &DataIO,int64 DestUnpSize)
{
- Array<byte> Buffer(0x10000);
- while (1)
+ Array<byte> Buffer(File::CopyBufferSize());
+ while (true)
+ {
+ int ReadSize=DataIO.UnpRead(&Buffer[0],Buffer.Size());
+ if (ReadSize<=0)
+ break;
+ int WriteSize=ReadSize<DestUnpSize ? ReadSize:(int)DestUnpSize;
+ if (WriteSize>0)
+ {
+ DataIO.UnpWrite(&Buffer[0],WriteSize);
+ DestUnpSize-=WriteSize;
+ }
+ }
+}
+
+
+bool CmdExtract::ExtractFileCopy(File &New,wchar *ArcName,const wchar *RedirName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize,int64 UnpSize)
+{
+ File Existing;
+ if (!Existing.Open(NameExisting))
+ {
+ bool OpenFailed=true;
+ // If we couldn't find the existing file, check if match is present
+ // in temporary reference sources list.
+ for (size_t I=0;I<RefList.Size();I++)
+ if (wcscmp(RedirName,RefList[I].RefName)==0 && RefList[I].TmpName!=NULL)
+ {
+ // If only one reference left targeting to this temporary file,
+ // it is faster to move the file instead of copying and deleting it.
+ bool RefMove=RefList[I].RefCount-- == 1;
+ NameExisting=RefList[I].TmpName;
+ if (RefMove) // Only one reference left for this temporary file.
+ {
+ New.Delete(); // Delete the previously opened destination file.
+ // Try moving the file first.
+ bool MoveFailed=!RenameFile(NameExisting,NameNew);
+ if (MoveFailed)
+ {
+ // If move failed, re-create the destination and try coping.
+ if (!New.WCreate(NameNew,FMF_WRITE|FMF_SHAREREAD))
+ return false;
+ RefMove=false; // Try copying below.
+ }
+ else
+ {
+ // If moved successfully, reopen the destination file and seek to
+ // end for SetOpenFileTime() and possible Truncate() calls later.
+ if (New.Open(NameNew))
+ New.Seek(0,SEEK_END);
+ // We already moved the file, so clean the name to not try
+ // deleting non-existent temporary file later.
+ free(RefList[I].TmpName);
+ RefList[I].TmpName=NULL;
+ return true;
+ }
+ }
+ if (!RefMove)
+ OpenFailed=!Existing.Open(NameExisting);
+ break;
+ }
+
+ if (OpenFailed)
+ {
+ ErrHandler.OpenErrorMsg(NameExisting);
+ uiMsg(UIERROR_FILECOPY,ArcName,NameExisting,NameNew);
+ uiMsg(UIERROR_FILECOPYHINT,ArcName);
+#ifdef RARDLL
+ Cmd->DllError=ERAR_EREFERENCE;
+#endif
+ return false;
+ }
+ }
+
+ Array<byte> Buffer(0x100000);
+ int64 CopySize=0;
+
+ while (true)
+ {
+ Wait();
+ int ReadSize=Existing.Read(&Buffer[0],Buffer.Size());
+ if (ReadSize==0)
+ break;
+ // Update only the current file progress in WinRAR, set the total to 0
+ // to keep it as is. It looks better for WinRAR.
+ uiExtractProgress(CopySize,UnpSize,0,0);
+
+ New.Write(&Buffer[0],ReadSize);
+ CopySize+=ReadSize;
+ }
+
+ return true;
+}
+
+
+void CmdExtract::ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *DestName,size_t DestSize)
+{
+ if (Cmd->Test)
+ {
+ // Destination name conversion isn't needed for simple archive test.
+ // This check also allows to avoid issuing "Attempting to correct...
+ // Renaming..." messages in MakeNameCompatible() below for problematic
+ // names like aux.txt when testing an archive.
+ wcsncpyz(DestName,ArcFileName,DestSize);
+ return;
+ }
+
+ wcsncpyz(DestName,Cmd->ExtrPath,DestSize);
+
+ if (*Cmd->ExtrPath!=0)
+ {
+ wchar LastChar=*PointToLastChar(Cmd->ExtrPath);
+ // We need IsPathDiv check here to correctly handle Unix forward slash
+ // in the end of destination path in Windows: rar x arc dest/
+ // so we call IsPathDiv first instead of just calling AddEndSlash,
+ // which checks for only one type of path separator.
+ // IsDriveDiv is needed for current drive dir: rar x arc d:
+ if (!IsPathDiv(LastChar) && !IsDriveDiv(LastChar))
+ {
+ // Destination path can be without trailing slash if it come from GUI shell.
+ AddEndSlash(DestName,DestSize);
+ }
+ }
+
+#ifndef SFX_MODULE
+ if (Cmd->AppendArcNameToPath!=APPENDARCNAME_NONE)
+ {
+ switch(Cmd->AppendArcNameToPath)
+ {
+ case APPENDARCNAME_DESTPATH: // To subdir of destination path.
+ wcsncatz(DestName,PointToName(Arc.FirstVolumeName),DestSize);
+ SetExt(DestName,NULL,DestSize);
+ break;
+ case APPENDARCNAME_OWNSUBDIR: // To subdir of archive own dir.
+ wcsncpyz(DestName,Arc.FirstVolumeName,DestSize);
+ SetExt(DestName,NULL,DestSize);
+ break;
+ case APPENDARCNAME_OWNDIR: // To archive own dir.
+ wcsncpyz(DestName,Arc.FirstVolumeName,DestSize);
+ RemoveNameFromPath(DestName);
+ break;
+ }
+ AddEndSlash(DestName,DestSize);
+ }
+#endif
+
+#ifndef SFX_MODULE
+ wchar *ArcPath=*Cmd->ExclArcPath!=0 ? Cmd->ExclArcPath:Cmd->ArcPath;
+ size_t ArcPathLength=wcslen(ArcPath);
+ if (ArcPathLength>0)
+ {
+ size_t NameLength=wcslen(ArcFileName);
+ if (NameLength>=ArcPathLength && wcsnicompc(ArcPath,ArcFileName,ArcPathLength)==0 &&
+ (IsPathDiv(ArcPath[ArcPathLength-1]) ||
+ IsPathDiv(ArcFileName[ArcPathLength]) || ArcFileName[ArcPathLength]==0))
+ {
+ ArcFileName+=Min(ArcPathLength,NameLength);
+ while (IsPathDiv(*ArcFileName))
+ ArcFileName++;
+ if (*ArcFileName==0) // Excessive -ap switch.
+ {
+ *DestName=0;
+ return;
+ }
+ }
+ }
+#endif
+
+ wchar Command=Cmd->Command[0];
+ // Use -ep3 only in systems, where disk letters are exist, not in Unix.
+ bool AbsPaths=Cmd->ExclPath==EXCL_ABSPATH && Command=='X' && IsDriveDiv(':');
+
+ // We do not use any user specified destination paths when extracting
+ // absolute paths in -ep3 mode.
+ if (AbsPaths)
+ *DestName=0;
+
+ if (Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH)
+ wcsncatz(DestName,PointToName(ArcFileName),DestSize);
+ else
+ wcsncatz(DestName,ArcFileName,DestSize);
+
+#ifdef _WIN_ALL
+ // Must do after Cmd->ArcPath processing above, so file name and arc path
+ // trailing spaces are in sync.
+ if (!Cmd->AllowIncompatNames)
+ MakeNameCompatible(DestName,DestSize);
+#endif
+
+ wchar DiskLetter=toupperw(DestName[0]);
+
+ if (AbsPaths)
+ {
+ if (DestName[1]=='_' && IsPathDiv(DestName[2]) &&
+ DiskLetter>='A' && DiskLetter<='Z')
+ DestName[1]=':';
+ else
+ if (DestName[0]=='_' && DestName[1]=='_')
+ {
+ // Convert __server\share to \\server\share.
+ DestName[0]=CPATHDIVIDER;
+ DestName[1]=CPATHDIVIDER;
+ }
+ }
+}
+
+
+#ifdef RARDLL
+bool CmdExtract::ExtrDllGetPassword()
+{
+ if (!Cmd->Password.IsSet())
+ {
+ if (Cmd->Callback!=NULL)
+ {
+ wchar PasswordW[MAXPASSWORD];
+ *PasswordW=0;
+ if (Cmd->Callback(UCM_NEEDPASSWORDW,Cmd->UserData,(LPARAM)PasswordW,ASIZE(PasswordW))==-1)
+ *PasswordW=0;
+ if (*PasswordW==0)
+ {
+ char PasswordA[MAXPASSWORD];
+ *PasswordA=0;
+ if (Cmd->Callback(UCM_NEEDPASSWORD,Cmd->UserData,(LPARAM)PasswordA,ASIZE(PasswordA))==-1)
+ *PasswordA=0;
+ GetWideName(PasswordA,NULL,PasswordW,ASIZE(PasswordW));
+ cleandata(PasswordA,sizeof(PasswordA));
+ }
+ Cmd->Password.Set(PasswordW);
+ cleandata(PasswordW,sizeof(PasswordW));
+ Cmd->ManualPassword=true;
+ }
+ if (!Cmd->Password.IsSet())
+ return false;
+ }
+ return true;
+}
+#endif
+
+
+#ifndef RARDLL
+bool CmdExtract::ExtrGetPassword(Archive &Arc,const wchar *ArcFileName,RarCheckPassword *CheckPwd)
+{
+ if (!Cmd->Password.IsSet())
+ {
+ if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password,CheckPwd)/* || !Cmd->Password.IsSet()*/)
+ {
+ // Suppress "test is ok" message if user cancelled the password prompt.
+ uiMsg(UIERROR_INCERRCOUNT);
+ return false;
+ }
+ Cmd->ManualPassword=true;
+ }
+#if !defined(SILENT)
+ else
+ if (!GlobalPassword && !Arc.FileHead.Solid)
+ {
+ eprintf(St(MUseCurPsw),ArcFileName);
+ switch(Cmd->AllYes ? 1 : Ask(St(MYesNoAll)))
+ {
+ case -1:
+ ErrHandler.Exit(RARX_USERBREAK);
+ case 2:
+ if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password,CheckPwd))
+ return false;
+ break;
+ case 3:
+ GlobalPassword=true;
+ break;
+ }
+ }
+#endif
+ return true;
+}
+#endif
+
+
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
+void CmdExtract::ConvertDosPassword(Archive &Arc,SecPassword &DestPwd)
+{
+ if (Arc.Format==RARFMT15 && Arc.FileHead.HostOS==HOST_MSDOS)
{
- unsigned int Code=DataIO.UnpRead(&Buffer[0],Buffer.Size());
- if (Code==0 || (int)Code==-1)
+ // We need the password in OEM encoding if file was encrypted by
+ // native RAR/DOS (not extender based). Let's make the conversion.
+ wchar PlainPsw[MAXPASSWORD];
+ Cmd->Password.Get(PlainPsw,ASIZE(PlainPsw));
+ char PswA[MAXPASSWORD];
+ CharToOemBuffW(PlainPsw,PswA,ASIZE(PswA));
+ PswA[ASIZE(PswA)-1]=0;
+ CharToWide(PswA,PlainPsw,ASIZE(PlainPsw));
+ DestPwd.Set(PlainPsw);
+ cleandata(PlainPsw,sizeof(PlainPsw));
+ cleandata(PswA,sizeof(PswA));
+ }
+}
+#endif
+
+
+void CmdExtract::ExtrCreateDir(Archive &Arc,const wchar *ArcFileName)
+{
+ if (Cmd->Test)
+ {
+ if (!Cmd->DisableNames)
+ {
+ mprintf(St(MExtrTestFile),ArcFileName);
+ mprintf(L" %s",St(MOk));
+ }
+ return;
+ }
+
+ MKDIR_CODE MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr);
+ bool DirExist=false;
+ if (MDCode!=MKDIR_SUCCESS)
+ {
+ DirExist=FileExist(DestFileName);
+ if (DirExist && !IsDir(GetFileAttr(DestFileName)))
+ {
+ // File with name same as this directory exists. Propose user
+ // to overwrite it.
+ bool UserReject;
+ FileCreate(Cmd,NULL,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime);
+ DirExist=false;
+ }
+ if (!DirExist)
+ {
+ CreatePath(DestFileName,true,Cmd->DisableNames);
+ MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr);
+ if (MDCode!=MKDIR_SUCCESS && !IsNameUsable(DestFileName))
+ {
+ uiMsg(UIMSG_CORRECTINGNAME,Arc.FileName);
+ wchar OrigName[ASIZE(DestFileName)];
+ wcsncpyz(OrigName,DestFileName,ASIZE(OrigName));
+ MakeNameUsable(DestFileName,true);
+#ifndef SFX_MODULE
+ uiMsg(UIERROR_RENAMING,Arc.FileName,OrigName,DestFileName);
+#endif
+ DirExist=FileExist(DestFileName) && IsDir(GetFileAttr(DestFileName));
+ if (!DirExist)
+ {
+ if (!Cmd->AbsoluteLinks && ConvertSymlinkPaths)
+ LinksToDirs(DestFileName,Cmd->ExtrPath,LastCheckedSymlink);
+ CreatePath(DestFileName,true,Cmd->DisableNames);
+ MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr);
+ }
+ }
+ }
+ }
+ if (MDCode==MKDIR_SUCCESS)
+ {
+ if (!Cmd->DisableNames)
+ {
+ mprintf(St(MCreatDir),DestFileName);
+ mprintf(L" %s",St(MOk));
+ }
+ PrevProcessed=true;
+ }
+ else
+ if (DirExist)
+ {
+ if (!Cmd->IgnoreGeneralAttr)
+ SetFileAttr(DestFileName,Arc.FileHead.FileAttr);
+ PrevProcessed=true;
+ }
+ else
+ {
+ uiMsg(UIERROR_DIRCREATE,Arc.FileName,DestFileName);
+ ErrHandler.SysErrMsg();
+#ifdef RARDLL
+ Cmd->DllError=ERAR_ECREATE;
+#endif
+ ErrHandler.SetErrorCode(RARX_CREATE);
+ }
+ if (PrevProcessed)
+ {
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
+ if (Cmd->SetCompressedAttr &&
+ (Arc.FileHead.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0 && WinNT()!=WNT_NONE)
+ SetFileCompression(DestFileName,true);
+#endif
+ SetFileHeaderExtra(Cmd,Arc,DestFileName);
+ SetDirTime(DestFileName,
+ Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime,
+ Cmd->xctime==EXTTIME_NONE ? NULL:&Arc.FileHead.ctime,
+ Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime);
+ }
+}
+
+
+bool CmdExtract::ExtrCreateFile(Archive &Arc,File &CurFile)
+{
+ bool Success=true;
+ wchar Command=Cmd->Command[0];
+#if !defined(SFX_MODULE)
+ if (Command=='P')
+ CurFile.SetHandleType(FILE_HANDLESTD);
+#endif
+ if ((Command=='E' || Command=='X') && !Cmd->Test)
+ {
+ bool UserReject;
+ // Specify "write only" mode to avoid OpenIndiana NAS problems
+ // with SetFileTime and read+write files.
+ if (!FileCreate(Cmd,&CurFile,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime,true))
+ {
+ Success=false;
+ if (!UserReject)
+ {
+ ErrHandler.CreateErrorMsg(Arc.FileName,DestFileName);
+ if (FileExist(DestFileName) && IsDir(GetFileAttr(DestFileName)))
+ uiMsg(UIERROR_DIRNAMEEXISTS);
+
+#ifdef RARDLL
+ Cmd->DllError=ERAR_ECREATE;
+#endif
+ if (!IsNameUsable(DestFileName))
+ {
+ uiMsg(UIMSG_CORRECTINGNAME,Arc.FileName);
+
+ wchar OrigName[ASIZE(DestFileName)];
+ wcsncpyz(OrigName,DestFileName,ASIZE(OrigName));
+
+ MakeNameUsable(DestFileName,true);
+
+ if (!Cmd->AbsoluteLinks && ConvertSymlinkPaths)
+ LinksToDirs(DestFileName,Cmd->ExtrPath,LastCheckedSymlink);
+ CreatePath(DestFileName,true,Cmd->DisableNames);
+ if (FileCreate(Cmd,&CurFile,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime,true))
+ {
+#ifndef SFX_MODULE
+ uiMsg(UIERROR_RENAMING,Arc.FileName,OrigName,DestFileName);
+#endif
+ Success=true;
+ }
+ else
+ ErrHandler.CreateErrorMsg(Arc.FileName,DestFileName);
+ }
+ }
+ }
+ }
+ return Success;
+}
+
+
+bool CmdExtract::CheckUnpVer(Archive &Arc,const wchar *ArcFileName)
+{
+ bool WrongVer;
+ if (Arc.Format==RARFMT50) // Both SFX and RAR can unpack RAR 5.0 archives.
+ WrongVer=Arc.FileHead.UnpVer>VER_UNPACK5;
+ else
+ {
+#ifdef SFX_MODULE // SFX can unpack only RAR 2.9 archives.
+ WrongVer=Arc.FileHead.UnpVer!=VER_UNPACK;
+#else // All formats since 1.3 for RAR.
+ WrongVer=Arc.FileHead.UnpVer<13 || Arc.FileHead.UnpVer>VER_UNPACK;
+#endif
+ }
+
+ // We can unpack stored files regardless of compression version field.
+ if (Arc.FileHead.Method==0)
+ WrongVer=false;
+
+ if (WrongVer)
+ {
+ ErrHandler.UnknownMethodMsg(Arc.FileName,ArcFileName);
+ uiMsg(UIERROR_NEWERRAR,Arc.FileName);
+ }
+ return !WrongVer;
+}
+
+
+#ifndef SFX_MODULE
+// Find non-matched reference sources in solid and non-solid archives.
+// Detect the optimal start position for semi-solid archives
+// and optimal start volume for independent solid volumes.
+//
+// Alternatively we could collect references while extracting an archive
+// and perform the second extraction pass for references only.
+// But it would be slower for solid archives than scaning headers
+// in first pass and extracting everything in second, as implemented now.
+//
+void CmdExtract::AnalyzeArchive(const wchar *ArcName,bool Volume,bool NewNumbering)
+{
+ FreeAnalyzeData(); // If processing non-first archive in multiple archives set.
+
+ wchar *ArgName=Cmd->FileArgs.GetString();
+ Cmd->FileArgs.Rewind();
+ if (ArgName!=NULL && (wcscmp(ArgName,L"*")==0 || wcscmp(ArgName,L"*.*")==0))
+ return; // No need to check further for * and *.* masks.
+
+ // Start search from first volume if all volumes preceding current are available.
+ wchar NextName[NM];
+ if (Volume)
+ GetFirstVolIfFullSet(ArcName,NewNumbering,NextName,ASIZE(NextName));
+ else
+ wcsncpyz(NextName,ArcName,ASIZE(NextName));
+
+ bool MatchFound=false;
+ bool PrevMatched=false;
+ bool OpenNext=false;
+
+ bool FirstVolume=true;
+
+ // We shall set FirstFile once for all volumes and not for each volume.
+ // So we do not reuse the outdated Analyze->StartPos from previous volume
+ // if extracted file resides completely in the beginning of current one.
+ bool FirstFile=true;
+
+ while (true)
+ {
+ Archive Arc(Cmd);
+ if (!Arc.Open(NextName) || !Arc.IsArchive(false))
+ {
+ if (OpenNext)
+ {
+ // If we couldn't open trailing volumes, we can't set early exit
+ // parameters. It is possible that some volume are on removable media
+ // and will be provided by user when extracting.
+ *Analyze->EndName=0;
+ Analyze->EndPos=0;
+ }
+ break;
+ }
+
+ OpenNext=false;
+ while (Arc.ReadHeader()>0)
+ {
+ Wait();
+
+ HEADER_TYPE HeaderType=Arc.GetHeaderType();
+ if (HeaderType==HEAD_ENDARC)
+ {
+ OpenNext|=Arc.EndArcHead.NextVolume; // Allow open next volume.
+ break;
+ }
+ if (HeaderType==HEAD_FILE)
+ {
+ if ((Arc.Format==RARFMT14 || Arc.Format==RARFMT15) && Arc.FileHead.UnpVer<=15)
+ {
+ // RAR versions earlier than 2.0 do not set per file solid flag.
+ // They have only the global archive solid flag, so we can't
+ // reliably analyze them here.
+ OpenNext=false;
+ break;
+ }
+
+ if (!Arc.FileHead.SplitBefore)
+ {
+ if (!MatchFound && !Arc.FileHead.Solid) // Can start extraction from here.
+ {
+ // We would gain nothing and unnecessarily complicate extraction
+ // if we set StartName for first volume or StartPos for first
+ // archived file.
+ if (!FirstVolume)
+ wcsncpyz(Analyze->StartName,NextName,ASIZE(Analyze->StartName));
+
+ // We shall set FirstFile once for all volumes for this code
+ // to work properly. Alternatively we could append
+ // "|| Analyze->StartPos!=0" to the condition, so we do not reuse
+ // the outdated Analyze->StartPos value from previous volume.
+ if (!FirstFile)
+ Analyze->StartPos=Arc.CurBlockPos;
+ }
+
+ if (Cmd->IsProcessFile(Arc.FileHead,NULL,MATCH_WILDSUBPATH,0,NULL,0)!=0)
+ {
+ MatchFound = true;
+ PrevMatched = true;
+
+ // Reset the previously set early exit position, if any, because
+ // we found a new matched file.
+ Analyze->EndPos=0;
+
+ // Matched file reference pointing at maybe non-matched source file.
+ // Even though we know RedirName, we can't check if source file
+ // is certainly non-matched, because it can be filtered out by
+ // date or attributes, which we do not know here.
+ if (Arc.FileHead.RedirType==FSREDIR_FILECOPY)
+ {
+ bool AlreadyAdded=false;
+ for (size_t I=0;I<RefList.Size();I++)
+ if (wcscmp(Arc.FileHead.RedirName,RefList[I].RefName)==0)
+ {
+ // Increment the reference count if we added such reference
+ // source earlier.
+ RefList[I].RefCount++;
+ AlreadyAdded=true;
+ break;
+ }
+
+ // Limit the maximum size of reference sources list to some
+ // sensible value to prevent the excessive memory allocation.
+ size_t MaxListSize=1000000;
+
+ if (!AlreadyAdded && RefList.Size()<MaxListSize)
+ {
+ ExtractRef Ref={0};
+ Ref.RefName=wcsdup(Arc.FileHead.RedirName);
+ Ref.RefCount=1;
+ RefList.Push(Ref);
+ }
+ }
+ }
+ else
+ {
+ if (PrevMatched) // First non-matched item after matched.
+ {
+ // We would perform the unnecessarily string comparison
+ // when extracting if we set this value for first volume
+ // or non-volume archive.
+ if (!FirstVolume)
+ wcsncpyz(Analyze->EndName,NextName,ASIZE(Analyze->EndName));
+ Analyze->EndPos=Arc.CurBlockPos;
+ }
+ PrevMatched=false;
+ }
+ }
+
+ FirstFile=false;
+ if (Arc.FileHead.SplitAfter)
+ {
+ OpenNext=true; // Allow open next volume.
+ break;
+ }
+ }
+ Arc.SeekToNext();
+ }
+ Arc.Close();
+
+ if (Volume && OpenNext)
+ {
+ NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering);
+ FirstVolume=false;
+
+ // Needed for multivolume archives. Added in case some 'break'
+ // will quit early from loop above, so we do not set it in the loop.
+ // Now it can happen for hypothetical archive without file records
+ // and with HEAD_ENDARC record.
+ FirstFile=false;
+ }
+ else
+ break;
+ }
+
+ // If file references are present, we can't reliably skip in semi-solid
+ // archives, because reference source can be present in skipped data.
+ if (RefList.Size()!=0)
+ memset(Analyze,0,sizeof(*Analyze));
+}
+#endif
+
+
+#ifndef SFX_MODULE
+// Return the first volume name if all volumes preceding the specified
+// are available. Otherwise return the specified volume name.
+void CmdExtract::GetFirstVolIfFullSet(const wchar *SrcName,bool NewNumbering,wchar *DestName,size_t DestSize)
+{
+ wchar FirstVolName[NM];
+ VolNameToFirstName(SrcName,FirstVolName,ASIZE(FirstVolName),NewNumbering);
+ wchar NextName[NM];
+ wcsncpyz(NextName,FirstVolName,ASIZE(NextName));
+ wchar ResultName[NM];
+ wcsncpyz(ResultName,SrcName,ASIZE(ResultName));
+ while (true)
+ {
+ if (wcscmp(SrcName,NextName)==0)
+ {
+ wcsncpyz(ResultName,FirstVolName,DestSize);
+ break;
+ }
+ if (!FileExist(NextName))
break;
- Code=Code<DestUnpSize ? Code:int64to32(DestUnpSize);
- DataIO.UnpWrite(&Buffer[0],Code);
- if (DestUnpSize>=0)
- DestUnpSize-=Code;
+ NextVolumeName(NextName,ASIZE(NextName),!NewNumbering);
}
+ wcsncpyz(DestName,ResultName,DestSize);
}
+#endif \ No newline at end of file
diff --git a/unrar/unrar/extract.hpp b/unrar/unrar/extract.hpp
index dcf4534..18396c5 100644
--- a/unrar/unrar/extract.hpp
+++ b/unrar/unrar/extract.hpp
@@ -6,38 +6,90 @@ enum EXTRACT_ARC_CODE {EXTRACT_ARC_NEXT,EXTRACT_ARC_REPEAT};
class CmdExtract
{
private:
- EXTRACT_ARC_CODE ExtractArchive(CommandData *Cmd);
- RarTime StartTime; // time when extraction started
+ struct ExtractRef
+ {
+ wchar *RefName;
+ wchar *TmpName;
+ uint64 RefCount;
+ };
+ Array<ExtractRef> RefList;
+
+ struct AnalyzeData
+ {
+ wchar StartName[NM];
+ uint64 StartPos;
+ wchar EndName[NM];
+ uint64 EndPos;
+ } *Analyze;
+
+ bool ArcAnalyzed;
+
+ void FreeAnalyzeData();
+ EXTRACT_ARC_CODE ExtractArchive();
+ bool ExtractFileCopy(File &New,wchar *ArcName,const wchar *RedirName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize,int64 UnpSize);
+ void ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *DestName,size_t DestSize);
+#ifdef RARDLL
+ bool ExtrDllGetPassword();
+#else
+ bool ExtrGetPassword(Archive &Arc,const wchar *ArcFileName,RarCheckPassword *CheckPwd);
+#endif
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
+ void ConvertDosPassword(Archive &Arc,SecPassword &DestPwd);
+#endif
+ void ExtrCreateDir(Archive &Arc,const wchar *ArcFileName);
+ bool ExtrCreateFile(Archive &Arc,File &CurFile);
+ bool CheckUnpVer(Archive &Arc,const wchar *ArcFileName);
+#ifndef SFX_MODULE
+ void AnalyzeArchive(const wchar *ArcName,bool Volume,bool NewNumbering);
+ void GetFirstVolIfFullSet(const wchar *SrcName,bool NewNumbering,wchar *DestName,size_t DestSize);
+#endif
+
+ RarTime StartTime; // Time when extraction started.
+
+ CommandData *Cmd;
ComprDataIO DataIO;
Unpack *Unp;
- long TotalFileCount;
+ unsigned long TotalFileCount;
- long FileCount;
- long MatchedArgs;
+ unsigned long FileCount;
+ unsigned long MatchedArgs;
bool FirstFile;
bool AllMatchesExact;
bool ReconstructDone;
+ bool UseExactVolName;
+
+ // If any non-zero solid file was successfully unpacked before current.
+ // If true and if current encrypted file is broken, obviously
+ // the password is correct and we can report broken CRC without
+ // any wrong password hints.
+ bool AnySolidDataUnpackedWell;
- char ArcName[NM];
- wchar ArcNameW[NM];
+ wchar ArcName[NM];
- char Password[MAXPASSWORD];
- bool PasswordAll;
- bool PrevExtracted;
- char DestFileName[NM];
- wchar DestFileNameW[NM];
+ bool GlobalPassword;
+ bool PrevProcessed; // If previous file was successfully extracted or tested.
+ wchar DestFileName[NM];
bool PasswordCancelled;
+
+ // In Windows it is set to true if at least one symlink with ".."
+ // in target was extracted.
+ bool ConvertSymlinkPaths;
+
+ // Last path checked for symlinks. We use it to improve the performance,
+ // so we do not check recently checked folders again.
+ std::wstring LastCheckedSymlink;
+
+#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
+ bool Fat32,NotFat32;
+#endif
public:
- CmdExtract();
+ CmdExtract(CommandData *Cmd);
~CmdExtract();
- void DoExtract(CommandData *Cmd);
- void ExtractArchiveInit(CommandData *Cmd,Archive &Arc);
- bool ExtractCurrentFile(CommandData *Cmd,Archive &Arc,int HeaderSize,
- bool &Repeat);
- static void UnstoreFile(ComprDataIO &DataIO,Int64 DestUnpSize);
-
- bool SignatureFound;
+ void DoExtract();
+ void ExtractArchiveInit(Archive &Arc);
+ bool ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat);
+ static void UnstoreFile(ComprDataIO &DataIO,int64 DestUnpSize);
};
#endif
diff --git a/unrar/unrar/filcreat.cpp b/unrar/unrar/filcreat.cpp
index c377dd6..d58e4f6 100644
--- a/unrar/unrar/filcreat.cpp
+++ b/unrar/unrar/filcreat.cpp
@@ -1,239 +1,165 @@
#include "rar.hpp"
-bool FileCreate(RAROptions *Cmd,File *NewFile,char *Name,wchar *NameW,
- OVERWRITE_MODE Mode,bool *UserReject,Int64 FileSize,
- uint FileTime)
+// If NewFile==NULL, we delete created file after user confirmation.
+// It is useful if we need to overwrite an existing folder or file,
+// but need user confirmation for that.
+bool FileCreate(CommandData *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize,
+ bool *UserReject,int64 FileSize,RarTime *FileTime,bool WriteOnly)
{
if (UserReject!=NULL)
*UserReject=false;
-#if defined(_WIN_32) && !defined(_WIN_CE)
+#ifdef _WIN_ALL
bool ShortNameChanged=false;
#endif
- while (FileExist(Name,NameW))
+ while (FileExist(Name))
{
-#if defined(_WIN_32) && !defined(_WIN_CE)
+#if defined(_WIN_ALL)
if (!ShortNameChanged)
{
+ // Avoid the infinite loop if UpdateExistingShortName returns
+ // the same name.
ShortNameChanged=true;
- if (UpdateExistingShortName(Name,NameW))
+
+ // Maybe our long name matches the short name of existing file.
+ // Let's check if we can change the short name.
+ if (UpdateExistingShortName(Name))
continue;
}
+ // Allow short name check again. It is necessary, because rename and
+ // autorename below can change the name, so we need to check it again.
+ ShortNameChanged=false;
#endif
- if (Mode==OVERWRITE_NONE)
+ UIASKREP_RESULT Choice=uiAskReplaceEx(Cmd,Name,MaxNameSize,FileSize,FileTime,(NewFile==NULL ? UIASKREP_F_NORENAME:0));
+
+ if (Choice==UIASKREP_R_REPLACE)
+ break;
+ if (Choice==UIASKREP_R_SKIP)
{
if (UserReject!=NULL)
*UserReject=true;
- return(false);
- }
-#ifdef SILENT
- Mode=OVERWRITE_ALL;
-#endif
- if (Cmd->AllYes || Mode==OVERWRITE_ALL)
- break;
- if (Mode==OVERWRITE_DEFAULT || Mode==OVERWRITE_FORCE_ASK)
- {
- eprintf(St(MFileExists),Name);
- int Choice=Ask(St(MYesNoAllRenQ));
- if (Choice==1)
- break;
- if (Choice==2)
- {
- if (UserReject!=NULL)
- *UserReject=true;
- return(false);
- }
- if (Choice==3)
- {
- Cmd->Overwrite=OVERWRITE_ALL;
- break;
- }
- if (Choice==4)
- {
- if (UserReject!=NULL)
- *UserReject=true;
- Cmd->Overwrite=OVERWRITE_NONE;
- return(false);
- }
- if (Choice==5)
- {
- mprintf(St(MAskNewName));
-
- char NewName[NM];
-#ifdef _WIN_32
- File SrcFile;
- SrcFile.SetHandleType(FILE_HANDLESTD);
- int Size=SrcFile.Read(NewName,sizeof(NewName)-1);
- NewName[Size]=0;
- OemToChar(NewName,NewName);
-#else
- fgets(NewName,sizeof(NewName),stdin);
-#endif
- RemoveLF(NewName);
- if (PointToName(NewName)==NewName)
- strcpy(PointToName(Name),NewName);
- else
- strcpy(Name,NewName);
- if (NameW!=NULL)
- *NameW=0;
- continue;
- }
- if (Choice==6)
- ErrHandler.Exit(USER_BREAK);
- }
- if (Mode==OVERWRITE_AUTORENAME)
- {
- if (GetAutoRenamedName(Name))
- {
- if (NameW!=NULL)
- *NameW=0;
- }
- else
- Mode=OVERWRITE_DEFAULT;
- continue;
+ return false;
}
+ if (Choice==UIASKREP_R_CANCEL)
+ ErrHandler.Exit(RARX_USERBREAK);
}
- if (NewFile!=NULL && NewFile->Create(Name,NameW))
- return(true);
- PrepareToDelete(Name,NameW);
- CreatePath(Name,NameW,true);
- return(NewFile!=NULL ? NewFile->Create(Name,NameW):DelFile(Name,NameW));
+
+ // Try to truncate the existing file first instead of delete,
+ // so we preserve existing file permissions, such as NTFS permissions,
+ // also as "Compressed" attribute and hard links. In GUI version we avoid
+ // deleting an existing file for non-.rar archive formats as well.
+ uint FileMode=WriteOnly ? FMF_WRITE|FMF_SHAREREAD:FMF_UPDATE|FMF_SHAREREAD;
+ if (NewFile!=NULL && NewFile->Create(Name,FileMode))
+ return true;
+
+ CreatePath(Name,true,Cmd->DisableNames);
+ return NewFile!=NULL ? NewFile->Create(Name,FileMode):DelFile(Name);
}
-bool GetAutoRenamedName(char *Name)
+bool GetAutoRenamedName(wchar *Name,size_t MaxNameSize)
{
- char NewName[NM];
-
- if (strlen(Name)>sizeof(NewName)-10)
- return(false);
- char *Ext=GetExt(Name);
+ wchar NewName[NM];
+ size_t NameLength=wcslen(Name);
+ wchar *Ext=GetExt(Name);
if (Ext==NULL)
- Ext=Name+strlen(Name);
- for (int FileVer=1;;FileVer++)
+ Ext=Name+NameLength;
+ for (uint FileVer=1;;FileVer++)
{
- sprintf(NewName,"%.*s(%d)%s",Ext-Name,Name,FileVer,Ext);
+ swprintf(NewName,ASIZE(NewName),L"%.*ls(%u)%ls",uint(Ext-Name),Name,FileVer,Ext);
if (!FileExist(NewName))
{
- strcpy(Name,NewName);
+ wcsncpyz(Name,NewName,MaxNameSize);
break;
}
if (FileVer>=1000000)
- return(false);
+ return false;
}
- return(true);
+ return true;
}
-#if defined(_WIN_32) && !defined(_WIN_CE)
-bool UpdateExistingShortName(char *Name,wchar *NameW)
+#if defined(_WIN_ALL)
+// If we find a file, which short name is equal to 'Name', we try to change
+// its short name, while preserving the long name. It helps when unpacking
+// an archived file, which long name is equal to short name of already
+// existing file. Otherwise we would overwrite the already existing file,
+// even though its long name does not match the name of unpacking file.
+bool UpdateExistingShortName(const wchar *Name)
{
- FindData fd;
- if (!FindFile::FastFind(Name,NameW,&fd))
- return(false);
- if (*fd.Name==0 || *fd.ShortName==0)
- return(false);
- if (stricomp(PointToName(fd.Name),fd.ShortName)==0 ||
- stricomp(PointToName(Name),fd.ShortName)!=0)
- return(false);
-
- char NewName[NM];
- for (int I=0;I<10000;I+=123)
+ wchar LongPathName[NM];
+ DWORD Res=GetLongPathName(Name,LongPathName,ASIZE(LongPathName));
+ if (Res==0 || Res>=ASIZE(LongPathName))
+ return false;
+ wchar ShortPathName[NM];
+ Res=GetShortPathName(Name,ShortPathName,ASIZE(ShortPathName));
+ if (Res==0 || Res>=ASIZE(ShortPathName))
+ return false;
+ wchar *LongName=PointToName(LongPathName);
+ wchar *ShortName=PointToName(ShortPathName);
+
+ // We continue only if file has a short name, which does not match its
+ // long name, and this short name is equal to name of file which we need
+ // to create.
+ if (*ShortName==0 || wcsicomp(LongName,ShortName)==0 ||
+ wcsicomp(PointToName(Name),ShortName)!=0)
+ return false;
+
+ // Generate the temporary new name for existing file.
+ wchar NewName[NM];
+ *NewName=0;
+ for (int I=0;I<10000 && *NewName==0;I+=123)
{
- strncpyz(NewName,Name,ASIZE(NewName));
- sprintf(PointToName(NewName),"rtmp%d",I);
- if (!FileExist(NewName))
- break;
+ // Here we copy the path part of file to create. We'll make the temporary
+ // file in the same folder.
+ wcsncpyz(NewName,Name,ASIZE(NewName));
+
+ // Here we set the random name part.
+ swprintf(PointToName(NewName),ASIZE(NewName),L"rtmp%d",I);
+
+ // If such file is already exist, try next random name.
+ if (FileExist(NewName))
+ *NewName=0;
}
- if (FileExist(NewName))
- return(false);
- char FullName[NM];
- strncpyz(FullName,Name,ASIZE(FullName));
- strcpy(PointToName(FullName),PointToName(fd.Name));
+
+ // If we could not generate the name not used by any other file, we return.
+ if (*NewName==0)
+ return false;
+
+ // FastFind returns the name without path, but we need the fully qualified
+ // name for renaming, so we use the path from file to create and long name
+ // from existing file.
+ wchar FullName[NM];
+ wcsncpyz(FullName,Name,ASIZE(FullName));
+ SetName(FullName,LongName,ASIZE(FullName));
+
+ // Rename the existing file to randomly generated name. Normally it changes
+ // the short name too.
if (!MoveFile(FullName,NewName))
- return(false);
+ return false;
+
+ // Now we need to create the temporary empty file with same name as
+ // short name of our already existing file. We do it to occupy its previous
+ // short name and not allow to use it again when renaming the file back to
+ // its original long name.
File KeepShortFile;
bool Created=false;
if (!FileExist(Name))
- Created=KeepShortFile.Create(Name);
+ Created=KeepShortFile.Create(Name,FMF_WRITE|FMF_SHAREREAD);
+
+ // Now we rename the existing file from temporary name to original long name.
+ // Since its previous short name is occupied by another file, it should
+ // get another short name.
MoveFile(NewName,FullName);
+
if (Created)
{
+ // Delete the temporary zero length file occupying the short name,
KeepShortFile.Close();
KeepShortFile.Delete();
}
- return(true);
-}
-
-/*
-bool UpdateExistingShortName(char *Name,wchar *NameW)
-{
- if (WinNT()<5)
- return(false);
- FindData fd;
- if (!FindFile::FastFind(Name,NameW,&fd))
- return(false);
- if (*fd.Name==0 || *fd.ShortName==0)
- return(false);
- if (stricomp(PointToName(fd.Name),fd.ShortName)==0 ||
- stricomp(PointToName(Name),fd.ShortName)!=0)
- return(false);
-
- typedef BOOL (WINAPI *SETFILESHORTNAME)(HANDLE,LPCSTR);
- static SETFILESHORTNAME pSetFileShortName=NULL;
- if (pSetFileShortName==NULL)
- {
- HMODULE hKernel=GetModuleHandle("kernel32.dll");
- if (hKernel!=NULL)
- pSetFileShortName=(SETFILESHORTNAME)GetProcAddress(hKernel,"SetFileShortNameA");
- if (pSetFileShortName==NULL)
- return(false);
- }
- static bool RestoreEnabled=false;
- if (!RestoreEnabled)
- {
- HANDLE hToken;
- if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
- return(false);
-
- TOKEN_PRIVILEGES tp;
- tp.PrivilegeCount = 1;
- tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
-
- if (LookupPrivilegeValue(NULL,SE_RESTORE_NAME,&tp.Privileges[0].Luid))
- AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL);
-
- CloseHandle(hToken);
- RestoreEnabled=true;
- }
-
- wchar FileNameW[NM];
- GetWideName(Name,NameW,FileNameW);
- HANDLE hFile=CreateFileW(FileNameW,GENERIC_WRITE|DELETE,FILE_SHARE_READ|FILE_SHARE_WRITE,
- NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL);
- if (hFile==INVALID_HANDLE_VALUE)
- return(false);
-
- bool RetCode=false;
-
- char FullName[NM];
- wchar FullNameW[NM];
- strcpy(FullName,Name);
- strcpyw(FullNameW,NullToEmpty(NameW));
- for (int I=1;I<1000000;I++)
- {
- char NewName[NM];
- sprintf(NewName,"NAME~%d.%d",I%1000,I/1000+1);
- strcpy(PointToName(FullName),NewName);
- if (*FullNameW)
- CharToWide(NewName,PointToName(FullNameW));
- if (!FileExist(FullName,FullNameW))
- {
- RetCode=pSetFileShortName(hFile,NewName);
- break;
- }
- }
- CloseHandle(hFile);
- return(RetCode);
+ // We successfully changed the short name. Maybe sometimes we'll simplify
+ // this function by use of SetFileShortName Windows API call.
+ // But SetFileShortName is not available in older Windows.
+ return true;
}
-*/
#endif
diff --git a/unrar/unrar/filcreat.hpp b/unrar/unrar/filcreat.hpp
index d803582..456a4a4 100644
--- a/unrar/unrar/filcreat.hpp
+++ b/unrar/unrar/filcreat.hpp
@@ -1,13 +1,14 @@
#ifndef _RAR_FILECREATE_
#define _RAR_FILECREATE_
-bool FileCreate(RAROptions *Cmd,File *NewFile,char *Name,wchar *NameW,
- OVERWRITE_MODE Mode,bool *UserReject,Int64 FileSize=INT64ERR,
- uint FileTime=0);
-bool GetAutoRenamedName(char *Name);
+bool FileCreate(CommandData *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize,
+ bool *UserReject,int64 FileSize=INT64NDF,
+ RarTime *FileTime=NULL,bool WriteOnly=false);
-#if defined(_WIN_32) && !defined(_WIN_CE)
-bool UpdateExistingShortName(char *Name,wchar *NameW);
+bool GetAutoRenamedName(wchar *Name,size_t MaxNameSize);
+
+#if defined(_WIN_ALL)
+bool UpdateExistingShortName(const wchar *Name);
#endif
#endif
diff --git a/unrar/unrar/file.cpp b/unrar/unrar/file.cpp
index 8fe71ec..7bf60fd 100644
--- a/unrar/unrar/file.cpp
+++ b/unrar/unrar/file.cpp
@@ -1,32 +1,31 @@
#include "rar.hpp"
-static File *CreatedFiles[256];
-static int RemoveCreatedActive=0;
-
File::File()
{
- hFile=BAD_HANDLE;
+ hFile=FILE_BAD_HANDLE;
*FileName=0;
- *FileNameW=0;
NewFile=false;
LastWrite=false;
HandleType=FILE_HANDLENORMAL;
+ LineInput=false;
SkipClose=false;
- IgnoreReadErrors=false;
ErrorType=FILE_SUCCESS;
OpenShared=false;
AllowDelete=true;
- CloseCount=0;
AllowExceptions=true;
-#ifdef _WIN_32
- NoSequentialRead=false;
+ PreserveAtime=false;
+#ifdef _WIN_ALL
+ CreateMode=FMF_UNDEFINED;
#endif
+ ReadErrorMode=FREM_ASK;
+ TruncatedAfterReadError=false;
+ CurFilePos=0;
}
File::~File()
{
- if (hFile!=BAD_HANDLE && !SkipClose)
+ if (hFile!=FILE_BAD_HANDLE && !SkipClose)
if (NewFile)
Delete();
else
@@ -37,294 +36,326 @@ File::~File()
void File::operator = (File &SrcFile)
{
hFile=SrcFile.hFile;
- strcpy(FileName,SrcFile.FileName);
NewFile=SrcFile.NewFile;
LastWrite=SrcFile.LastWrite;
HandleType=SrcFile.HandleType;
+ TruncatedAfterReadError=SrcFile.TruncatedAfterReadError;
+ wcsncpyz(FileName,SrcFile.FileName,ASIZE(FileName));
SrcFile.SkipClose=true;
}
-bool File::Open(const char *Name,const wchar *NameW,bool OpenShared,bool Update)
+bool File::Open(const wchar *Name,uint Mode)
{
ErrorType=FILE_SUCCESS;
FileHandle hNewFile;
- if (File::OpenShared)
- OpenShared=true;
-#ifdef _WIN_32
- uint Access=GENERIC_READ;
- if (Update)
+ bool OpenShared=File::OpenShared || (Mode & FMF_OPENSHARED)!=0;
+ bool UpdateMode=(Mode & FMF_UPDATE)!=0;
+ bool WriteMode=(Mode & FMF_WRITE)!=0;
+#ifdef _WIN_ALL
+ uint Access=WriteMode ? GENERIC_WRITE:GENERIC_READ;
+ if (UpdateMode)
Access|=GENERIC_WRITE;
- uint ShareMode=FILE_SHARE_READ;
+ uint ShareMode=(Mode & FMF_OPENEXCLUSIVE) ? 0 : FILE_SHARE_READ;
if (OpenShared)
ShareMode|=FILE_SHARE_WRITE;
- uint Flags=NoSequentialRead ? 0:FILE_FLAG_SEQUENTIAL_SCAN;
- if (WinNT() && NameW!=NULL && *NameW!=0)
- hNewFile=CreateFileW(NameW,Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL);
- else
- hNewFile=CreateFile(Name,Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL);
+ uint Flags=FILE_FLAG_SEQUENTIAL_SCAN;
+ FindData FD;
+ if (PreserveAtime)
+ Access|=FILE_WRITE_ATTRIBUTES; // Needed to preserve atime.
+ hNewFile=CreateFile(Name,Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL);
+
+ DWORD LastError;
+ if (hNewFile==FILE_BAD_HANDLE)
+ {
+ LastError=GetLastError();
- if (hNewFile==BAD_HANDLE && GetLastError()==ERROR_FILE_NOT_FOUND)
+ wchar LongName[NM];
+ if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
+ {
+ hNewFile=CreateFile(LongName,Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL);
+
+ // For archive names longer than 260 characters first CreateFile
+ // (without \\?\) fails and sets LastError to 3 (access denied).
+ // We need the correct "file not found" error code to decide
+ // if we create a new archive or quit with "cannot create" error.
+ // So we need to check the error code after \\?\ CreateFile again,
+ // otherwise we'll fail to create new archives with long names.
+ // But we cannot simply assign the new code to LastError,
+ // because it would break "..\arcname.rar" relative names processing.
+ // First CreateFile returns the correct "file not found" code for such
+ // names, but "\\?\" CreateFile returns ERROR_INVALID_NAME treating
+ // dots as a directory name. So we check only for "file not found"
+ // error here and for other errors use the first CreateFile result.
+ if (GetLastError()==ERROR_FILE_NOT_FOUND)
+ LastError=ERROR_FILE_NOT_FOUND;
+ }
+ }
+ if (hNewFile==FILE_BAD_HANDLE && LastError==ERROR_FILE_NOT_FOUND)
ErrorType=FILE_NOTFOUND;
+ if (PreserveAtime && hNewFile!=FILE_BAD_HANDLE)
+ {
+ FILETIME ft={0xffffffff,0xffffffff}; // This value prevents atime modification.
+ SetFileTime(hNewFile,NULL,&ft,NULL);
+ }
+
#else
- int flags=Update ? O_RDWR:O_RDONLY;
+ int flags=UpdateMode ? O_RDWR:(WriteMode ? O_WRONLY:O_RDONLY);
#ifdef O_BINARY
flags|=O_BINARY;
#if defined(_AIX) && defined(_LARGE_FILE_API)
flags|=O_LARGEFILE;
#endif
#endif
-#if defined(_EMX) && !defined(_DJGPP)
- int sflags=OpenShared ? SH_DENYNO:SH_DENYWR;
- int handle=sopen(Name,flags,sflags);
-#else
- int handle=open(Name,flags);
+ // NDK r20 has O_NOATIME, but fails to create files with it in Android 7+.
+#if defined(O_NOATIME)
+ if (PreserveAtime)
+ flags|=O_NOATIME;
+#endif
+ char NameA[NM];
+ WideToChar(Name,NameA,ASIZE(NameA));
+
+ int handle=open(NameA,flags);
#ifdef LOCK_EX
#ifdef _OSF_SOURCE
extern "C" int flock(int, int);
#endif
-
- if (!OpenShared && Update && handle>=0 && flock(handle,LOCK_EX|LOCK_NB)==-1)
+ if (!OpenShared && UpdateMode && handle>=0 && flock(handle,LOCK_EX|LOCK_NB)==-1)
{
close(handle);
- return(false);
+ return false;
}
+
#endif
+ if (handle==-1)
+ hNewFile=FILE_BAD_HANDLE;
+ else
+ {
+#ifdef FILE_USE_OPEN
+ hNewFile=handle;
+#else
+ hNewFile=fdopen(handle,UpdateMode ? UPDATEBINARY:READBINARY);
#endif
- hNewFile=handle==-1 ? BAD_HANDLE:fdopen(handle,Update ? UPDATEBINARY:READBINARY);
- if (hNewFile==BAD_HANDLE && errno==ENOENT)
+ }
+ if (hNewFile==FILE_BAD_HANDLE && errno==ENOENT)
ErrorType=FILE_NOTFOUND;
#endif
NewFile=false;
HandleType=FILE_HANDLENORMAL;
SkipClose=false;
- bool Success=hNewFile!=BAD_HANDLE;
+ bool Success=hNewFile!=FILE_BAD_HANDLE;
if (Success)
{
hFile=hNewFile;
- if (NameW!=NULL)
- strcpyw(FileNameW,NameW);
- else
- *FileNameW=0;
- if (Name!=NULL)
- strcpy(FileName,Name);
- else
- WideToChar(NameW,FileName);
- AddFileToList(hFile);
+ wcsncpyz(FileName,Name,ASIZE(FileName));
+ TruncatedAfterReadError=false;
}
- return(Success);
+ return Success;
}
-#if !defined(SHELL_EXT) && !defined(SFX_MODULE)
-void File::TOpen(const char *Name,const wchar *NameW)
+#if !defined(SFX_MODULE)
+void File::TOpen(const wchar *Name)
{
- if (!WOpen(Name,NameW))
- ErrHandler.Exit(OPEN_ERROR);
+ if (!WOpen(Name))
+ ErrHandler.Exit(RARX_OPEN);
}
#endif
-bool File::WOpen(const char *Name,const wchar *NameW)
+bool File::WOpen(const wchar *Name)
{
- if (Open(Name,NameW))
- return(true);
+ if (Open(Name))
+ return true;
ErrHandler.OpenErrorMsg(Name);
- return(false);
-}
+ return false;
+}
+
+
+bool File::Create(const wchar *Name,uint Mode)
+{
+ // OpenIndiana based NAS and CIFS shares fail to set the file time if file
+ // was created in read+write mode and some data was written and not flushed
+ // before SetFileTime call. So we should use the write only mode if we plan
+ // SetFileTime call and do not need to read from file.
+ bool WriteMode=(Mode & FMF_WRITE)!=0;
+ bool ShareRead=(Mode & FMF_SHAREREAD)!=0 || File::OpenShared;
+#ifdef _WIN_ALL
+ CreateMode=Mode;
+ uint Access=WriteMode ? GENERIC_WRITE:GENERIC_READ|GENERIC_WRITE;
+ DWORD ShareMode=ShareRead ? FILE_SHARE_READ:0;
+
+ // Windows automatically removes dots and spaces in the end of file name,
+ // So we detect such names and process them with \\?\ prefix.
+ wchar *LastChar=PointToLastChar(Name);
+ bool Special=*LastChar=='.' || *LastChar==' ';
+
+ if (Special && (Mode & FMF_STANDARDNAMES)==0)
+ hFile=FILE_BAD_HANDLE;
+ else
+ hFile=CreateFile(Name,Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL);
+ if (hFile==FILE_BAD_HANDLE)
+ {
+ wchar LongName[NM];
+ if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
+ hFile=CreateFile(LongName,Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL);
+ }
-bool File::Create(const char *Name,const wchar *NameW,bool ShareRead)
-{
-#ifdef _WIN_32
- DWORD ShareMode=(ShareRead || File::OpenShared) ? FILE_SHARE_READ:0;
- if (WinNT() && NameW!=NULL && *NameW!=0)
- hFile=CreateFileW(NameW,GENERIC_READ|GENERIC_WRITE,ShareMode,NULL,
- CREATE_ALWAYS,0,NULL);
- else
- hFile=CreateFile(Name,GENERIC_READ|GENERIC_WRITE,ShareMode,NULL,
- CREATE_ALWAYS,0,NULL);
#else
- hFile=fopen(Name,CREATEBINARY);
+ char NameA[NM];
+ WideToChar(Name,NameA,ASIZE(NameA));
+#ifdef FILE_USE_OPEN
+ hFile=open(NameA,(O_CREAT|O_TRUNC) | (WriteMode ? O_WRONLY : O_RDWR),0666);
+#else
+ hFile=fopen(NameA,WriteMode ? WRITEBINARY:CREATEBINARY);
+#endif
#endif
NewFile=true;
HandleType=FILE_HANDLENORMAL;
SkipClose=false;
- if (NameW!=NULL)
- strcpyw(FileNameW,NameW);
- else
- *FileNameW=0;
- if (Name!=NULL)
- strcpy(FileName,Name);
- else
- WideToChar(NameW,FileName);
- AddFileToList(hFile);
- return(hFile!=BAD_HANDLE);
+ wcsncpyz(FileName,Name,ASIZE(FileName));
+ return hFile!=FILE_BAD_HANDLE;
}
-void File::AddFileToList(FileHandle hFile)
+#if !defined(SFX_MODULE)
+void File::TCreate(const wchar *Name,uint Mode)
{
- if (hFile!=BAD_HANDLE)
- for (int I=0;I<sizeof(CreatedFiles)/sizeof(CreatedFiles[0]);I++)
- if (CreatedFiles[I]==NULL)
- {
- CreatedFiles[I]=this;
- break;
- }
-}
-
-
-#if !defined(SHELL_EXT) && !defined(SFX_MODULE)
-void File::TCreate(const char *Name,const wchar *NameW,bool ShareRead)
-{
- if (!WCreate(Name,NameW,ShareRead))
- ErrHandler.Exit(FATAL_ERROR);
+ if (!WCreate(Name,Mode))
+ ErrHandler.Exit(RARX_FATAL);
}
#endif
-bool File::WCreate(const char *Name,const wchar *NameW,bool ShareRead)
+bool File::WCreate(const wchar *Name,uint Mode)
{
- if (Create(Name,NameW,ShareRead))
- return(true);
- ErrHandler.SetErrorCode(CREATE_ERROR);
+ if (Create(Name,Mode))
+ return true;
ErrHandler.CreateErrorMsg(Name);
- return(false);
+ return false;
}
bool File::Close()
{
bool Success=true;
- if (HandleType!=FILE_HANDLENORMAL)
- HandleType=FILE_HANDLENORMAL;
- else
- if (hFile!=BAD_HANDLE)
+
+ if (hFile!=FILE_BAD_HANDLE)
+ {
+ if (!SkipClose)
{
- if (!SkipClose)
- {
-#ifdef _WIN_32
- Success=CloseHandle(hFile);
+#ifdef _WIN_ALL
+ // We use the standard system handle for stdout in Windows
+ // and it must not be closed here.
+ if (HandleType==FILE_HANDLENORMAL)
+ Success=CloseHandle(hFile)==TRUE;
#else
- Success=fclose(hFile)!=EOF;
-#endif
- if (Success || !RemoveCreatedActive)
- for (int I=0;I<sizeof(CreatedFiles)/sizeof(CreatedFiles[0]);I++)
- if (CreatedFiles[I]==this)
- {
- CreatedFiles[I]=NULL;
- break;
- }
- }
- hFile=BAD_HANDLE;
- if (!Success && AllowExceptions)
- ErrHandler.CloseError(FileName);
- }
- CloseCount++;
- return(Success);
-}
-
-
-void File::Flush()
-{
-#ifdef _WIN_32
- FlushFileBuffers(hFile);
+#ifdef FILE_USE_OPEN
+ Success=close(hFile)!=-1;
#else
- fflush(hFile);
+ Success=fclose(hFile)!=EOF;
#endif
+#endif
+ }
+ hFile=FILE_BAD_HANDLE;
+ }
+ HandleType=FILE_HANDLENORMAL;
+ if (!Success && AllowExceptions)
+ ErrHandler.CloseError(FileName);
+ return Success;
}
bool File::Delete()
{
if (HandleType!=FILE_HANDLENORMAL)
- return(false);
- if (hFile!=BAD_HANDLE)
+ return false;
+ if (hFile!=FILE_BAD_HANDLE)
Close();
if (!AllowDelete)
- return(false);
- return(DelFile(FileName,FileNameW));
+ return false;
+ return DelFile(FileName);
}
-bool File::Rename(const char *NewName,const wchar *NewNameW)
+bool File::Rename(const wchar *NewName)
{
- // we do not need to rename if names are already same
- bool Success=strcmp(FileName,NewName)==0;
- if (Success && *FileNameW!=0 && *NullToEmpty(NewNameW)!=0)
- Success=strcmpw(FileNameW,NewNameW)==0;
+ // No need to rename if names are already same.
+ bool Success=wcscmp(FileName,NewName)==0;
if (!Success)
- Success=RenameFile(FileName,FileNameW,NewName,NewNameW);
+ Success=RenameFile(FileName,NewName);
if (Success)
- {
- // renamed successfully, storing the new name
- strcpy(FileName,NewName);
- strcpyw(FileNameW,NullToEmpty(NewNameW));
- }
- return(Success);
+ wcsncpyz(FileName,NewName,ASIZE(FileName));
+
+ return Success;
}
-void File::Write(const void *Data,int Size)
+bool File::Write(const void *Data,size_t Size)
{
if (Size==0)
- return;
-#ifndef _WIN_CE
- if (HandleType!=FILE_HANDLENORMAL)
- switch(HandleType)
- {
- case FILE_HANDLESTD:
-#ifdef _WIN_32
- hFile=GetStdHandle(STD_OUTPUT_HANDLE);
+ return true;
+ if (HandleType==FILE_HANDLESTD)
+ {
+#ifdef _WIN_ALL
+ hFile=GetStdHandle(STD_OUTPUT_HANDLE);
#else
- hFile=stdout;
-#endif
- break;
- case FILE_HANDLEERR:
-#ifdef _WIN_32
- hFile=GetStdHandle(STD_ERROR_HANDLE);
+ // Cannot use the standard stdout here, because it already has wide orientation.
+ if (hFile==FILE_BAD_HANDLE)
+ {
+#ifdef FILE_USE_OPEN
+ hFile=dup(STDOUT_FILENO); // Open new stdout stream.
#else
- hFile=stderr;
+ hFile=fdopen(dup(STDOUT_FILENO),"w"); // Open new stdout stream.
#endif
- break;
}
#endif
+ }
+ bool Success;
while (1)
{
- bool Success=false;
-#ifdef _WIN_32
+ Success=false;
+#ifdef _WIN_ALL
DWORD Written=0;
if (HandleType!=FILE_HANDLENORMAL)
{
// writing to stdout can fail in old Windows if data block is too large
- const int MaxSize=0x4000;
- for (int I=0;I<Size;I+=MaxSize)
- if (!(Success=WriteFile(hFile,(byte *)Data+I,Min(Size-I,MaxSize),&Written,NULL)))
+ const size_t MaxSize=0x4000;
+ for (size_t I=0;I<Size;I+=MaxSize)
+ {
+ Success=WriteFile(hFile,(byte *)Data+I,(DWORD)Min(Size-I,MaxSize),&Written,NULL)==TRUE;
+ if (!Success)
break;
+ }
}
else
- Success=WriteFile(hFile,Data,Size,&Written,NULL);
+ Success=WriteFile(hFile,Data,(DWORD)Size,&Written,NULL)==TRUE;
+#else
+#ifdef FILE_USE_OPEN
+ ssize_t Written=write(hFile,Data,Size);
+ Success=Written==Size;
#else
int Written=fwrite(Data,1,Size,hFile);
Success=Written==Size && !ferror(hFile);
#endif
+#endif
if (!Success && AllowExceptions && HandleType==FILE_HANDLENORMAL)
{
-#if defined(_WIN_32) && !defined(SFX_MODULE) && !defined(RARDLL)
+#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(RARDLL)
int ErrCode=GetLastError();
- Int64 FilePos=Tell();
- Int64 FreeSize=GetFreeDisk(FileName);
+ int64 FilePos=Tell();
+ uint64 FreeSize=GetFreeDisk(FileName);
SetLastError(ErrCode);
if (FreeSize>Size && FilePos-Size<=0xffffffff && FilePos+Size>0xffffffff)
ErrHandler.WriteErrorFAT(FileName);
#endif
- if (ErrHandler.AskRepeatWrite(FileName))
+ if (ErrHandler.AskRepeatWrite(FileName,false))
{
-#ifndef _WIN_32
+#if !defined(_WIN_ALL) && !defined(FILE_USE_OPEN)
clearerr(hFile);
#endif
if (Written<Size && Written>0)
@@ -336,75 +367,134 @@ void File::Write(const void *Data,int Size)
break;
}
LastWrite=true;
+ return Success; // It can return false only if AllowExceptions is disabled.
}
-int File::Read(void *Data,int Size)
+int File::Read(void *Data,size_t Size)
{
- Int64 FilePos=0; //initialized only to suppress some compilers warning
+ if (TruncatedAfterReadError)
+ return 0;
- if (IgnoreReadErrors)
+ int64 FilePos=0; // Initialized only to suppress some compilers warning.
+
+ if (ReadErrorMode==FREM_IGNORE)
FilePos=Tell();
- int ReadSize;
+ int TotalRead=0;
while (true)
{
- ReadSize=DirectRead(Data,Size);
+ int ReadSize=DirectRead(Data,Size);
+
if (ReadSize==-1)
{
ErrorType=FILE_READERROR;
if (AllowExceptions)
- if (IgnoreReadErrors)
+ if (ReadErrorMode==FREM_IGNORE)
{
ReadSize=0;
- for (int I=0;I<Size;I+=512)
+ for (size_t I=0;I<Size;I+=512)
{
Seek(FilePos+I,SEEK_SET);
- int SizeToRead=Min(Size-I,512);
+ size_t SizeToRead=Min(Size-I,512);
int ReadCode=DirectRead(Data,SizeToRead);
ReadSize+=(ReadCode==-1) ? 512:ReadCode;
+ if (ReadSize!=-1)
+ TotalRead+=ReadSize;
}
}
else
{
- if (HandleType==FILE_HANDLENORMAL && ErrHandler.AskRepeatRead(FileName))
- continue;
+ bool Ignore=false,Retry=false,Quit=false;
+ if (ReadErrorMode==FREM_ASK && HandleType==FILE_HANDLENORMAL)
+ {
+ ErrHandler.AskRepeatRead(FileName,Ignore,Retry,Quit);
+ if (Retry)
+ continue;
+ }
+ if (Ignore || ReadErrorMode==FREM_TRUNCATE)
+ {
+ TruncatedAfterReadError=true;
+ return 0;
+ }
ErrHandler.ReadError(FileName);
}
}
+ TotalRead+=ReadSize; // If ReadSize is -1, TotalRead is also set to -1 here.
+
+ if (HandleType==FILE_HANDLESTD && !LineInput && ReadSize>0 && (uint)ReadSize<Size)
+ {
+ // Unlike regular files, for pipe we can read only as much as was
+ // written at the other end of pipe. We had seen data coming in small
+ // ~80 byte chunks when piping from 'type arc.rar'. Extraction code
+ // would fail if we read an incomplete archive header from stdin.
+ // So here we ensure that requested size is completely read.
+ // But we return the available data immediately in "line input" mode,
+ // when processing user's input in console prompts. Otherwise apps
+ // piping user responses to multiple Ask() prompts can hang if no more
+ // data is available yet and pipe isn't closed.
+ Data=(byte*)Data+ReadSize;
+ Size-=ReadSize;
+ continue;
+ }
break;
}
- return(ReadSize);
+ if (TotalRead>0) // Can be -1 for error and AllowExceptions disabled.
+ CurFilePos+=TotalRead;
+ return TotalRead; // It can return -1 only if AllowExceptions is disabled.
}
-int File::DirectRead(void *Data,int Size)
+// Returns -1 in case of error.
+int File::DirectRead(void *Data,size_t Size)
{
-#ifdef _WIN_32
- const int MaxDeviceRead=20000;
+#ifdef _WIN_ALL
+ const size_t MaxDeviceRead=20000;
+ const size_t MaxLockedRead=32768;
#endif
-#ifndef _WIN_CE
if (HandleType==FILE_HANDLESTD)
{
-#ifdef _WIN_32
- if (Size>MaxDeviceRead)
- Size=MaxDeviceRead;
+#ifdef _WIN_ALL
+// if (Size>MaxDeviceRead)
+// Size=MaxDeviceRead;
hFile=GetStdHandle(STD_INPUT_HANDLE);
#else
+#ifdef FILE_USE_OPEN
+ hFile=STDIN_FILENO;
+#else
hFile=stdin;
#endif
- }
#endif
-#ifdef _WIN_32
+ }
+#ifdef _WIN_ALL
+ // For pipes like 'type file.txt | rar -si arcname' ReadFile may return
+ // data in small ~4KB blocks. It may slightly reduce the compression ratio.
DWORD Read;
- if (!ReadFile(hFile,Data,Size,&Read,NULL))
+ if (!ReadFile(hFile,Data,(DWORD)Size,&Read,NULL))
{
if (IsDevice() && Size>MaxDeviceRead)
- return(DirectRead(Data,MaxDeviceRead));
+ return DirectRead(Data,MaxDeviceRead);
if (HandleType==FILE_HANDLESTD && GetLastError()==ERROR_BROKEN_PIPE)
- return(0);
- return(-1);
+ return 0;
+
+ // We had a bug report about failure to archive 1C database lock file
+ // 1Cv8tmp.1CL, which is a zero length file with a region above 200 KB
+ // permanently locked. If our first read request uses too large buffer
+ // and if we are in -dh mode, so we were able to open the file,
+ // we'll fail with "Read error". So now we use try a smaller buffer size
+ // in case of lock error.
+ if (HandleType==FILE_HANDLENORMAL && Size>MaxLockedRead &&
+ GetLastError()==ERROR_LOCK_VIOLATION)
+ return DirectRead(Data,MaxLockedRead);
+
+ return -1;
}
- return(Read);
+ return Read;
+#else
+#ifdef FILE_USE_OPEN
+ ssize_t ReadSize=read(hFile,Data,Size);
+ if (ReadSize==-1)
+ return -1;
+ return (int)ReadSize;
#else
if (LastWrite)
{
@@ -412,78 +502,130 @@ int File::DirectRead(void *Data,int Size)
LastWrite=false;
}
clearerr(hFile);
- int ReadSize=fread(Data,1,Size,hFile);
+ size_t ReadSize=fread(Data,1,Size,hFile);
if (ferror(hFile))
- return(-1);
- return(ReadSize);
+ return -1;
+ return (int)ReadSize;
+#endif
#endif
}
-void File::Seek(Int64 Offset,int Method)
+void File::Seek(int64 Offset,int Method)
{
if (!RawSeek(Offset,Method) && AllowExceptions)
ErrHandler.SeekError(FileName);
}
-bool File::RawSeek(Int64 Offset,int Method)
+bool File::RawSeek(int64 Offset,int Method)
{
- if (hFile==BAD_HANDLE)
- return(true);
- if (!is64plus(Offset) && Method!=SEEK_SET)
+ if (hFile==FILE_BAD_HANDLE)
+ return true;
+ if (!IsSeekable()) // To extract archives from stdin with -si.
+ {
+ // We tried to dynamically allocate 32 KB buffer here, but it improved
+ // speed in Windows 10 by mere ~1.5%.
+ byte Buf[4096];
+ if (Method==SEEK_CUR || Method==SEEK_SET && Offset>=CurFilePos)
+ {
+ uint64 SkipSize=Method==SEEK_CUR ? Offset:Offset-CurFilePos;
+ while (SkipSize>0) // Reading to emulate seek forward.
+ {
+ int ReadSize=Read(Buf,(size_t)Min(SkipSize,ASIZE(Buf)));
+ if (ReadSize<=0)
+ return false;
+ SkipSize-=ReadSize;
+ CurFilePos+=ReadSize;
+ }
+ return true;
+ }
+ // May need it in FileLength() in Archive::UnexpEndArcMsg() when unpacking
+ // RAR 4.x archives without the end of archive block created with -en.
+ if (Method==SEEK_END)
+ {
+ int ReadSize;
+ while ((ReadSize=Read(Buf,ASIZE(Buf)))>0)
+ CurFilePos+=ReadSize;
+ return true;
+ }
+
+ return false; // Backward seek on unseekable file.
+ }
+ if (Offset<0 && Method!=SEEK_SET)
{
Offset=(Method==SEEK_CUR ? Tell():FileLength())+Offset;
Method=SEEK_SET;
}
-#ifdef _WIN_32
- LONG HighDist=int64to32(Offset>>32);
- if (SetFilePointer(hFile,int64to32(Offset),&HighDist,Method)==0xffffffff &&
+#ifdef _WIN_ALL
+ LONG HighDist=(LONG)(Offset>>32);
+ if (SetFilePointer(hFile,(LONG)Offset,&HighDist,Method)==0xffffffff &&
GetLastError()!=NO_ERROR)
- return(false);
+ return false;
#else
LastWrite=false;
-#if defined(_LARGEFILE_SOURCE) && !defined(_OSF_SOURCE) && !defined(__VMS)
+#ifdef FILE_USE_OPEN
+ if (lseek(hFile,(off_t)Offset,Method)==-1)
+ return false;
+#elif defined(_LARGEFILE_SOURCE) && !defined(_OSF_SOURCE) && !defined(__VMS)
if (fseeko(hFile,Offset,Method)!=0)
+ return false;
#else
- if (fseek(hFile,(long)int64to32(Offset),Method)!=0)
+ if (fseek(hFile,(long)Offset,Method)!=0)
+ return false;
#endif
- return(false);
#endif
- return(true);
+ return true;
}
-Int64 File::Tell()
+int64 File::Tell()
{
-#ifdef _WIN_32
+ if (hFile==FILE_BAD_HANDLE)
+ if (AllowExceptions)
+ ErrHandler.SeekError(FileName);
+ else
+ return -1;
+ if (!IsSeekable())
+ return CurFilePos;
+#ifdef _WIN_ALL
LONG HighDist=0;
uint LowDist=SetFilePointer(hFile,0,&HighDist,FILE_CURRENT);
if (LowDist==0xffffffff && GetLastError()!=NO_ERROR)
if (AllowExceptions)
ErrHandler.SeekError(FileName);
else
- return(-1);
- return(int32to64(HighDist,LowDist));
+ return -1;
+ return INT32TO64(HighDist,LowDist);
#else
-#if defined(_LARGEFILE_SOURCE) && !defined(_OSF_SOURCE)
- return(ftello(hFile));
+#ifdef FILE_USE_OPEN
+ return lseek(hFile,0,SEEK_CUR);
+#elif defined(_LARGEFILE_SOURCE) && !defined(_OSF_SOURCE)
+ return ftello(hFile);
#else
- return(ftell(hFile));
+ return ftell(hFile);
#endif
#endif
}
-void File::Prealloc(Int64 Size)
+void File::Prealloc(int64 Size)
{
-#ifdef _WIN_32
+#ifdef _WIN_ALL
if (RawSeek(Size,SEEK_SET))
{
Truncate();
Seek(0,SEEK_SET);
}
#endif
+
+#if defined(_UNIX) && defined(USE_FALLOCATE)
+ // fallocate is rather new call. Only latest kernels support it.
+ // So we are not using it by default yet.
+ int fd = GetFD();
+ if (fd >= 0)
+ fallocate(fd, 0, 0, Size);
+#endif
}
@@ -491,7 +633,7 @@ byte File::GetByte()
{
byte Byte=0;
Read(&Byte,1);
- return(Byte);
+ return Byte;
}
@@ -503,27 +645,46 @@ void File::PutByte(byte Byte)
bool File::Truncate()
{
-#ifdef _WIN_32
- return(SetEndOfFile(hFile));
+#ifdef _WIN_ALL
+ return SetEndOfFile(hFile)==TRUE;
+#else
+ return ftruncate(GetFD(),(off_t)Tell())==0;
+#endif
+}
+
+
+void File::Flush()
+{
+#ifdef _WIN_ALL
+ FlushFileBuffers(hFile);
#else
- return(false);
+#ifndef FILE_USE_OPEN
+ fflush(hFile);
+#endif
+ fsync(GetFD());
#endif
}
void File::SetOpenFileTime(RarTime *ftm,RarTime *ftc,RarTime *fta)
{
-#ifdef _WIN_32
+#ifdef _WIN_ALL
+ // Workaround for OpenIndiana NAS time bug. If we cannot create a file
+ // in write only mode, we need to flush the write buffer before calling
+ // SetFileTime or file time will not be changed.
+ if (CreateMode!=FMF_UNDEFINED && (CreateMode & FMF_WRITE)==0)
+ FlushFileBuffers(hFile);
+
bool sm=ftm!=NULL && ftm->IsSet();
bool sc=ftc!=NULL && ftc->IsSet();
bool sa=fta!=NULL && fta->IsSet();
FILETIME fm,fc,fa;
if (sm)
- ftm->GetWin32(&fm);
+ ftm->GetWinFT(&fm);
if (sc)
- ftc->GetWin32(&fc);
+ ftc->GetWinFT(&fc);
if (sa)
- fta->GetWin32(&fa);
+ fta->GetWinFT(&fa);
SetFileTime(hFile,sc ? &fc:NULL,sa ? &fa:NULL,sm ? &fm:NULL);
#endif
}
@@ -531,168 +692,148 @@ void File::SetOpenFileTime(RarTime *ftm,RarTime *ftc,RarTime *fta)
void File::SetCloseFileTime(RarTime *ftm,RarTime *fta)
{
-#if defined(_UNIX) || defined(_EMX)
+// Android APP_PLATFORM := android-14 does not support futimens and futimes.
+// Newer platforms support futimens, but fail on Android 4.2.
+// We have to use utime for Android.
+// Also we noticed futimens fail to set timestamps on NTFS partition
+// mounted to virtual Linux x86 machine, but utimensat worked correctly.
+// So we set timestamps for already closed files in Unix.
+#ifdef _UNIX
SetCloseFileTimeByName(FileName,ftm,fta);
#endif
}
-void File::SetCloseFileTimeByName(const char *Name,RarTime *ftm,RarTime *fta)
+void File::SetCloseFileTimeByName(const wchar *Name,RarTime *ftm,RarTime *fta)
{
-#if defined(_UNIX) || defined(_EMX)
+#ifdef _UNIX
bool setm=ftm!=NULL && ftm->IsSet();
bool seta=fta!=NULL && fta->IsSet();
if (setm || seta)
{
- struct utimbuf ut;
+ char NameA[NM];
+ WideToChar(Name,NameA,ASIZE(NameA));
+
+#ifdef UNIX_TIME_NS
+ timespec times[2];
+ times[0].tv_sec=seta ? fta->GetUnix() : 0;
+ times[0].tv_nsec=seta ? long(fta->GetUnixNS()%1000000000) : UTIME_NOW;
+ times[1].tv_sec=setm ? ftm->GetUnix() : 0;
+ times[1].tv_nsec=setm ? long(ftm->GetUnixNS()%1000000000) : UTIME_NOW;
+ utimensat(AT_FDCWD,NameA,times,0);
+#else
+ utimbuf ut;
if (setm)
ut.modtime=ftm->GetUnix();
else
- ut.modtime=fta->GetUnix();
+ ut.modtime=fta->GetUnix(); // Need to set something, cannot left it 0.
if (seta)
ut.actime=fta->GetUnix();
else
- ut.actime=ut.modtime;
- utime(Name,&ut);
+ ut.actime=ut.modtime; // Need to set something, cannot left it 0.
+ utime(NameA,&ut);
+#endif
}
#endif
}
-void File::GetOpenFileTime(RarTime *ft)
+#ifdef _UNIX
+void File::StatToRarTime(struct stat &st,RarTime *ftm,RarTime *ftc,RarTime *fta)
{
-#ifdef _WIN_32
- FILETIME FileTime;
- GetFileTime(hFile,NULL,NULL,&FileTime);
- *ft=FileTime;
+#ifdef UNIX_TIME_NS
+#if defined(_APPLE)
+ if (ftm!=NULL) ftm->SetUnixNS(st.st_mtimespec.tv_sec*(uint64)1000000000+st.st_mtimespec.tv_nsec);
+ if (ftc!=NULL) ftc->SetUnixNS(st.st_ctimespec.tv_sec*(uint64)1000000000+st.st_ctimespec.tv_nsec);
+ if (fta!=NULL) fta->SetUnixNS(st.st_atimespec.tv_sec*(uint64)1000000000+st.st_atimespec.tv_nsec);
+#else
+ if (ftm!=NULL) ftm->SetUnixNS(st.st_mtim.tv_sec*(uint64)1000000000+st.st_mtim.tv_nsec);
+ if (ftc!=NULL) ftc->SetUnixNS(st.st_ctim.tv_sec*(uint64)1000000000+st.st_ctim.tv_nsec);
+ if (fta!=NULL) fta->SetUnixNS(st.st_atim.tv_sec*(uint64)1000000000+st.st_atim.tv_nsec);
#endif
-#if defined(_UNIX) || defined(_EMX)
- struct stat st;
- fstat(fileno(hFile),&st);
- *ft=st.st_mtime;
+#else
+ if (ftm!=NULL) ftm->SetUnix(st.st_mtime);
+ if (ftc!=NULL) ftc->SetUnix(st.st_ctime);
+ if (fta!=NULL) fta->SetUnix(st.st_atime);
#endif
}
-
-
-void File::SetOpenFileStat(RarTime *ftm,RarTime *ftc,RarTime *fta)
-{
-#ifdef _WIN_32
- SetOpenFileTime(ftm,ftc,fta);
#endif
-}
-void File::SetCloseFileStat(RarTime *ftm,RarTime *fta,uint FileAttr)
+void File::GetOpenFileTime(RarTime *ftm,RarTime *ftc,RarTime *fta)
{
-#ifdef _WIN_32
- SetFileAttr(FileName,FileNameW,FileAttr);
-#endif
-#ifdef _EMX
- SetCloseFileTime(ftm,fta);
- SetFileAttr(FileName,FileNameW,FileAttr);
-#endif
-#ifdef _UNIX
- SetCloseFileTime(ftm,fta);
- chmod(FileName,(mode_t)FileAttr);
+#ifdef _WIN_ALL
+ FILETIME ctime,atime,mtime;
+ GetFileTime(hFile,&ctime,&atime,&mtime);
+ if (ftm!=NULL) ftm->SetWinFT(&mtime);
+ if (ftc!=NULL) ftc->SetWinFT(&ctime);
+ if (fta!=NULL) fta->SetWinFT(&atime);
+#elif defined(_UNIX)
+ struct stat st;
+ fstat(GetFD(),&st);
+ StatToRarTime(st,ftm,ftc,fta);
#endif
}
-Int64 File::FileLength()
+int64 File::FileLength()
{
- SaveFilePos SavePos(*this);
+ int64 SavePos=Tell();
Seek(0,SEEK_END);
- return(Tell());
-}
-
-
-void File::SetHandleType(FILE_HANDLETYPE Type)
-{
- HandleType=Type;
+ int64 Length=Tell();
+ Seek(SavePos,SEEK_SET);
+ return Length;
}
bool File::IsDevice()
{
- if (hFile==BAD_HANDLE)
- return(false);
-#ifdef _WIN_32
+ if (hFile==FILE_BAD_HANDLE)
+ return false;
+#ifdef _WIN_ALL
uint Type=GetFileType(hFile);
- return(Type==FILE_TYPE_CHAR || Type==FILE_TYPE_PIPE);
-#else
- return(isatty(fileno(hFile)));
-#endif
-}
-
-
-#ifndef SFX_MODULE
-void File::fprintf(const char *fmt,...)
-{
- va_list argptr;
- va_start(argptr,fmt);
- safebuf char Msg[2*NM+1024],OutMsg[2*NM+1024];
- vsprintf(Msg,fmt,argptr);
-#ifdef _WIN_32
- for (int Src=0,Dest=0;;Src++)
- {
- char CurChar=Msg[Src];
- if (CurChar=='\n')
- OutMsg[Dest++]='\r';
- OutMsg[Dest++]=CurChar;
- if (CurChar==0)
- break;
- }
+ return Type==FILE_TYPE_CHAR || Type==FILE_TYPE_PIPE;
#else
- strcpy(OutMsg,Msg);
-#endif
- Write(OutMsg,strlen(OutMsg));
- va_end(argptr);
-}
+ return isatty(GetFD());
#endif
-
-
-bool File::RemoveCreated()
-{
- RemoveCreatedActive++;
- bool RetCode=true;
- for (int I=0;I<sizeof(CreatedFiles)/sizeof(CreatedFiles[0]);I++)
- if (CreatedFiles[I]!=NULL)
- {
- CreatedFiles[I]->SetExceptions(false);
- bool Success;
- if (CreatedFiles[I]->NewFile)
- Success=CreatedFiles[I]->Delete();
- else
- Success=CreatedFiles[I]->Close();
- if (Success)
- CreatedFiles[I]=NULL;
- else
- RetCode=false;
- }
- RemoveCreatedActive--;
- return(RetCode);
}
#ifndef SFX_MODULE
-long File::Copy(File &Dest,Int64 Length)
+int64 File::Copy(File &Dest,int64 Length)
{
- Array<char> Buffer(0x10000);
- long CopySize=0;
- bool CopyAll=(Length==INT64ERR);
+ Array<byte> Buffer(File::CopyBufferSize());
+ int64 CopySize=0;
+ bool CopyAll=(Length==INT64NDF);
while (CopyAll || Length>0)
{
Wait();
- int SizeToRead=(!CopyAll && Length<Buffer.Size()) ? int64to32(Length):Buffer.Size();
- int ReadSize=Read(&Buffer[0],SizeToRead);
+ size_t SizeToRead=(!CopyAll && Length<(int64)Buffer.Size()) ? (size_t)Length:Buffer.Size();
+ byte *Buf=&Buffer[0];
+ int ReadSize=Read(Buf,SizeToRead);
if (ReadSize==0)
break;
- Dest.Write(&Buffer[0],ReadSize);
+ size_t WriteSize=ReadSize;
+#ifdef _WIN_ALL
+ // For FAT32 USB flash drives in Windows if first write is 4 KB or more,
+ // write caching is disabled and "write through" is enabled, resulting
+ // in bad performance, especially for many small files. It happens when
+ // we create SFX archive on USB drive, because SFX module is written first.
+ // So we split the first write to small 1 KB followed by rest of data.
+ if (CopySize==0 && WriteSize>=4096)
+ {
+ const size_t FirstWrite=1024;
+ Dest.Write(Buf,FirstWrite);
+ Buf+=FirstWrite;
+ WriteSize-=FirstWrite;
+ }
+#endif
+ Dest.Write(Buf,WriteSize);
CopySize+=ReadSize;
if (!CopyAll)
Length-=ReadSize;
}
- return(CopySize);
+ return CopySize;
}
#endif
diff --git a/unrar/unrar/file.hpp b/unrar/unrar/file.hpp
index e5f768c..5f55de9 100644
--- a/unrar/unrar/file.hpp
+++ b/unrar/unrar/file.hpp
@@ -1,100 +1,165 @@
#ifndef _RAR_FILE_
#define _RAR_FILE_
-#ifdef _WIN_32
-typedef HANDLE FileHandle;
-#define BAD_HANDLE INVALID_HANDLE_VALUE
+#define FILE_USE_OPEN
+
+#ifdef _WIN_ALL
+ typedef HANDLE FileHandle;
+ #define FILE_BAD_HANDLE INVALID_HANDLE_VALUE
+#elif defined(FILE_USE_OPEN)
+ typedef off_t FileHandle;
+ #define FILE_BAD_HANDLE -1
#else
-typedef FILE* FileHandle;
-#define BAD_HANDLE NULL
+ typedef FILE* FileHandle;
+ #define FILE_BAD_HANDLE NULL
#endif
-class RAROptions;
-
-enum FILE_HANDLETYPE {FILE_HANDLENORMAL,FILE_HANDLESTD,FILE_HANDLEERR};
+enum FILE_HANDLETYPE {FILE_HANDLENORMAL,FILE_HANDLESTD};
enum FILE_ERRORTYPE {FILE_SUCCESS,FILE_NOTFOUND,FILE_READERROR};
-struct FileStat
-{
- uint FileAttr;
- uint FileTime;
- Int64 FileSize;
- bool IsDir;
+enum FILE_MODE_FLAGS {
+ // Request read only access to file. Default for Open.
+ FMF_READ=0,
+
+ // Request both read and write access to file. Default for Create.
+ FMF_UPDATE=1,
+
+ // Request write only access to file.
+ FMF_WRITE=2,
+
+ // Open files which are already opened for write by other programs.
+ FMF_OPENSHARED=4,
+
+ // Open files only if no other program is opened it even in shared mode.
+ FMF_OPENEXCLUSIVE=8,
+
+ // Provide read access to created file for other programs.
+ FMF_SHAREREAD=16,
+
+ // Use standard NTFS names without trailing dots and spaces.
+ FMF_STANDARDNAMES=32,
+
+ // Mode flags are not defined yet.
+ FMF_UNDEFINED=256
+};
+
+enum FILE_READ_ERROR_MODE {
+ FREM_ASK, // Propose to use the already read part, retry or abort.
+ FREM_TRUNCATE, // Use the already read part without additional prompt.
+ FREM_IGNORE // Try to skip unreadable block and read further.
};
class File
{
private:
- void AddFileToList(FileHandle hFile);
-
FileHandle hFile;
bool LastWrite;
FILE_HANDLETYPE HandleType;
+
+ // If we read the user input in console prompts from stdin, we shall
+ // process the available line immediately, not waiting for rest of data.
+ // Otherwise apps piping user responses to multiple Ask() prompts can
+ // hang if no more data is available yet and pipe isn't closed.
+ // If we read RAR archive or other file data from stdin, we shall collect
+ // the entire requested block as long as pipe isn't closed, so we get
+ // complete archive headers, not split between different reads.
+ bool LineInput;
+
bool SkipClose;
- bool IgnoreReadErrors;
+ FILE_READ_ERROR_MODE ReadErrorMode;
bool NewFile;
bool AllowDelete;
bool AllowExceptions;
-#ifdef _WIN_32
+#ifdef _WIN_ALL
bool NoSequentialRead;
+ uint CreateMode;
#endif
+ bool PreserveAtime;
+ bool TruncatedAfterReadError;
+
+ int64 CurFilePos; // Used for forward seeks in stdin files.
protected:
- bool OpenShared;
+ bool OpenShared; // Set by 'Archive' class.
public:
- char FileName[NM];
- wchar FileNameW[NM];
+ wchar FileName[NM];
FILE_ERRORTYPE ErrorType;
- uint CloseCount;
+ byte *SeekBuf; // To read instead of seek for stdin files.
+ static const size_t SeekBufSize=0x10000;
public:
File();
virtual ~File();
void operator = (File &SrcFile);
- bool Open(const char *Name,const wchar *NameW=NULL,bool OpenShared=false,bool Update=false);
- void TOpen(const char *Name,const wchar *NameW=NULL);
- bool WOpen(const char *Name,const wchar *NameW=NULL);
- bool Create(const char *Name,const wchar *NameW=NULL,bool ShareRead=true);
- void TCreate(const char *Name,const wchar *NameW=NULL,bool ShareRead=true);
- bool WCreate(const char *Name,const wchar *NameW=NULL,bool ShareRead=true);
- bool Close();
- void Flush();
+
+ // Several functions below are 'virtual', because they are redefined
+ // by Archive for QOpen and by MultiFile for split files in WinRAR.
+ virtual bool Open(const wchar *Name,uint Mode=FMF_READ);
+ void TOpen(const wchar *Name);
+ bool WOpen(const wchar *Name);
+ bool Create(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD);
+ void TCreate(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD);
+ bool WCreate(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD);
+ virtual bool Close(); // 'virtual' for MultiFile class.
bool Delete();
- bool Rename(const char *NewName,const wchar *NewNameW=NULL);
- void Write(const void *Data,int Size);
- int Read(void *Data,int Size);
- int DirectRead(void *Data,int Size);
- void Seek(Int64 Offset,int Method);
- bool RawSeek(Int64 Offset,int Method);
- Int64 Tell();
- void Prealloc(Int64 Size);
+ bool Rename(const wchar *NewName);
+ bool Write(const void *Data,size_t Size);
+ virtual int Read(void *Data,size_t Size);
+ int DirectRead(void *Data,size_t Size);
+ virtual void Seek(int64 Offset,int Method);
+ bool RawSeek(int64 Offset,int Method);
+ virtual int64 Tell();
+ void Prealloc(int64 Size);
byte GetByte();
void PutByte(byte Byte);
bool Truncate();
+ void Flush();
void SetOpenFileTime(RarTime *ftm,RarTime *ftc=NULL,RarTime *fta=NULL);
void SetCloseFileTime(RarTime *ftm,RarTime *fta=NULL);
- static void SetCloseFileTimeByName(const char *Name,RarTime *ftm,RarTime *fta);
- void SetOpenFileStat(RarTime *ftm,RarTime *ftc,RarTime *fta);
- void SetCloseFileStat(RarTime *ftm,RarTime *fta,uint FileAttr);
- void GetOpenFileTime(RarTime *ft);
- bool IsOpened() {return(hFile!=BAD_HANDLE);};
- Int64 FileLength();
- void SetHandleType(FILE_HANDLETYPE Type);
- FILE_HANDLETYPE GetHandleType() {return(HandleType);};
+ static void SetCloseFileTimeByName(const wchar *Name,RarTime *ftm,RarTime *fta);
+#ifdef _UNIX
+ static void StatToRarTime(struct stat &st,RarTime *ftm,RarTime *ftc,RarTime *fta);
+#endif
+ void GetOpenFileTime(RarTime *ftm,RarTime *ftc=NULL,RarTime *fta=NULL);
+ virtual bool IsOpened() {return hFile!=FILE_BAD_HANDLE;} // 'virtual' for MultiFile class.
+ int64 FileLength();
+ void SetHandleType(FILE_HANDLETYPE Type) {HandleType=Type;}
+ void SetLineInputMode(bool Mode) {LineInput=Mode;}
+ FILE_HANDLETYPE GetHandleType() {return HandleType;}
+ bool IsSeekable() {return HandleType!=FILE_HANDLESTD;}
bool IsDevice();
- void fprintf(const char *fmt,...);
static bool RemoveCreated();
- FileHandle GetHandle() {return(hFile);};
- void SetIgnoreReadErrors(bool Mode) {IgnoreReadErrors=Mode;};
- char *GetName() {return(FileName);}
- long Copy(File &Dest,Int64 Length=INT64ERR);
+ FileHandle GetHandle() {return hFile;}
+ void SetHandle(FileHandle Handle) {Close();hFile=Handle;}
+ void SetReadErrorMode(FILE_READ_ERROR_MODE Mode) {ReadErrorMode=Mode;}
+ int64 Copy(File &Dest,int64 Length=INT64NDF);
void SetAllowDelete(bool Allow) {AllowDelete=Allow;}
void SetExceptions(bool Allow) {AllowExceptions=Allow;}
-#ifdef _WIN_32
- void RemoveSequentialFlag() {NoSequentialRead=true;}
+ void SetPreserveAtime(bool Preserve) {PreserveAtime=Preserve;}
+ bool IsTruncatedAfterReadError() {return TruncatedAfterReadError;}
+#ifdef _UNIX
+ int GetFD()
+ {
+#ifdef FILE_USE_OPEN
+ return hFile;
+#else
+ return fileno(hFile);
+#endif
+ }
+#endif
+ static size_t CopyBufferSize()
+ {
+#ifdef _WIN_ALL
+ // USB flash performance is poor with 64 KB buffer, 256+ KB resolved it.
+ // For copying from HDD to same HDD the best performance was with 256 KB
+ // buffer in XP and with 1 MB buffer in Win10.
+ return WinNT()==WNT_WXP ? 0x40000:0x100000;
+#else
+ return 0x100000;
#endif
+ }
};
#endif
diff --git a/unrar/unrar/filefn.cpp b/unrar/unrar/filefn.cpp
index 54b0b99..4bcc8bf 100644
--- a/unrar/unrar/filefn.cpp
+++ b/unrar/unrar/filefn.cpp
@@ -1,146 +1,126 @@
#include "rar.hpp"
-MKDIR_CODE MakeDir(const char *Name,const wchar *NameW,uint Attr)
+MKDIR_CODE MakeDir(const wchar *Name,bool SetAttr,uint Attr)
{
-#ifdef _WIN_32
- int Success;
- if (WinNT() && NameW!=NULL && *NameW!=0)
- Success=CreateDirectoryW(NameW,NULL);
- else
- Success=CreateDirectory(Name,NULL);
- if (Success)
+#ifdef _WIN_ALL
+ // Windows automatically removes dots and spaces in the end of directory
+ // name. So we detect such names and process them with \\?\ prefix.
+ wchar *LastChar=PointToLastChar(Name);
+ bool Special=*LastChar=='.' || *LastChar==' ';
+ BOOL RetCode=Special ? FALSE : CreateDirectory(Name,NULL);
+ if (RetCode==0 && !FileExist(Name))
{
- SetFileAttr(Name,NameW,Attr);
- return(MKDIR_SUCCESS);
+ wchar LongName[NM];
+ if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
+ RetCode=CreateDirectory(LongName,NULL);
}
- int ErrCode=GetLastError();
- if (ErrCode==ERROR_FILE_NOT_FOUND || ErrCode==ERROR_PATH_NOT_FOUND)
- return(MKDIR_BADPATH);
- return(MKDIR_ERROR);
-#endif
-#ifdef _EMX
-#ifdef _DJGPP
- if (mkdir(Name,(Attr & FA_RDONLY) ? 0:S_IWUSR)==0)
-#else
- if (__mkdir(Name)==0)
-#endif
+ if (RetCode!=0) // Non-zero return code means success for CreateDirectory.
{
- SetFileAttr(Name,NameW,Attr);
- return(MKDIR_SUCCESS);
+ if (SetAttr)
+ SetFileAttr(Name,Attr);
+ return MKDIR_SUCCESS;
}
- return(errno==ENOENT ? MKDIR_BADPATH:MKDIR_ERROR);
-#endif
-#ifdef _UNIX
- int prevmask=umask(0);
- int ErrCode=Name==NULL ? -1:mkdir(Name,(mode_t)Attr);
- umask(prevmask);
+ int ErrCode=GetLastError();
+ if (ErrCode==ERROR_FILE_NOT_FOUND || ErrCode==ERROR_PATH_NOT_FOUND)
+ return MKDIR_BADPATH;
+ return MKDIR_ERROR;
+#elif defined(_UNIX)
+ char NameA[NM];
+ WideToChar(Name,NameA,ASIZE(NameA));
+ mode_t uattr=SetAttr ? (mode_t)Attr:0777;
+ int ErrCode=mkdir(NameA,uattr);
if (ErrCode==-1)
- return(errno==ENOENT ? MKDIR_BADPATH:MKDIR_ERROR);
- return(MKDIR_SUCCESS);
+ return errno==ENOENT ? MKDIR_BADPATH:MKDIR_ERROR;
+ return MKDIR_SUCCESS;
+#else
+ return MKDIR_ERROR;
#endif
}
-bool CreatePath(const char *Path,const wchar *PathW,bool SkipLastName)
+bool CreatePath(const wchar *Path,bool SkipLastName,bool Silent)
{
-#if defined(_WIN_32) || defined(_EMX)
+ if (Path==NULL || *Path==0)
+ return false;
+
+#if defined(_WIN_ALL) || defined(_EMX)
uint DirAttr=0;
#else
uint DirAttr=0777;
#endif
-#ifdef UNICODE_SUPPORTED
- bool Wide=PathW!=NULL && *PathW!=0 && UnicodeEnabled();
-#else
- bool Wide=false;
-#endif
- bool IgnoreAscii=false;
+
bool Success=true;
- const char *s=Path;
- for (int PosW=0;;PosW++)
+ for (const wchar *s=Path;*s!=0;s++)
{
- if (s==NULL || s-Path>=NM || *s==0)
- IgnoreAscii=true;
- if (Wide && (PosW>=NM || PathW[PosW]==0) || !Wide && IgnoreAscii)
+ wchar DirName[NM];
+ if (s-Path>=ASIZE(DirName))
break;
- if (Wide && PathW[PosW]==CPATHDIVIDER || !Wide && *s==CPATHDIVIDER)
+
+ // Process all kinds of path separators, so user can enter Unix style
+ // path in Windows or Windows in Unix. s>Path check avoids attempting
+ // creating an empty directory for paths starting from path separator.
+ if (IsPathDiv(*s) && s>Path)
{
- wchar *DirPtrW=NULL,DirNameW[NM];
- if (Wide)
- {
- strncpyw(DirNameW,PathW,PosW);
- DirNameW[PosW]=0;
- DirPtrW=DirNameW;
- }
- char DirName[NM];
- if (IgnoreAscii)
- WideToChar(DirPtrW,DirName);
- else
- {
-#ifndef DBCS_SUPPORTED
- if (*s!=CPATHDIVIDER)
- for (const char *n=s;*n!=0 && n-Path<NM;n++)
- if (*n==CPATHDIVIDER)
- {
- s=n;
- break;
- }
-#endif
- strncpy(DirName,Path,s-Path);
- DirName[s-Path]=0;
- }
- if (MakeDir(DirName,DirPtrW,DirAttr)==MKDIR_SUCCESS)
+#ifdef _WIN_ALL
+ // We must not attempt to create "D:" directory, because first
+ // CreateDirectory will fail, so we'll use \\?\D:, which forces Wine
+ // to create "D:" directory.
+ if (s==Path+2 && Path[1]==':')
+ continue;
+#endif
+ wcsncpy(DirName,Path,s-Path);
+ DirName[s-Path]=0;
+
+ Success=MakeDir(DirName,true,DirAttr)==MKDIR_SUCCESS;
+ if (Success && !Silent)
{
-#ifndef GUI
mprintf(St(MCreatDir),DirName);
- mprintf(" %s",St(MOk));
-#endif
+ mprintf(L" %s",St(MOk));
}
- else
- Success=false;
}
- if (!IgnoreAscii)
- s=charnext(s);
}
if (!SkipLastName && !IsPathDiv(*PointToLastChar(Path)))
- if (MakeDir(Path,PathW,DirAttr)!=MKDIR_SUCCESS)
- Success=false;
- return(Success);
+ Success=MakeDir(Path,true,DirAttr)==MKDIR_SUCCESS;
+ return Success;
}
-void SetDirTime(const char *Name,const wchar *NameW,RarTime *ftm,RarTime *ftc,RarTime *fta)
+void SetDirTime(const wchar *Name,RarTime *ftm,RarTime *ftc,RarTime *fta)
{
-#ifdef _WIN_32
- if (!WinNT())
- return;
-
+#if defined(_WIN_ALL)
bool sm=ftm!=NULL && ftm->IsSet();
bool sc=ftc!=NULL && ftc->IsSet();
bool sa=fta!=NULL && fta->IsSet();
- unsigned int DirAttr=GetFileAttr(Name,NameW);
- bool ResetAttr=(DirAttr!=0xffffffff && (DirAttr & FA_RDONLY)!=0);
+ uint DirAttr=GetFileAttr(Name);
+ bool ResetAttr=(DirAttr!=0xffffffff && (DirAttr & FILE_ATTRIBUTE_READONLY)!=0);
if (ResetAttr)
- SetFileAttr(Name,NameW,0);
+ SetFileAttr(Name,0);
- wchar DirNameW[NM];
- GetWideName(Name,NameW,DirNameW);
- HANDLE hFile=CreateFileW(DirNameW,GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,
+ HANDLE hFile=CreateFile(Name,GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL);
if (hFile==INVALID_HANDLE_VALUE)
+ {
+ wchar LongName[NM];
+ if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
+ hFile=CreateFile(LongName,GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,
+ NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL);
+ }
+
+ if (hFile==INVALID_HANDLE_VALUE)
return;
FILETIME fm,fc,fa;
if (sm)
- ftm->GetWin32(&fm);
+ ftm->GetWinFT(&fm);
if (sc)
- ftc->GetWin32(&fc);
+ ftc->GetWinFT(&fc);
if (sa)
- fta->GetWin32(&fa);
+ fta->GetWinFT(&fa);
SetFileTime(hFile,sc ? &fc:NULL,sa ? &fa:NULL,sm ? &fm:NULL);
CloseHandle(hFile);
if (ResetAttr)
- SetFileAttr(Name,NameW,DirAttr);
+ SetFileAttr(Name,DirAttr);
#endif
#if defined(_UNIX) || defined(_EMX)
File::SetCloseFileTimeByName(Name,ftm,fta);
@@ -148,145 +128,100 @@ void SetDirTime(const char *Name,const wchar *NameW,RarTime *ftm,RarTime *ftc,Ra
}
-bool IsRemovable(const char *Name)
+bool IsRemovable(const wchar *Name)
{
-#ifdef _WIN_32
- char Root[NM];
- GetPathRoot(Name,Root);
- int Type=GetDriveType(*Root ? Root:NULL);
- return(Type==DRIVE_REMOVABLE || Type==DRIVE_CDROM);
-#elif defined(_EMX)
- char Drive=etoupper(Name[0]);
- return((Drive=='A' || Drive=='B') && Name[1]==':');
+#if defined(_WIN_ALL)
+ wchar Root[NM];
+ GetPathRoot(Name,Root,ASIZE(Root));
+ int Type=GetDriveType(*Root!=0 ? Root:NULL);
+ return Type==DRIVE_REMOVABLE || Type==DRIVE_CDROM;
#else
- return(false);
+ return false;
#endif
}
#ifndef SFX_MODULE
-Int64 GetFreeDisk(const char *Name)
+int64 GetFreeDisk(const wchar *Name)
{
-#ifdef _WIN_32
- char Root[NM];
- GetPathRoot(Name,Root);
-
- typedef BOOL (WINAPI *GETDISKFREESPACEEX)(
- LPCTSTR,PULARGE_INTEGER,PULARGE_INTEGER,PULARGE_INTEGER
- );
- static GETDISKFREESPACEEX pGetDiskFreeSpaceEx=NULL;
-
- if (pGetDiskFreeSpaceEx==NULL)
- {
- HMODULE hKernel=GetModuleHandle("kernel32.dll");
- if (hKernel!=NULL)
- pGetDiskFreeSpaceEx=(GETDISKFREESPACEEX)GetProcAddress(hKernel,"GetDiskFreeSpaceExA");
- }
- if (pGetDiskFreeSpaceEx!=NULL)
- {
- GetFilePath(Name,Root,ASIZE(Root));
- ULARGE_INTEGER uiTotalSize,uiTotalFree,uiUserFree;
- uiUserFree.u.LowPart=uiUserFree.u.HighPart=0;
- if (pGetDiskFreeSpaceEx(*Root ? Root:NULL,&uiUserFree,&uiTotalSize,&uiTotalFree) &&
- uiUserFree.u.HighPart<=uiTotalFree.u.HighPart)
- return(int32to64(uiUserFree.u.HighPart,uiUserFree.u.LowPart));
- }
-
- DWORD SectorsPerCluster,BytesPerSector,FreeClusters,TotalClusters;
- if (!GetDiskFreeSpace(*Root ? Root:NULL,&SectorsPerCluster,&BytesPerSector,&FreeClusters,&TotalClusters))
- return(1457664);
- Int64 FreeSize=SectorsPerCluster*BytesPerSector;
- FreeSize=FreeSize*FreeClusters;
- return(FreeSize);
-#elif defined(_BEOS)
- char Root[NM];
+#ifdef _WIN_ALL
+ wchar Root[NM];
GetFilePath(Name,Root,ASIZE(Root));
- dev_t Dev=dev_for_path(*Root ? Root:".");
- if (Dev<0)
- return(1457664);
- fs_info Info;
- if (fs_stat_dev(Dev,&Info)!=0)
- return(1457664);
- Int64 FreeSize=Info.block_size;
- FreeSize=FreeSize*Info.free_blocks;
- return(FreeSize);
+
+ ULARGE_INTEGER uiTotalSize,uiTotalFree,uiUserFree;
+ uiUserFree.u.LowPart=uiUserFree.u.HighPart=0;
+ if (GetDiskFreeSpaceEx(*Root!=0 ? Root:NULL,&uiUserFree,&uiTotalSize,&uiTotalFree) &&
+ uiUserFree.u.HighPart<=uiTotalFree.u.HighPart)
+ return INT32TO64(uiUserFree.u.HighPart,uiUserFree.u.LowPart);
+ return 0;
#elif defined(_UNIX)
- return(1457664);
-#elif defined(_EMX)
- int Drive=(!isalpha(Name[0]) || Name[1]!=':') ? 0:etoupper(Name[0])-'A'+1;
-#ifndef _DJGPP
- if (_osmode == OS2_MODE)
- {
- FSALLOCATE fsa;
- if (DosQueryFSInfo(Drive,1,&fsa,sizeof(fsa))!=0)
- return(1457664);
- Int64 FreeSize=fsa.cSectorUnit*fsa.cbSector;
- FreeSize=FreeSize*fsa.cUnitAvail;
- return(FreeSize);
- }
- else
-#endif
- {
- union REGS regs,outregs;
- memset(&regs,0,sizeof(regs));
- regs.h.ah=0x36;
- regs.h.dl=Drive;
-#ifdef _DJGPP
- int86 (0x21,&regs,&outregs);
+ wchar Root[NM];
+ GetFilePath(Name,Root,ASIZE(Root));
+ char RootA[NM];
+ WideToChar(Root,RootA,ASIZE(RootA));
+ struct statvfs sfs;
+ if (statvfs(*RootA!=0 ? RootA:".",&sfs)!=0)
+ return 0;
+ int64 FreeSize=sfs.f_bsize;
+ FreeSize=FreeSize*sfs.f_bavail;
+ return FreeSize;
#else
- _int86 (0x21,&regs,&outregs);
+ return 0;
#endif
- if (outregs.x.ax==0xffff)
- return(1457664);
- Int64 FreeSize=outregs.x.ax*outregs.x.cx;
- FreeSize=FreeSize*outregs.x.bx;
- return(FreeSize);
- }
-#else
- #define DISABLEAUTODETECT
- return(1457664);
+}
#endif
+
+
+#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
+// Return 'true' for FAT and FAT32, so we can adjust the maximum supported
+// file size to 4 GB for these file systems.
+bool IsFAT(const wchar *Name)
+{
+ wchar Root[NM];
+ GetPathRoot(Name,Root,ASIZE(Root));
+ wchar FileSystem[MAX_PATH+1];
+ if (GetVolumeInformation(Root,NULL,0,NULL,NULL,NULL,FileSystem,ASIZE(FileSystem)))
+ return wcscmp(FileSystem,L"FAT")==0 || wcscmp(FileSystem,L"FAT32")==0;
+ return false;
}
#endif
-bool FileExist(const char *Name,const wchar *NameW)
+bool FileExist(const wchar *Name)
{
-#ifdef _WIN_32
- if (WinNT() && NameW!=NULL && *NameW!=0)
- return(GetFileAttributesW(NameW)!=0xffffffff);
- else
- return(GetFileAttributes(Name)!=0xffffffff);
+#ifdef _WIN_ALL
+ return GetFileAttr(Name)!=0xffffffff;
#elif defined(ENABLE_ACCESS)
- return(access(Name,0)==0);
+ char NameA[NM];
+ WideToChar(Name,NameA,ASIZE(NameA));
+ return access(NameA,0)==0;
#else
- struct FindData FD;
- return(FindFile::FastFind(Name,NameW,&FD));
+ FindData FD;
+ return FindFile::FastFind(Name,&FD);
#endif
}
+
-
-bool WildFileExist(const char *Name,const wchar *NameW)
+bool WildFileExist(const wchar *Name)
{
- if (IsWildcard(Name,NameW))
+ if (IsWildcard(Name))
{
FindFile Find;
Find.SetMask(Name);
- Find.SetMaskW(NameW);
- struct FindData fd;
- return(Find.Next(&fd));
+ FindData fd;
+ return Find.Next(&fd);
}
- return(FileExist(Name,NameW));
+ return FileExist(Name);
}
bool IsDir(uint Attr)
{
-#if defined (_WIN_32) || defined(_EMX)
- return(Attr!=0xffffffff && (Attr & 0x10)!=0);
+#ifdef _WIN_ALL
+ return Attr!=0xffffffff && (Attr & FILE_ATTRIBUTE_DIRECTORY)!=0;
#endif
#if defined(_UNIX)
- return((Attr & 0xF000)==0x4000);
+ return (Attr & 0xF000)==0x4000;
#endif
}
@@ -294,28 +229,20 @@ bool IsDir(uint Attr)
bool IsUnreadable(uint Attr)
{
#if defined(_UNIX) && defined(S_ISFIFO) && defined(S_ISSOCK) && defined(S_ISCHR)
- return(S_ISFIFO(Attr) || S_ISSOCK(Attr) || S_ISCHR(Attr));
-#endif
- return(false);
-}
-
-
-bool IsLabel(uint Attr)
-{
-#if defined (_WIN_32) || defined(_EMX)
- return((Attr & 8)!=0);
-#else
- return(false);
+ return S_ISFIFO(Attr) || S_ISSOCK(Attr) || S_ISCHR(Attr);
#endif
+ return false;
}
bool IsLink(uint Attr)
{
#ifdef _UNIX
- return((Attr & 0xF000)==0xA000);
+ return (Attr & 0xF000)==0xA000;
+#elif defined(_WIN_ALL)
+ return (Attr & FILE_ATTRIBUTE_REPARSE_POINT)!=0;
#else
- return(false);
+ return false;
#endif
}
@@ -326,249 +253,283 @@ bool IsLink(uint Attr)
bool IsDeleteAllowed(uint FileAttr)
{
-#if defined(_WIN_32) || defined(_EMX)
- return((FileAttr & (FA_RDONLY|FA_SYSTEM|FA_HIDDEN))==0);
+#ifdef _WIN_ALL
+ return (FileAttr & (FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN))==0;
#else
- return((FileAttr & (S_IRUSR|S_IWUSR))==(S_IRUSR|S_IWUSR));
+ return (FileAttr & (S_IRUSR|S_IWUSR))==(S_IRUSR|S_IWUSR);
#endif
}
-void PrepareToDelete(const char *Name,const wchar *NameW)
+void PrepareToDelete(const wchar *Name)
{
-#if defined(_WIN_32) || defined(_EMX)
- SetFileAttr(Name,NameW,0);
+#if defined(_WIN_ALL) || defined(_EMX)
+ SetFileAttr(Name,0);
#endif
#ifdef _UNIX
- chmod(Name,S_IRUSR|S_IWUSR|S_IXUSR);
+ if (Name!=NULL)
+ {
+ char NameA[NM];
+ WideToChar(Name,NameA,ASIZE(NameA));
+ chmod(NameA,S_IRUSR|S_IWUSR|S_IXUSR);
+ }
#endif
}
-uint GetFileAttr(const char *Name,const wchar *NameW)
+uint GetFileAttr(const wchar *Name)
{
-#ifdef _WIN_32
- if (WinNT() && NameW!=NULL && *NameW!=0)
- return(GetFileAttributesW(NameW));
- else
- return(GetFileAttributes(Name));
-#elif defined(_DJGPP)
- return(_chmod(Name,0));
+#ifdef _WIN_ALL
+ DWORD Attr=GetFileAttributes(Name);
+ if (Attr==0xffffffff)
+ {
+ wchar LongName[NM];
+ if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
+ Attr=GetFileAttributes(LongName);
+ }
+ return Attr;
#else
+ char NameA[NM];
+ WideToChar(Name,NameA,ASIZE(NameA));
struct stat st;
- if (stat(Name,&st)!=0)
- return(0);
-#ifdef _EMX
- return(st.st_attr);
-#else
- return(st.st_mode);
-#endif
+ if (stat(NameA,&st)!=0)
+ return 0;
+ return st.st_mode;
#endif
}
-bool SetFileAttr(const char *Name,const wchar *NameW,uint Attr)
+bool SetFileAttr(const wchar *Name,uint Attr)
{
- bool Success;
-#ifdef _WIN_32
- if (WinNT() && NameW!=NULL && *NameW!=0)
- Success=SetFileAttributesW(NameW,Attr)!=0;
- else
- Success=SetFileAttributes(Name,Attr)!=0;
-#elif defined(_DJGPP)
- Success=_chmod(Name,1,Attr)!=-1;
-#elif defined(_EMX)
- Success=__chmod(Name,1,Attr)!=-1;
+#ifdef _WIN_ALL
+ bool Success=SetFileAttributes(Name,Attr)!=0;
+ if (!Success)
+ {
+ wchar LongName[NM];
+ if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
+ Success=SetFileAttributes(LongName,Attr)!=0;
+ }
+ return Success;
#elif defined(_UNIX)
- Success=chmod(Name,(mode_t)Attr)==0;
+ char NameA[NM];
+ WideToChar(Name,NameA,ASIZE(NameA));
+ return chmod(NameA,(mode_t)Attr)==0;
#else
- Success=false;
+ return false;
#endif
- return(Success);
}
-void ConvertNameToFull(const char *Src,char *Dest)
+wchar *MkTemp(wchar *Name,size_t MaxSize)
{
-#ifdef _WIN_32
-#ifndef _WIN_CE
- char FullName[NM],*NamePtr;
- if (GetFullPathName(Src,sizeof(FullName),FullName,&NamePtr))
- strcpy(Dest,FullName);
- else
-#endif
- if (Src!=Dest)
- strcpy(Dest,Src);
-#else
- char FullName[NM];
- if (IsPathDiv(*Src) || IsDiskLetter(Src))
- strcpy(FullName,Src);
- else
- {
- getcwd(FullName,sizeof(FullName));
- AddEndSlash(FullName);
- strcat(FullName,Src);
- }
- strcpy(Dest,FullName);
-#endif
-}
+ size_t Length=wcslen(Name);
+ RarTime CurTime;
+ CurTime.SetCurrentTime();
-#ifndef SFX_MODULE
-void ConvertNameToFull(const wchar *Src,wchar *Dest)
-{
- if (Src==NULL || *Src==0)
- {
- *Dest=0;
- return;
- }
-#ifdef _WIN_32
-#ifndef _WIN_CE
- if (WinNT())
-#endif
- {
-#ifndef _WIN_CE
- wchar FullName[NM],*NamePtr;
- if (GetFullPathNameW(Src,sizeof(FullName)/sizeof(FullName[0]),FullName,&NamePtr))
- strcpyw(Dest,FullName);
- else
-#endif
- if (Src!=Dest)
- strcpyw(Dest,Src);
- }
-#ifndef _WIN_CE
- else
- {
- char AnsiName[NM];
- WideToChar(Src,AnsiName);
- ConvertNameToFull(AnsiName,AnsiName);
- CharToWide(AnsiName,Dest);
- }
-#endif
-#else
- char AnsiName[NM];
- WideToChar(Src,AnsiName);
- ConvertNameToFull(AnsiName,AnsiName);
- CharToWide(AnsiName,Dest);
-#endif
-}
-#endif
+ // We cannot use CurTime.GetWin() as is, because its lowest bits can
+ // have low informational value, like being a zero or few fixed numbers.
+ uint Random=(uint)(CurTime.GetWin()/100000);
+ // Using PID we guarantee that different RAR copies use different temp names
+ // even if started in exactly the same time.
+ uint PID=0;
+#ifdef _WIN_ALL
+ PID=(uint)GetCurrentProcessId();
+#elif defined(_UNIX)
+ PID=(uint)getpid();
+#endif
-#ifndef SFX_MODULE
-char *MkTemp(char *Name)
-{
- size_t Length=strlen(Name);
- if (Length<=6)
- return(NULL);
- int Random=clock();
- for (int Attempt=0;;Attempt++)
+ for (uint Attempt=0;;Attempt++)
{
- sprintf(Name+Length-6,"%06u",Random+Attempt);
- Name[Length-4]='.';
+ uint Ext=Random%50000+Attempt;
+ wchar RndText[50];
+ swprintf(RndText,ASIZE(RndText),L"%u.%03u",PID,Ext);
+ if (Length+wcslen(RndText)>=MaxSize || Attempt==1000)
+ return NULL;
+ wcsncpyz(Name+Length,RndText,MaxSize-Length);
if (!FileExist(Name))
break;
- if (Attempt==1000)
- return(NULL);
}
- return(Name);
+ return Name;
}
-#endif
+#if !defined(SFX_MODULE)
+void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size,uint Flags)
+{
+ int64 SavePos=SrcFile->Tell();
+#ifndef SILENT
+ int64 FileLength=Size==INT64NDF ? SrcFile->FileLength() : Size;
+#endif
+
+ if ((Flags & (CALCFSUM_SHOWTEXT|CALCFSUM_SHOWPERCENT))!=0)
+ uiMsg(UIEVENT_FILESUMSTART);
+ if ((Flags & CALCFSUM_CURPOS)==0)
+ SrcFile->Seek(0,SEEK_SET);
-#ifndef SFX_MODULE
-uint CalcFileCRC(File *SrcFile,Int64 Size,CALCCRC_SHOWMODE ShowMode)
-{
- SaveFilePos SavePos(*SrcFile);
- const int BufSize=0x10000;
+ const size_t BufSize=0x100000;
Array<byte> Data(BufSize);
- Int64 BlockCount=0;
- uint DataCRC=0xffffffff;
- int ReadSize;
-#if !defined(SILENT) && !defined(_WIN_CE)
- Int64 FileLength=SrcFile->FileLength();
- if (ShowMode!=CALCCRC_SHOWNONE)
- {
- mprintf(St(MCalcCRC));
- mprintf(" ");
- }
-#endif
+ DataHash HashCRC,HashBlake2;
+ HashCRC.Init(HASH_CRC32,Threads);
+ HashBlake2.Init(HASH_BLAKE2,Threads);
- SrcFile->Seek(0,SEEK_SET);
- while ((ReadSize=SrcFile->Read(&Data[0],int64to32(Size==INT64ERR ? Int64(BufSize):Min(Int64(BufSize),Size))))!=0)
+ int64 BlockCount=0;
+ int64 TotalRead=0;
+ while (true)
{
- ++BlockCount;
- if ((BlockCount & 15)==0)
+ size_t SizeToRead;
+ if (Size==INT64NDF) // If we process the entire file.
+ SizeToRead=BufSize; // Then always attempt to read the entire buffer.
+ else
+ SizeToRead=(size_t)Min((int64)BufSize,Size);
+ int ReadSize=SrcFile->Read(&Data[0],SizeToRead);
+ if (ReadSize==0)
+ break;
+ TotalRead+=ReadSize;
+
+ if ((++BlockCount & 0xf)==0)
{
-#if !defined(SILENT) && !defined(_WIN_CE)
- if (ShowMode==CALCCRC_SHOWALL)
- mprintf("\b\b\b\b%3d%%",ToPercent(BlockCount*Int64(BufSize),FileLength));
+#ifndef SILENT
+ if ((Flags & CALCFSUM_SHOWPROGRESS)!=0)
+ {
+ // Update only the current file progress in WinRAR, set the total to 0
+ // to keep it as is. It looks better for WinRAR.
+ uiExtractProgress(TotalRead,FileLength,0,0);
+ }
+ else
+ {
+ if ((Flags & CALCFSUM_SHOWPERCENT)!=0)
+ uiMsg(UIEVENT_FILESUMPROGRESS,ToPercent(TotalRead,FileLength));
+ }
#endif
Wait();
}
- DataCRC=CRC(DataCRC,&Data[0],ReadSize);
- if (Size!=INT64ERR)
- Size-=ReadSize;
- }
-#if !defined(SILENT) && !defined(_WIN_CE)
- if (ShowMode==CALCCRC_SHOWALL)
- mprintf("\b\b\b\b ");
-#endif
- return(DataCRC^0xffffffff);
-}
-#endif
+ if (CRC32!=NULL)
+ HashCRC.Update(&Data[0],ReadSize);
+ if (Blake2!=NULL)
+ HashBlake2.Update(&Data[0],ReadSize);
-bool RenameFile(const char *SrcName,const wchar *SrcNameW,const char *DestName,const wchar *DestNameW)
-{
- return(rename(SrcName,DestName)==0);
-}
+ if (Size!=INT64NDF)
+ Size-=ReadSize;
+ }
+ SrcFile->Seek(SavePos,SEEK_SET);
+ if ((Flags & CALCFSUM_SHOWPERCENT)!=0)
+ uiMsg(UIEVENT_FILESUMEND);
-bool DelFile(const char *Name)
-{
- return(DelFile(Name,NULL));
+ if (CRC32!=NULL)
+ *CRC32=HashCRC.GetCRC32();
+ if (Blake2!=NULL)
+ {
+ HashValue Result;
+ HashBlake2.Result(&Result);
+ memcpy(Blake2,Result.Digest,sizeof(Result.Digest));
+ }
}
+#endif
-bool DelFile(const char *Name,const wchar *NameW)
+bool RenameFile(const wchar *SrcName,const wchar *DestName)
{
- return(remove(Name)==0);
+#ifdef _WIN_ALL
+ bool Success=MoveFile(SrcName,DestName)!=0;
+ if (!Success)
+ {
+ wchar LongName1[NM],LongName2[NM];
+ if (GetWinLongPath(SrcName,LongName1,ASIZE(LongName1)) &&
+ GetWinLongPath(DestName,LongName2,ASIZE(LongName2)))
+ Success=MoveFile(LongName1,LongName2)!=0;
+ }
+ return Success;
+#else
+ char SrcNameA[NM],DestNameA[NM];
+ WideToChar(SrcName,SrcNameA,ASIZE(SrcNameA));
+ WideToChar(DestName,DestNameA,ASIZE(DestNameA));
+ bool Success=rename(SrcNameA,DestNameA)==0;
+ return Success;
+#endif
}
-bool DelDir(const char *Name)
+bool DelFile(const wchar *Name)
{
- return(DelDir(Name,NULL));
+#ifdef _WIN_ALL
+ bool Success=DeleteFile(Name)!=0;
+ if (!Success)
+ {
+ wchar LongName[NM];
+ if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
+ Success=DeleteFile(LongName)!=0;
+ }
+ return Success;
+#else
+ char NameA[NM];
+ WideToChar(Name,NameA,ASIZE(NameA));
+ bool Success=remove(NameA)==0;
+ return Success;
+#endif
}
-bool DelDir(const char *Name,const wchar *NameW)
+bool DelDir(const wchar *Name)
{
- return(rmdir(Name)==0);
+#ifdef _WIN_ALL
+ bool Success=RemoveDirectory(Name)!=0;
+ if (!Success)
+ {
+ wchar LongName[NM];
+ if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
+ Success=RemoveDirectory(LongName)!=0;
+ }
+ return Success;
+#else
+ char NameA[NM];
+ WideToChar(Name,NameA,ASIZE(NameA));
+ bool Success=rmdir(NameA)==0;
+ return Success;
+#endif
}
-#if defined(_WIN_32) && !defined(_WIN_CE)
-bool SetFileCompression(char *Name,wchar *NameW,bool State)
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
+bool SetFileCompression(const wchar *Name,bool State)
{
- wchar FileNameW[NM];
- GetWideName(Name,NameW,FileNameW);
- HANDLE hFile=CreateFileW(FileNameW,FILE_READ_DATA|FILE_WRITE_DATA,
+ HANDLE hFile=CreateFile(Name,FILE_READ_DATA|FILE_WRITE_DATA,
FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_SEQUENTIAL_SCAN,NULL);
if (hFile==INVALID_HANDLE_VALUE)
- return(false);
+ {
+ wchar LongName[NM];
+ if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
+ hFile=CreateFile(LongName,FILE_READ_DATA|FILE_WRITE_DATA,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_SEQUENTIAL_SCAN,NULL);
+ }
+ if (hFile==INVALID_HANDLE_VALUE)
+ return false;
SHORT NewState=State ? COMPRESSION_FORMAT_DEFAULT:COMPRESSION_FORMAT_NONE;
DWORD Result;
int RetCode=DeviceIoControl(hFile,FSCTL_SET_COMPRESSION,&NewState,
sizeof(NewState),NULL,0,&Result,NULL);
CloseHandle(hFile);
- return(RetCode!=0);
+ return RetCode!=0;
+}
+
+
+void ResetFileCache(const wchar *Name)
+{
+ // To reset file cache in Windows it is enough to open it with
+ // FILE_FLAG_NO_BUFFERING and then close it.
+ HANDLE hSrc=CreateFile(Name,GENERIC_READ,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ NULL,OPEN_EXISTING,FILE_FLAG_NO_BUFFERING,NULL);
+ if (hSrc!=INVALID_HANDLE_VALUE)
+ CloseHandle(hSrc);
}
#endif
@@ -577,3 +538,69 @@ bool SetFileCompression(char *Name,wchar *NameW,bool State)
+
+
+
+
+
+
+// Delete symbolic links in file path, if any, and replace them by directories.
+// Prevents extracting files outside of destination folder with symlink chains.
+bool LinksToDirs(const wchar *SrcName,const wchar *SkipPart,std::wstring &LastChecked)
+{
+ // Unlike Unix, Windows doesn't expand lnk1 in symlink targets like
+ // "lnk1/../dir", but converts the path to "dir". In Unix we need to call
+ // this function to prevent placing unpacked files outside of destination
+ // folder if previously we unpacked "dir/lnk1" -> "..",
+ // "dir/lnk2" -> "lnk1/.." and "dir/lnk2/anypath/poc.txt".
+ // We may still need this function to prevent abusing symlink chains
+ // in link source path if we remove detection of such chains
+ // in IsRelativeSymlinkSafe. This function seems to make other symlink
+ // related safety checks redundant, but for now we prefer to keep them too.
+ //
+ // 2022.12.01: the performance impact is minimized after adding the check
+ // against the previous path and enabling this verification only after
+ // extracting a symlink with ".." in target. So we enabled it for Windows
+ // as well for extra safety.
+//#ifdef _UNIX
+ wchar Path[NM];
+ if (wcslen(SrcName)>=ASIZE(Path))
+ return false; // It should not be that long, skip.
+ wcsncpyz(Path,SrcName,ASIZE(Path));
+
+ size_t SkipLength=wcslen(SkipPart);
+
+ if (SkipLength>0 && wcsncmp(Path,SkipPart,SkipLength)!=0)
+ SkipLength=0; // Parameter validation, not really needed now.
+
+ // Do not check parts already checked in previous path to improve performance.
+ for (uint I=0;Path[I]!=0 && I<LastChecked.size() && Path[I]==LastChecked[I];I++)
+ if (IsPathDiv(Path[I]) && I>SkipLength)
+ SkipLength=I;
+
+ wchar *Name=Path;
+ if (SkipLength>0)
+ {
+ // Avoid converting symlinks in destination path part specified by user.
+ Name+=SkipLength;
+ while (IsPathDiv(*Name))
+ Name++;
+ }
+
+ for (wchar *s=Path+wcslen(Path)-1;s>Name;s--)
+ if (IsPathDiv(*s))
+ {
+ *s=0;
+ FindData FD;
+ if (FindFile::FastFind(Path,&FD,true) && FD.IsLink)
+#ifdef _WIN_ALL
+ if (!DelDir(Path))
+#else
+ if (!DelFile(Path))
+#endif
+ return false; // Couldn't delete the symlink to replace it with directory.
+ }
+ LastChecked=SrcName;
+//#endif
+ return true;
+}
diff --git a/unrar/unrar/filefn.hpp b/unrar/unrar/filefn.hpp
index cd8e3fc..d73d55e 100644
--- a/unrar/unrar/filefn.hpp
+++ b/unrar/unrar/filefn.hpp
@@ -3,42 +3,49 @@
enum MKDIR_CODE {MKDIR_SUCCESS,MKDIR_ERROR,MKDIR_BADPATH};
-MKDIR_CODE MakeDir(const char *Name,const wchar *NameW,uint Attr);
-bool CreatePath(const char *Path,const wchar *PathW,bool SkipLastName);
-void SetDirTime(const char *Name,const wchar *NameW,RarTime *ftm,RarTime *ftc,RarTime *fta);
-bool IsRemovable(const char *Name);
-Int64 GetFreeDisk(const char *Name);
-bool FileExist(const char *Name,const wchar *NameW=NULL);
-bool WildFileExist(const char *Name,const wchar *NameW=NULL);
+MKDIR_CODE MakeDir(const wchar *Name,bool SetAttr,uint Attr);
+bool CreatePath(const wchar *Path,bool SkipLastName,bool Silent);
+void SetDirTime(const wchar *Name,RarTime *ftm,RarTime *ftc,RarTime *fta);
+bool IsRemovable(const wchar *Name);
+
+#ifndef SFX_MODULE
+int64 GetFreeDisk(const wchar *Name);
+#endif
+
+#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
+bool IsFAT(const wchar *Root);
+#endif
+
+bool FileExist(const wchar *Name);
+bool WildFileExist(const wchar *Name);
bool IsDir(uint Attr);
bool IsUnreadable(uint Attr);
-bool IsLabel(uint Attr);
bool IsLink(uint Attr);
-void SetSFXMode(const char *FileName);
-void EraseDiskContents(const char *FileName);
+void SetSFXMode(const wchar *FileName);
+void EraseDiskContents(const wchar *FileName);
bool IsDeleteAllowed(uint FileAttr);
-void PrepareToDelete(const char *Name,const wchar *NameW=NULL);
-uint GetFileAttr(const char *Name,const wchar *NameW=NULL);
-bool SetFileAttr(const char *Name,const wchar *NameW,uint Attr);
-void ConvertNameToFull(const char *Src,char *Dest);
-void ConvertNameToFull(const wchar *Src,wchar *Dest);
-char* MkTemp(char *Name);
+void PrepareToDelete(const wchar *Name);
+uint GetFileAttr(const wchar *Name);
+bool SetFileAttr(const wchar *Name,uint Attr);
+wchar* MkTemp(wchar *Name,size_t MaxSize);
+enum CALCFSUM_FLAGS {CALCFSUM_SHOWTEXT=1,CALCFSUM_SHOWPERCENT=2,CALCFSUM_SHOWPROGRESS=4,CALCFSUM_CURPOS=8};
-enum CALCCRC_SHOWMODE {CALCCRC_SHOWNONE,CALCCRC_SHOWTEXT,CALCCRC_SHOWALL};
-uint CalcFileCRC(File *SrcFile,Int64 Size=INT64ERR,CALCCRC_SHOWMODE ShowMode=CALCCRC_SHOWNONE);
+void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size=INT64NDF,uint Flags=0);
-bool RenameFile(const char *SrcName,const wchar *SrcNameW,const char *DestName,const wchar *DestNameW);
-bool DelFile(const char *Name);
-bool DelFile(const char *Name,const wchar *NameW);
-bool DelDir(const char *Name);
-bool DelDir(const char *Name,const wchar *NameW);
+bool RenameFile(const wchar *SrcName,const wchar *DestName);
+bool DelFile(const wchar *Name);
+bool DelDir(const wchar *Name);
-#if defined(_WIN_32) && !defined(_WIN_CE)
-bool SetFileCompression(char *Name,wchar *NameW,bool State);
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
+bool SetFileCompression(const wchar *Name,bool State);
+void ResetFileCache(const wchar *Name);
#endif
+
+bool LinksToDirs(const wchar *SrcName,const wchar *SkipPart,std::wstring &LastChecked);
+
#endif
diff --git a/unrar/unrar/filestr.cpp b/unrar/unrar/filestr.cpp
index c7217af..a5d29d7 100644
--- a/unrar/unrar/filestr.cpp
+++ b/unrar/unrar/filestr.cpp
@@ -1,182 +1,166 @@
#include "rar.hpp"
-static bool IsUnicode(byte *Data,int Size);
-
-bool ReadTextFile(char *Name,StringList *List,bool Config,bool AbortOnError,
- RAR_CHARSET SrcCharset,bool Unquote,bool SkipComments,
- bool ExpandEnvStr)
+bool ReadTextFile(
+ const wchar *Name,
+ StringList *List,
+ bool Config,
+ bool AbortOnError,
+ RAR_CHARSET SrcCharset,
+ bool Unquote,
+ bool SkipComments,
+ bool ExpandEnvStr)
{
- char FileName[NM];
- if (Config)
- GetConfigName(Name,FileName,true);
- else
- strcpy(FileName,Name);
+ wchar FileName[NM];
+ *FileName=0;
+
+ if (Name!=NULL)
+ if (Config)
+ GetConfigName(Name,FileName,ASIZE(FileName),true,false);
+ else
+ wcsncpyz(FileName,Name,ASIZE(FileName));
File SrcFile;
- if (*FileName)
+ if (*FileName!=0)
{
- bool OpenCode=AbortOnError ? SrcFile.WOpen(FileName):SrcFile.Open(FileName);
+ bool OpenCode=AbortOnError ? SrcFile.WOpen(FileName):SrcFile.Open(FileName,0);
if (!OpenCode)
{
if (AbortOnError)
- ErrHandler.Exit(OPEN_ERROR);
- return(false);
+ ErrHandler.Exit(RARX_OPEN);
+ return false;
}
}
else
SrcFile.SetHandleType(FILE_HANDLESTD);
- unsigned int DataSize=0,ReadSize;
- const int ReadBlock=1024;
- Array<char> Data(ReadBlock+5);
+ uint DataSize=0,ReadSize;
+ const int ReadBlock=4096;
+
+ Array<byte> Data(ReadBlock);
while ((ReadSize=SrcFile.Read(&Data[DataSize],ReadBlock))!=0)
{
DataSize+=ReadSize;
- Data.Add(ReadSize);
+ Data.Add(ReadSize); // Always have ReadBlock available for next data.
}
+ // Set to really read size, so we can zero terminate it correctly.
+ Data.Alloc(DataSize);
- memset(&Data[DataSize],0,5);
+ int LittleEndian=DataSize>=2 && Data[0]==255 && Data[1]==254 ? 1:0;
+ int BigEndian=DataSize>=2 && Data[0]==254 && Data[1]==255 ? 1:0;
+ bool Utf8=DataSize>=3 && Data[0]==0xef && Data[1]==0xbb && Data[2]==0xbf;
- if (SrcCharset==RCH_UNICODE ||
- SrcCharset==RCH_DEFAULT && IsUnicode((byte *)&Data[0],DataSize))
- {
- // Unicode in native system format, can be more than 2 bytes per character
- Array<wchar> DataW(Data.Size()/2+1);
- for (int I=2;I<Data.Size()-1;I+=2)
- DataW[(I-2)/2]=(wchar)Data[I]+(wchar)Data[I+1]*256;
+ if (SrcCharset==RCH_DEFAULT)
+ SrcCharset=DetectTextEncoding(&Data[0],DataSize);
- wchar *CurStr=&DataW[0];
- Array<char> AnsiName;
+ Array<wchar> DataW;
- while (*CurStr!=0)
- {
- wchar *NextStr=CurStr,*CmtPtr=NULL;
- while (*NextStr!='\r' && *NextStr!='\n' && *NextStr!=0)
- {
- if (SkipComments && NextStr[0]=='/' && NextStr[1]=='/')
- {
- *NextStr=0;
- CmtPtr=NextStr;
- }
- NextStr++;
- }
- *NextStr=0;
- for (wchar *SpacePtr=(CmtPtr ? CmtPtr:NextStr)-1;SpacePtr>=CurStr;SpacePtr--)
- {
- if (*SpacePtr!=' ' && *SpacePtr!='\t')
- break;
- *SpacePtr=0;
- }
- if (*CurStr)
- {
- int Length=strlenw(CurStr);
- int AddSize=4*(Length-AnsiName.Size()+1);
- if (AddSize>0)
- AnsiName.Add(AddSize);
- if (Unquote && *CurStr=='\"' && CurStr[Length-1]=='\"')
- {
- CurStr[Length-1]=0;
- CurStr++;
- }
- WideToChar(CurStr,&AnsiName[0],AnsiName.Size());
-
- bool Expanded=false;
-#if defined(_WIN_32) && !defined(_WIN_CE)
- if (ExpandEnvStr && *CurStr=='%')
- {
- // expanding environment variables in Windows version
-
- char ExpName[NM];
- wchar ExpNameW[NM];
- *ExpNameW=0;
- int ret,retw=1;
- ret=ExpandEnvironmentStrings(&AnsiName[0],ExpName,ASIZE(ExpName));
- if (ret!=0 && WinNT())
- retw=ExpandEnvironmentStringsW(CurStr,ExpNameW,ASIZE(ExpNameW));
- Expanded=ret!=0 && ret<ASIZE(ExpName) &&
- retw!=0 && retw<ASIZE(ExpNameW);
- if (Expanded)
- List->AddString(ExpName,ExpNameW);
- }
+ if (SrcCharset==RCH_DEFAULT || SrcCharset==RCH_OEM || SrcCharset==RCH_ANSI)
+ {
+ Data.Push(0); // Zero terminate.
+#if defined(_WIN_ALL)
+ if (SrcCharset==RCH_OEM)
+ OemToCharA((char *)&Data[0],(char *)&Data[0]);
#endif
- if (!Expanded)
- List->AddString(&AnsiName[0],CurStr);
- }
- CurStr=NextStr+1;
- while (*CurStr=='\r' || *CurStr=='\n')
- CurStr++;
+ DataW.Alloc(Data.Size());
+ CharToWide((char *)&Data[0],&DataW[0],DataW.Size());
+ }
+
+ if (SrcCharset==RCH_UNICODE)
+ {
+ size_t Start=2; // Skip byte order mark.
+ if (!LittleEndian && !BigEndian) // No byte order mask.
+ {
+ Start=0;
+ LittleEndian=1;
}
+
+ DataW.Alloc(Data.Size()/2+1);
+ size_t End=Data.Size() & ~1; // We need even bytes number for UTF-16.
+ for (size_t I=Start;I<End;I+=2)
+ DataW[(I-Start)/2]=Data[I+BigEndian]+Data[I+LittleEndian]*256;
+ DataW[(End-Start)/2]=0;
}
- else
+
+ if (SrcCharset==RCH_UTF8)
+ {
+ Data.Push(0); // Zero terminate data.
+ DataW.Alloc(Data.Size());
+ UtfToWide((const char *)(Data+(Utf8 ? 3:0)),&DataW[0],DataW.Size());
+ }
+
+ wchar *CurStr=&DataW[0];
+
+ while (*CurStr!=0)
{
- char *CurStr=&Data[0];
- while (*CurStr!=0)
+ wchar *NextStr=CurStr,*CmtPtr=NULL;
+ while (*NextStr!='\r' && *NextStr!='\n' && *NextStr!=0)
{
- char *NextStr=CurStr,*CmtPtr=NULL;
- while (*NextStr!='\r' && *NextStr!='\n' && *NextStr!=0)
+ if (SkipComments && NextStr[0]=='/' && NextStr[1]=='/')
{
- if (SkipComments && NextStr[0]=='/' && NextStr[1]=='/')
- {
- *NextStr=0;
- CmtPtr=NextStr;
- }
- NextStr++;
+ *NextStr=0;
+ CmtPtr=NextStr;
}
- *NextStr=0;
- for (char *SpacePtr=(CmtPtr ? CmtPtr:NextStr)-1;SpacePtr>=CurStr;SpacePtr--)
+ NextStr++;
+ }
+ bool Done=*NextStr==0;
+
+ *NextStr=0;
+ for (wchar *SpacePtr=(CmtPtr!=NULL ? CmtPtr:NextStr)-1;SpacePtr>=CurStr;SpacePtr--)
+ {
+ if (*SpacePtr!=' ' && *SpacePtr!='\t')
+ break;
+ *SpacePtr=0;
+ }
+
+ if (Unquote && *CurStr=='\"')
+ {
+ size_t Length=wcslen(CurStr);
+ if (CurStr[Length-1]=='\"')
{
- if (*SpacePtr!=' ' && *SpacePtr!='\t')
- break;
- *SpacePtr=0;
+ CurStr[Length-1]=0;
+ CurStr++;
}
- if (*CurStr)
- {
- if (Unquote && *CurStr=='\"')
- {
- size_t Length=strlen(CurStr);
- if (CurStr[Length-1]=='\"')
- {
- CurStr[Length-1]=0;
- CurStr++;
- }
- }
-#if defined(_WIN_32)
- if (SrcCharset==RCH_OEM)
- OemToChar(CurStr,CurStr);
-#endif
+ }
- bool Expanded=false;
-#if defined(_WIN_32) && !defined(_WIN_CE)
- if (ExpandEnvStr && *CurStr=='%')
- {
- // expanding environment variables in Windows version
-
- char ExpName[NM];
- int ret=ExpandEnvironmentStrings(CurStr,ExpName,ASIZE(ExpName));
- Expanded=ret!=0 && ret<ASIZE(ExpName);
- if (Expanded)
- List->AddString(ExpName);
- }
-#endif
- if (!Expanded)
- List->AddString(CurStr);
- }
- CurStr=NextStr+1;
- while (*CurStr=='\r' || *CurStr=='\n')
- CurStr++;
+ bool Expanded=false;
+#if defined(_WIN_ALL)
+ if (ExpandEnvStr && *CurStr=='%') // Expand environment variables in Windows.
+ {
+ wchar ExpName[NM];
+ *ExpName=0;
+ DWORD Result=ExpandEnvironmentStrings(CurStr,ExpName,ASIZE(ExpName));
+ Expanded=Result!=0 && Result<ASIZE(ExpName);
+ if (Expanded && *ExpName!=0)
+ List->AddString(ExpName);
}
+#endif
+ if (!Expanded && *CurStr!=0)
+ List->AddString(CurStr);
+
+ if (Done)
+ break;
+ CurStr=NextStr+1;
+ while (*CurStr=='\r' || *CurStr=='\n')
+ CurStr++;
}
- return(true);
+ return true;
}
-bool IsUnicode(byte *Data,int Size)
+RAR_CHARSET DetectTextEncoding(const byte *Data,size_t DataSize)
{
- if (Size<4 || Data[0]!=0xff || Data[1]!=0xfe)
- return(false);
- for (int I=2;I<Size;I++)
- if (Data[I]<32 && Data[I]!='\r' && Data[I]!='\n')
- return(true);
- return(false);
+ if (DataSize>3 && Data[0]==0xef && Data[1]==0xbb && Data[2]==0xbf &&
+ IsTextUtf8(Data+3,DataSize-3))
+ return RCH_UTF8;
+
+ bool LittleEndian=DataSize>2 && Data[0]==255 && Data[1]==254;
+ bool BigEndian=DataSize>2 && Data[0]==254 && Data[1]==255;
+
+ if (LittleEndian || BigEndian)
+ for (size_t I=LittleEndian ? 3 : 2;I<DataSize;I+=2)
+ if (Data[I]<32 && Data[I]!='\r' && Data[I]!='\n')
+ return RCH_UNICODE; // High byte in UTF-16 char is found.
+
+ return RCH_DEFAULT;
}
diff --git a/unrar/unrar/filestr.hpp b/unrar/unrar/filestr.hpp
index 927711b..febd0a2 100644
--- a/unrar/unrar/filestr.hpp
+++ b/unrar/unrar/filestr.hpp
@@ -1,9 +1,17 @@
#ifndef _RAR_FILESTR_
#define _RAR_FILESTR_
-bool ReadTextFile(char *Name,StringList *List,bool Config,
- bool AbortOnError=false,RAR_CHARSET SrcCharset=RCH_DEFAULT,
- bool Unquote=false,bool SkipComments=false,
- bool ExpandEnvStr=false);
+bool ReadTextFile(
+ const wchar *Name,
+ StringList *List,
+ bool Config,
+ bool AbortOnError=false,
+ RAR_CHARSET SrcCharset=RCH_DEFAULT,
+ bool Unquote=false,
+ bool SkipComments=false,
+ bool ExpandEnvStr=false
+);
+
+RAR_CHARSET DetectTextEncoding(const byte *Data,size_t DataSize);
#endif
diff --git a/unrar/unrar/find.cpp b/unrar/unrar/find.cpp
index 50a0af6..c9f2c57 100644
--- a/unrar/unrar/find.cpp
+++ b/unrar/unrar/find.cpp
@@ -3,9 +3,8 @@
FindFile::FindFile()
{
*FindMask=0;
- *FindMaskW=0;
FirstCall=true;
-#ifdef _WIN_32
+#ifdef _WIN_ALL
hFind=INVALID_HANDLE_VALUE;
#else
dirp=NULL;
@@ -15,7 +14,7 @@ FindFile::FindFile()
FindFile::~FindFile()
{
-#ifdef _WIN_32
+#ifdef _WIN_ALL
if (hFind!=INVALID_HANDLE_VALUE)
FindClose(hFind);
#else
@@ -25,275 +24,187 @@ FindFile::~FindFile()
}
-void FindFile::SetMask(const char *FindMask)
+void FindFile::SetMask(const wchar *Mask)
{
- strcpy(FindFile::FindMask,FindMask);
- if (*FindMaskW==0)
- CharToWide(FindMask,FindMaskW);
+ wcsncpyz(FindMask,Mask,ASIZE(FindMask));
FirstCall=true;
}
-void FindFile::SetMaskW(const wchar *FindMaskW)
-{
- if (FindMaskW==NULL)
- return;
- strcpyw(FindFile::FindMaskW,FindMaskW);
- if (*FindMask==0)
- WideToChar(FindMaskW,FindMask);
- FirstCall=true;
-}
-
-
-bool FindFile::Next(struct FindData *fd,bool GetSymLink)
+bool FindFile::Next(FindData *fd,bool GetSymLink)
{
fd->Error=false;
if (*FindMask==0)
- return(false);
-#ifdef _WIN_32
+ return false;
+#ifdef _WIN_ALL
if (FirstCall)
{
- if ((hFind=Win32Find(INVALID_HANDLE_VALUE,FindMask,FindMaskW,fd))==INVALID_HANDLE_VALUE)
- return(false);
+ if ((hFind=Win32Find(INVALID_HANDLE_VALUE,FindMask,fd))==INVALID_HANDLE_VALUE)
+ return false;
}
else
- if (Win32Find(hFind,FindMask,FindMaskW,fd)==INVALID_HANDLE_VALUE)
- return(false);
+ if (Win32Find(hFind,FindMask,fd)==INVALID_HANDLE_VALUE)
+ return false;
#else
if (FirstCall)
{
- char DirName[NM];
- strcpy(DirName,FindMask);
+ wchar DirName[NM];
+ wcsncpyz(DirName,FindMask,ASIZE(DirName));
RemoveNameFromPath(DirName);
if (*DirName==0)
- strcpy(DirName,".");
-/*
- else
- {
- int Length=strlen(DirName);
- if (Length>1 && DirName[Length-1]==CPATHDIVIDER && (Length!=3 || !IsDriveDiv(DirName[1])))
- DirName[Length-1]=0;
- }
-*/
- if ((dirp=opendir(DirName))==NULL)
+ wcsncpyz(DirName,L".",ASIZE(DirName));
+ char DirNameA[NM];
+ WideToChar(DirName,DirNameA,ASIZE(DirNameA));
+ if ((dirp=opendir(DirNameA))==NULL)
{
fd->Error=(errno!=ENOENT);
- return(false);
+ return false;
}
}
while (1)
{
+ wchar Name[NM];
struct dirent *ent=readdir(dirp);
if (ent==NULL)
- return(false);
+ return false;
if (strcmp(ent->d_name,".")==0 || strcmp(ent->d_name,"..")==0)
continue;
- if (CmpName(FindMask,ent->d_name,MATCH_NAMES))
+ if (!CharToWide(ent->d_name,Name,ASIZE(Name)))
+ uiMsg(UIERROR_INVALIDNAME,UINULL,Name);
+
+ if (CmpName(FindMask,Name,MATCH_NAMES))
{
- char FullName[NM];
- strcpy(FullName,FindMask);
+ wchar FullName[NM];
+ wcsncpyz(FullName,FindMask,ASIZE(FullName));
*PointToName(FullName)=0;
- if (strlen(FullName)+strlen(ent->d_name)>=ASIZE(FullName)-1)
+ if (wcslen(FullName)+wcslen(Name)>=ASIZE(FullName)-1)
{
-#ifndef SILENT
- Log(NULL,"\n%s%s",FullName,ent->d_name);
- Log(NULL,St(MPathTooLong));
-#endif
- return(false);
+ uiMsg(UIERROR_PATHTOOLONG,FullName,L"",Name);
+ return false;
}
- strcat(FullName,ent->d_name);
- if (!FastFind(FullName,NULL,fd,GetSymLink))
+ wcsncatz(FullName,Name,ASIZE(FullName));
+ if (!FastFind(FullName,fd,GetSymLink))
{
ErrHandler.OpenErrorMsg(FullName);
continue;
}
- strcpy(fd->Name,FullName);
+ wcsncpyz(fd->Name,FullName,ASIZE(fd->Name));
break;
}
}
- *fd->NameW=0;
-#ifdef _APPLE
- if (!LowAscii(fd->Name))
- UtfToWide(fd->Name,fd->NameW,sizeof(fd->NameW));
-#elif defined(UNICODE_SUPPORTED)
- if (!LowAscii(fd->Name) && UnicodeEnabled())
- CharToWide(fd->Name,fd->NameW);
-#endif
#endif
fd->Flags=0;
fd->IsDir=IsDir(fd->FileAttr);
+ fd->IsLink=IsLink(fd->FileAttr);
+
FirstCall=false;
- char *Name=PointToName(fd->Name);
- if (strcmp(Name,".")==0 || strcmp(Name,"..")==0)
- return(Next(fd));
- return(true);
+ wchar *NameOnly=PointToName(fd->Name);
+ if (wcscmp(NameOnly,L".")==0 || wcscmp(NameOnly,L"..")==0)
+ return Next(fd);
+ return true;
}
-bool FindFile::FastFind(const char *FindMask,const wchar *FindMaskW,struct FindData *fd,bool GetSymLink)
+bool FindFile::FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink)
{
fd->Error=false;
#ifndef _UNIX
- if (IsWildcard(FindMask,FindMaskW))
- return(false);
+ if (IsWildcard(FindMask))
+ return false;
#endif
-#ifdef _WIN_32
- HANDLE hFind=Win32Find(INVALID_HANDLE_VALUE,FindMask,FindMaskW,fd);
+#ifdef _WIN_ALL
+ HANDLE hFind=Win32Find(INVALID_HANDLE_VALUE,FindMask,fd);
if (hFind==INVALID_HANDLE_VALUE)
- return(false);
+ return false;
FindClose(hFind);
-#else
+#elif defined(_UNIX)
+ char FindMaskA[NM];
+ WideToChar(FindMask,FindMaskA,ASIZE(FindMaskA));
+
struct stat st;
if (GetSymLink)
{
#ifdef SAVE_LINKS
- if (lstat(FindMask,&st)!=0)
+ if (lstat(FindMaskA,&st)!=0)
#else
- if (stat(FindMask,&st)!=0)
+ if (stat(FindMaskA,&st)!=0)
#endif
{
fd->Error=(errno!=ENOENT);
- return(false);
+ return false;
}
}
else
- if (stat(FindMask,&st)!=0)
+ if (stat(FindMaskA,&st)!=0)
{
fd->Error=(errno!=ENOENT);
- return(false);
+ return false;
}
-#ifdef _DJGPP
- fd->FileAttr=_chmod(FindMask,0);
-#elif defined(_EMX)
- fd->FileAttr=st.st_attr;
-#else
fd->FileAttr=st.st_mode;
-#endif
- fd->IsDir=IsDir(st.st_mode);
fd->Size=st.st_size;
- fd->mtime=st.st_mtime;
- fd->atime=st.st_atime;
- fd->ctime=st.st_ctime;
- fd->FileTime=fd->mtime.GetDos();
- strcpy(fd->Name,FindMask);
- *fd->NameW=0;
-#ifdef _APPLE
- if (!LowAscii(fd->Name))
- UtfToWide(fd->Name,fd->NameW,sizeof(fd->NameW));
-#elif defined(UNICODE_SUPPORTED)
- if (!LowAscii(fd->Name) && UnicodeEnabled())
- CharToWide(fd->Name,fd->NameW);
-#endif
+ File::StatToRarTime(st,&fd->mtime,&fd->ctime,&fd->atime);
+
+ wcsncpyz(fd->Name,FindMask,ASIZE(fd->Name));
#endif
fd->Flags=0;
fd->IsDir=IsDir(fd->FileAttr);
- return(true);
+ fd->IsLink=IsLink(fd->FileAttr);
+
+ return true;
}
-#ifdef _WIN_32
-HANDLE FindFile::Win32Find(HANDLE hFind,const char *Mask,const wchar *MaskW,struct FindData *fd)
+#ifdef _WIN_ALL
+HANDLE FindFile::Win32Find(HANDLE hFind,const wchar *Mask,FindData *fd)
{
-#ifndef _WIN_CE
- if (WinNT())
-#endif
+ WIN32_FIND_DATA FindData;
+ if (hFind==INVALID_HANDLE_VALUE)
{
- wchar WideMask[NM];
- if (MaskW!=NULL && *MaskW!=0)
- strcpyw(WideMask,MaskW);
- else
- CharToWide(Mask,WideMask);
-
- WIN32_FIND_DATAW FindData;
+ hFind=FindFirstFile(Mask,&FindData);
if (hFind==INVALID_HANDLE_VALUE)
{
- hFind=FindFirstFileW(WideMask,&FindData);
- if (hFind==INVALID_HANDLE_VALUE)
- {
- int SysErr=GetLastError();
- fd->Error=(SysErr!=ERROR_FILE_NOT_FOUND &&
- SysErr!=ERROR_PATH_NOT_FOUND &&
- SysErr!=ERROR_NO_MORE_FILES);
- }
+ wchar LongMask[NM];
+ if (GetWinLongPath(Mask,LongMask,ASIZE(LongMask)))
+ hFind=FindFirstFile(LongMask,&FindData);
}
- else
- if (!FindNextFileW(hFind,&FindData))
- {
- hFind=INVALID_HANDLE_VALUE;
- fd->Error=GetLastError()!=ERROR_NO_MORE_FILES;
- }
-
- if (hFind!=INVALID_HANDLE_VALUE)
+ if (hFind==INVALID_HANDLE_VALUE)
{
- strcpyw(fd->NameW,WideMask);
- strcpyw(PointToName(fd->NameW),FindData.cFileName);
- WideToChar(fd->NameW,fd->Name);
- fd->Size=int32to64(FindData.nFileSizeHigh,FindData.nFileSizeLow);
- fd->FileAttr=FindData.dwFileAttributes;
- WideToChar(FindData.cAlternateFileName,fd->ShortName);
- fd->ftCreationTime=FindData.ftCreationTime;
- fd->ftLastAccessTime=FindData.ftLastAccessTime;
- fd->ftLastWriteTime=FindData.ftLastWriteTime;
- fd->mtime=FindData.ftLastWriteTime;
- fd->ctime=FindData.ftCreationTime;
- fd->atime=FindData.ftLastAccessTime;
- fd->FileTime=fd->mtime.GetDos();
-
-#ifndef _WIN_CE
- if (LowAscii(fd->NameW))
- *fd->NameW=0;
-#endif
+ int SysErr=GetLastError();
+ // We must not issue an error for "file not found" and "path not found",
+ // because it is normal to not find anything for wildcard mask when
+ // archiving. Also searching for non-existent file is normal in some
+ // other modules, like WinRAR scanning for winrar_theme_description.txt
+ // to check if any themes are available.
+ fd->Error=SysErr!=ERROR_FILE_NOT_FOUND &&
+ SysErr!=ERROR_PATH_NOT_FOUND &&
+ SysErr!=ERROR_NO_MORE_FILES;
}
}
-#ifndef _WIN_CE
else
- {
- char CharMask[NM];
- if (Mask!=NULL && *Mask!=0)
- strcpy(CharMask,Mask);
- else
- WideToChar(MaskW,CharMask);
-
- WIN32_FIND_DATA FindData;
- if (hFind==INVALID_HANDLE_VALUE)
+ if (!FindNextFile(hFind,&FindData))
{
- hFind=FindFirstFile(CharMask,&FindData);
- if (hFind==INVALID_HANDLE_VALUE)
- {
- int SysErr=GetLastError();
- fd->Error=SysErr!=ERROR_FILE_NOT_FOUND && SysErr!=ERROR_PATH_NOT_FOUND;
- }
+ hFind=INVALID_HANDLE_VALUE;
+ fd->Error=GetLastError()!=ERROR_NO_MORE_FILES;
}
- else
- if (!FindNextFile(hFind,&FindData))
- {
- hFind=INVALID_HANDLE_VALUE;
- fd->Error=GetLastError()!=ERROR_NO_MORE_FILES;
- }
- if (hFind!=INVALID_HANDLE_VALUE)
- {
- strcpy(fd->Name,CharMask);
- strcpy(PointToName(fd->Name),FindData.cFileName);
- CharToWide(fd->Name,fd->NameW);
- fd->Size=int32to64(FindData.nFileSizeHigh,FindData.nFileSizeLow);
- fd->FileAttr=FindData.dwFileAttributes;
- strcpy(fd->ShortName,FindData.cAlternateFileName);
- fd->ftCreationTime=FindData.ftCreationTime;
- fd->ftLastAccessTime=FindData.ftLastAccessTime;
- fd->ftLastWriteTime=FindData.ftLastWriteTime;
- fd->mtime=FindData.ftLastWriteTime;
- fd->ctime=FindData.ftCreationTime;
- fd->atime=FindData.ftLastAccessTime;
- fd->FileTime=fd->mtime.GetDos();
- if (LowAscii(fd->Name))
- *fd->NameW=0;
- }
+ if (hFind!=INVALID_HANDLE_VALUE)
+ {
+ wcsncpyz(fd->Name,Mask,ASIZE(fd->Name));
+ SetName(fd->Name,FindData.cFileName,ASIZE(fd->Name));
+ fd->Size=INT32TO64(FindData.nFileSizeHigh,FindData.nFileSizeLow);
+ fd->FileAttr=FindData.dwFileAttributes;
+ fd->ftCreationTime=FindData.ftCreationTime;
+ fd->ftLastAccessTime=FindData.ftLastAccessTime;
+ fd->ftLastWriteTime=FindData.ftLastWriteTime;
+ fd->mtime.SetWinFT(&FindData.ftLastWriteTime);
+ fd->ctime.SetWinFT(&FindData.ftCreationTime);
+ fd->atime.SetWinFT(&FindData.ftLastAccessTime);
+
+
}
-#endif
fd->Flags=0;
- return(hFind);
+ return hFind;
}
#endif
diff --git a/unrar/unrar/find.hpp b/unrar/unrar/find.hpp
index 3320d50..250637f 100644
--- a/unrar/unrar/find.hpp
+++ b/unrar/unrar/find.hpp
@@ -2,22 +2,20 @@
#define _RAR_FINDDATA_
enum FINDDATA_FLAGS {
- FDDF_SECONDDIR=1 // second entry of directory in SCAN_GETDIRSTWICE ScanTree mode
+ FDDF_SECONDDIR=1 // Second encounter of same directory in SCAN_GETDIRSTWICE ScanTree mode.
};
struct FindData
{
- char Name[NM];
- wchar NameW[NM];
- Int64 Size;
+ wchar Name[NM];
+ uint64 Size;
uint FileAttr;
- uint FileTime;
bool IsDir;
+ bool IsLink;
RarTime mtime;
RarTime ctime;
RarTime atime;
-#ifdef _WIN_32
- char ShortName[NM];
+#ifdef _WIN_ALL
FILETIME ftCreationTime;
FILETIME ftLastAccessTime;
FILETIME ftLastWriteTime;
@@ -29,14 +27,13 @@ struct FindData
class FindFile
{
private:
-#ifdef _WIN_32
- static HANDLE Win32Find(HANDLE hFind,const char *Mask,const wchar *MaskW,struct FindData *fd);
+#ifdef _WIN_ALL
+ static HANDLE Win32Find(HANDLE hFind,const wchar *Mask,FindData *fd);
#endif
- char FindMask[NM];
- wchar FindMaskW[NM];
+ wchar FindMask[NM];
bool FirstCall;
-#ifdef _WIN_32
+#ifdef _WIN_ALL
HANDLE hFind;
#else
DIR *dirp;
@@ -44,10 +41,9 @@ class FindFile
public:
FindFile();
~FindFile();
- void SetMask(const char *FindMask);
- void SetMaskW(const wchar *FindMaskW);
- bool Next(struct FindData *fd,bool GetSymLink=false);
- static bool FastFind(const char *FindMask,const wchar *FindMaskW,struct FindData *fd,bool GetSymLink=false);
+ void SetMask(const wchar *Mask);
+ bool Next(FindData *fd,bool GetSymLink=false);
+ static bool FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink=false);
};
#endif
diff --git a/unrar/unrar/getbits.cpp b/unrar/unrar/getbits.cpp
index 71ecfc8..5d5ad2b 100644
--- a/unrar/unrar/getbits.cpp
+++ b/unrar/unrar/getbits.cpp
@@ -1,24 +1,52 @@
#include "rar.hpp"
-BitInput::BitInput()
+BitInput::BitInput(bool AllocBuffer)
{
- InBuf=new byte[MAX_SIZE];
+ ExternalBuffer=false;
+ if (AllocBuffer)
+ {
+ // getbits*() attempt to read data from InAddr, ... InAddr+4 positions.
+ // So let's allocate 4 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 4 bytes, which contents we do not need.
+ size_t BufSize=MAX_SIZE+4;
+ 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()
{
- delete[] InBuf;
+ if (!ExternalBuffer)
+ delete[] InBuf;
}
-void BitInput::faddbits(int Bits)
+void BitInput::faddbits(uint Bits)
{
+ // Function wrapped version of inline addbits to save code size.
addbits(Bits);
}
-unsigned int BitInput::fgetbits()
+uint BitInput::fgetbits()
+{
+ // Function wrapped version of inline getbits to save code size.
+ return getbits();
+}
+
+
+void BitInput::SetExternalBuffer(byte *Buf)
{
- return(getbits());
+ if (InBuf!=NULL && !ExternalBuffer)
+ delete[] InBuf;
+ InBuf=Buf;
+ ExternalBuffer=true;
}
+
diff --git a/unrar/unrar/getbits.hpp b/unrar/unrar/getbits.hpp
index eff4daf..00acbea 100644
--- a/unrar/unrar/getbits.hpp
+++ b/unrar/unrar/getbits.hpp
@@ -4,35 +4,77 @@
class BitInput
{
public:
- enum BufferSize {MAX_SIZE=0x8000};
- protected:
- int InAddr,InBit;
+ 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();
+ BitInput(bool AllocBuffer);
~BitInput();
- byte *InBuf;
+ byte *InBuf; // Dynamically allocated input buffer.
void InitBitInput()
{
InAddr=InBit=0;
}
- void addbits(int Bits)
+
+ // Move forward by 'Bits' bits.
+ void addbits(uint Bits)
{
Bits+=InBit;
InAddr+=Bits>>3;
InBit=Bits&7;
}
- unsigned int getbits()
+
+ // Return 16 bits from current position in the buffer.
+ // Bit at (InAddr,InBit) has the highest position in returning data.
+ uint getbits()
{
- unsigned int BitField=(uint)InBuf[InAddr] << 16;
+#if defined(LITTLE_ENDIAN) && defined(ALLOW_MISALIGNED)
+ uint32 BitField=*(uint32*)(InBuf+InAddr);
+ BitField=ByteSwap32(BitField);
+ BitField >>= (16-InBit);
+#else
+ uint BitField=(uint)InBuf[InAddr] << 16;
BitField|=(uint)InBuf[InAddr+1] << 8;
BitField|=(uint)InBuf[InAddr+2];
BitField >>= (8-InBit);
- return(BitField & 0xffff);
+#endif
+ 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()
+ {
+#if defined(LITTLE_ENDIAN) && defined(ALLOW_MISALIGNED)
+ uint32 BitField=*(uint32*)(InBuf+InAddr);
+ BitField=ByteSwap32(BitField);
+#else
+ uint BitField=(uint)InBuf[InAddr] << 24;
+ BitField|=(uint)InBuf[InAddr+1] << 16;
+ BitField|=(uint)InBuf[InAddr+2] << 8;
+ BitField|=(uint)InBuf[InAddr+3];
+#endif
+ BitField <<= InBit;
+ BitField|=(uint)InBuf[InAddr+4] >> (8-InBit);
+ return BitField & 0xffffffff;
}
- void faddbits(int Bits);
- unsigned int fgetbits();
- bool Overflow(int IncPtr) {return(InAddr+IncPtr>=MAX_SIZE);}
+
+ 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
diff --git a/unrar/unrar/global.cpp b/unrar/unrar/global.cpp
index 593a057..771f000 100644
--- a/unrar/unrar/global.cpp
+++ b/unrar/unrar/global.cpp
@@ -1,4 +1,7 @@
#define INCLUDEGLOBAL
+#ifdef _MSC_VER
+#pragma hdrstop
+#endif
#include "rar.hpp"
diff --git a/unrar/unrar/hardlinks.cpp b/unrar/unrar/hardlinks.cpp
new file mode 100644
index 0000000..171b5fa
--- /dev/null
+++ b/unrar/unrar/hardlinks.cpp
@@ -0,0 +1,37 @@
+bool ExtractHardlink(CommandData *Cmd,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize)
+{
+ if (!FileExist(NameExisting))
+ {
+ uiMsg(UIERROR_HLINKCREATE,NameNew);
+ uiMsg(UIERROR_NOLINKTARGET);
+ ErrHandler.SetErrorCode(RARX_CREATE);
+ return false;
+ }
+ CreatePath(NameNew,true,Cmd->DisableNames);
+
+#ifdef _WIN_ALL
+ bool Success=CreateHardLink(NameNew,NameExisting,NULL)!=0;
+ if (!Success)
+ {
+ uiMsg(UIERROR_HLINKCREATE,NameNew);
+ ErrHandler.SysErrMsg();
+ ErrHandler.SetErrorCode(RARX_CREATE);
+ }
+ return Success;
+#elif defined(_UNIX)
+ char NameExistingA[NM],NameNewA[NM];
+ WideToChar(NameExisting,NameExistingA,ASIZE(NameExistingA));
+ WideToChar(NameNew,NameNewA,ASIZE(NameNewA));
+ bool Success=link(NameExistingA,NameNewA)==0;
+ if (!Success)
+ {
+ uiMsg(UIERROR_HLINKCREATE,NameNew);
+ ErrHandler.SysErrMsg();
+ ErrHandler.SetErrorCode(RARX_CREATE);
+ }
+ return Success;
+#else
+ return false;
+#endif
+}
+
diff --git a/unrar/unrar/hash.cpp b/unrar/unrar/hash.cpp
new file mode 100644
index 0000000..106cc60
--- /dev/null
+++ b/unrar/unrar/hash.cpp
@@ -0,0 +1,135 @@
+#include "rar.hpp"
+
+void HashValue::Init(HASH_TYPE Type)
+{
+ HashValue::Type=Type;
+
+ // Zero length data CRC32 is 0. It is important to set it when creating
+ // headers with no following data like directories or symlinks.
+ if (Type==HASH_RAR14 || Type==HASH_CRC32)
+ CRC32=0;
+ if (Type==HASH_BLAKE2)
+ {
+ // dd0e891776933f43c7d032b08a917e25741f8aa9a12c12e1cac8801500f2ca4f
+ // is BLAKE2sp hash of empty data. We init the structure to this value,
+ // so if we create a file or service header with no following data like
+ // "file copy" or "symlink", we set the checksum to proper value avoiding
+ // additional header type or size checks when extracting.
+ static byte EmptyHash[32]={
+ 0xdd, 0x0e, 0x89, 0x17, 0x76, 0x93, 0x3f, 0x43,
+ 0xc7, 0xd0, 0x32, 0xb0, 0x8a, 0x91, 0x7e, 0x25,
+ 0x74, 0x1f, 0x8a, 0xa9, 0xa1, 0x2c, 0x12, 0xe1,
+ 0xca, 0xc8, 0x80, 0x15, 0x00, 0xf2, 0xca, 0x4f
+ };
+ memcpy(Digest,EmptyHash,sizeof(Digest));
+ }
+}
+
+
+bool HashValue::operator == (const HashValue &cmp) const
+{
+ if (Type==HASH_NONE || cmp.Type==HASH_NONE)
+ return true;
+ if (Type==HASH_RAR14 && cmp.Type==HASH_RAR14 ||
+ Type==HASH_CRC32 && cmp.Type==HASH_CRC32)
+ return CRC32==cmp.CRC32;
+ if (Type==HASH_BLAKE2 && cmp.Type==HASH_BLAKE2)
+ return memcmp(Digest,cmp.Digest,sizeof(Digest))==0;
+ return false;
+}
+
+
+DataHash::DataHash()
+{
+ blake2ctx=NULL;
+ HashType=HASH_NONE;
+#ifdef RAR_SMP
+ ThPool=NULL;
+ MaxThreads=0;
+#endif
+}
+
+
+DataHash::~DataHash()
+{
+#ifdef RAR_SMP
+ delete ThPool;
+#endif
+ cleandata(&CurCRC32, sizeof(CurCRC32));
+ if (blake2ctx!=NULL)
+ {
+ cleandata(blake2ctx, sizeof(blake2sp_state));
+ delete blake2ctx;
+ }
+}
+
+
+void DataHash::Init(HASH_TYPE Type,uint MaxThreads)
+{
+ if (blake2ctx==NULL)
+ blake2ctx=new blake2sp_state;
+ HashType=Type;
+ if (Type==HASH_RAR14)
+ CurCRC32=0;
+ if (Type==HASH_CRC32)
+ CurCRC32=0xffffffff; // Initial CRC32 value.
+ if (Type==HASH_BLAKE2)
+ blake2sp_init(blake2ctx);
+#ifdef RAR_SMP
+ DataHash::MaxThreads=Min(MaxThreads,MaxHashThreads);
+#endif
+}
+
+
+void DataHash::Update(const void *Data,size_t DataSize)
+{
+#ifndef SFX_MODULE
+ if (HashType==HASH_RAR14)
+ CurCRC32=Checksum14((ushort)CurCRC32,Data,DataSize);
+#endif
+ if (HashType==HASH_CRC32)
+ CurCRC32=CRC32(CurCRC32,Data,DataSize);
+
+ if (HashType==HASH_BLAKE2)
+ {
+#ifdef RAR_SMP
+ if (MaxThreads>1 && ThPool==NULL)
+ ThPool=new ThreadPool(BLAKE2_THREADS_NUMBER);
+ blake2ctx->ThPool=ThPool;
+ blake2ctx->MaxThreads=MaxThreads;
+#endif
+ blake2sp_update( blake2ctx, (byte *)Data, DataSize);
+ }
+}
+
+
+void DataHash::Result(HashValue *Result)
+{
+ Result->Type=HashType;
+ if (HashType==HASH_RAR14)
+ Result->CRC32=CurCRC32;
+ if (HashType==HASH_CRC32)
+ Result->CRC32=CurCRC32^0xffffffff;
+ if (HashType==HASH_BLAKE2)
+ {
+ // Preserve the original context, so we can continue hashing if necessary.
+ blake2sp_state res=*blake2ctx;
+ blake2sp_final(&res,Result->Digest);
+ }
+}
+
+
+uint DataHash::GetCRC32()
+{
+ return HashType==HASH_CRC32 ? CurCRC32^0xffffffff : 0;
+}
+
+
+bool DataHash::Cmp(HashValue *CmpValue,byte *Key)
+{
+ HashValue Final;
+ Result(&Final);
+ if (Key!=NULL)
+ ConvertHashToMAC(&Final,Key);
+ return Final==*CmpValue;
+}
diff --git a/unrar/unrar/hash.hpp b/unrar/unrar/hash.hpp
new file mode 100644
index 0000000..6315680
--- /dev/null
+++ b/unrar/unrar/hash.hpp
@@ -0,0 +1,58 @@
+#ifndef _RAR_DATAHASH_
+#define _RAR_DATAHASH_
+
+enum HASH_TYPE {HASH_NONE,HASH_RAR14,HASH_CRC32,HASH_BLAKE2};
+
+struct HashValue
+{
+ void Init(HASH_TYPE Type);
+
+ // Use the const member, so types on both sides of "==" match.
+ // Otherwise clang -std=c++20 issues "ambiguity is between a regular call
+ // to this operator and a call with the argument order reversed" warning.
+ bool operator == (const HashValue &cmp) const;
+
+ // Not actually used now. Const member for same reason as operator == above.
+ bool operator != (const HashValue &cmp) const {return !(*this==cmp);}
+
+ HASH_TYPE Type;
+ union
+ {
+ uint CRC32;
+ byte Digest[SHA256_DIGEST_SIZE];
+ };
+};
+
+
+#ifdef RAR_SMP
+class ThreadPool;
+class DataHash;
+#endif
+
+
+class DataHash
+{
+ private:
+ HASH_TYPE HashType;
+ uint CurCRC32;
+ blake2sp_state *blake2ctx;
+
+#ifdef RAR_SMP
+ ThreadPool *ThPool;
+
+ uint MaxThreads;
+ // Upper limit for maximum threads to prevent wasting threads in pool.
+ static const uint MaxHashThreads=8;
+#endif
+ public:
+ DataHash();
+ ~DataHash();
+ void Init(HASH_TYPE Type,uint MaxThreads);
+ void Update(const void *Data,size_t DataSize);
+ void Result(HashValue *Result);
+ uint GetCRC32();
+ bool Cmp(HashValue *CmpValue,byte *Key);
+ HASH_TYPE Type() {return HashType;}
+};
+
+#endif
diff --git a/unrar/unrar/headers.cpp b/unrar/unrar/headers.cpp
new file mode 100644
index 0000000..b441376
--- /dev/null
+++ b/unrar/unrar/headers.cpp
@@ -0,0 +1,53 @@
+#include "rar.hpp"
+
+void FileHeader::Reset(size_t SubDataSize)
+{
+ SubData.Alloc(SubDataSize);
+ BaseBlock::Reset();
+ FileHash.Init(HASH_NONE);
+ mtime.Reset();
+ atime.Reset();
+ ctime.Reset();
+ SplitBefore=false;
+ SplitAfter=false;
+
+ UnknownUnpSize=0;
+
+ SubFlags=0; // Important for RAR 3.0 subhead.
+
+ CryptMethod=CRYPT_NONE;
+ Encrypted=false;
+ SaltSet=false;
+ UsePswCheck=false;
+ UseHashKey=false;
+ Lg2Count=0;
+
+ Solid=false;
+ Dir=false;
+ WinSize=0;
+ Inherited=false;
+ SubBlock=false;
+ CommentInHeader=false;
+ Version=false;
+ LargeFile=false;
+
+ RedirType=FSREDIR_NONE;
+ DirTarget=false;
+ UnixOwnerSet=false;
+}
+
+
+FileHeader& FileHeader::operator = (FileHeader &hd)
+{
+ SubData.Reset();
+ memcpy(this,&hd,sizeof(*this));
+ SubData.CleanData();
+ SubData=hd.SubData;
+ return *this;
+}
+
+
+void MainHeader::Reset()
+{
+ *this={};
+}
diff --git a/unrar/unrar/headers.hpp b/unrar/unrar/headers.hpp
index 94e8aa0..5984f99 100644
--- a/unrar/unrar/headers.hpp
+++ b/unrar/unrar/headers.hpp
@@ -1,154 +1,158 @@
#ifndef _RAR_HEADERS_
#define _RAR_HEADERS_
-#define SIZEOF_MARKHEAD 7
-#define SIZEOF_OLDMHD 7
-#define SIZEOF_NEWMHD 13
-#define SIZEOF_OLDLHD 21
-#define SIZEOF_NEWLHD 32
-#define SIZEOF_SHORTBLOCKHEAD 7
+#define SIZEOF_MARKHEAD3 7 // Size of RAR 4.x archive mark header.
+#define SIZEOF_MAINHEAD14 7 // Size of RAR 1.4 main archive header.
+#define SIZEOF_MAINHEAD3 13 // Size of RAR 4.x main archive header.
+#define SIZEOF_FILEHEAD14 21 // Size of RAR 1.4 file header.
+#define SIZEOF_FILEHEAD3 32 // Size of RAR 3.0 file header.
+#define SIZEOF_SHORTBLOCKHEAD 7 // Smallest RAR 4.x block size.
#define SIZEOF_LONGBLOCKHEAD 11
#define SIZEOF_SUBBLOCKHEAD 14
#define SIZEOF_COMMHEAD 13
#define SIZEOF_PROTECTHEAD 26
-#define SIZEOF_AVHEAD 14
-#define SIZEOF_SIGNHEAD 15
-#define SIZEOF_UOHEAD 18
-#define SIZEOF_MACHEAD 22
-#define SIZEOF_EAHEAD 24
-#define SIZEOF_BEEAHEAD 24
#define SIZEOF_STREAMHEAD 26
-#define PACK_VER 29
-#define PACK_CRYPT_VER 29
-#define UNP_VER 36
-#define CRYPT_VER 29
-#define AV_VER 20
-#define PROTECT_VER 20
-
-#define MHD_VOLUME 0x0001
-#define MHD_COMMENT 0x0002
-#define MHD_LOCK 0x0004
-#define MHD_SOLID 0x0008
-#define MHD_PACK_COMMENT 0x0010
-#define MHD_NEWNUMBERING 0x0010
-#define MHD_AV 0x0020
-#define MHD_PROTECT 0x0040
-#define MHD_PASSWORD 0x0080
-#define MHD_FIRSTVOLUME 0x0100
-#define MHD_ENCRYPTVER 0x0200
-
-#define LHD_SPLIT_BEFORE 0x0001
-#define LHD_SPLIT_AFTER 0x0002
-#define LHD_PASSWORD 0x0004
-#define LHD_COMMENT 0x0008
-#define LHD_SOLID 0x0010
-
-#define LHD_WINDOWMASK 0x00e0
-#define LHD_WINDOW64 0x0000
-#define LHD_WINDOW128 0x0020
-#define LHD_WINDOW256 0x0040
-#define LHD_WINDOW512 0x0060
-#define LHD_WINDOW1024 0x0080
-#define LHD_WINDOW2048 0x00a0
-#define LHD_WINDOW4096 0x00c0
-#define LHD_DIRECTORY 0x00e0
-
-#define LHD_LARGE 0x0100
-#define LHD_UNICODE 0x0200
-#define LHD_SALT 0x0400
-#define LHD_VERSION 0x0800
-#define LHD_EXTTIME 0x1000
-#define LHD_EXTFLAGS 0x2000
-
-#define SKIP_IF_UNKNOWN 0x4000
-#define LONG_BLOCK 0x8000
-
-#define EARC_NEXT_VOLUME 0x0001 // not last volume
-#define EARC_DATACRC 0x0002 // store CRC32 of RAR archive (now used only in volumes)
-#define EARC_REVSPACE 0x0004 // reserve space for end of REV file 7 byte record
-#define EARC_VOLNUMBER 0x0008 // store a number of current volume
+#define VER_PACK 29U
+#define VER_PACK5 50U // It is stored as 0, but we subtract 50 when saving an archive.
+#define VER_UNPACK 29U
+#define VER_UNPACK5 50U // It is stored as 0, but we add 50 when reading an archive.
+#define VER_UNKNOWN 9999U // Just some large value.
+
+#define MHD_VOLUME 0x0001U
+
+// Old style main archive comment embed into main archive header. Must not
+// be used in new archives anymore.
+#define MHD_COMMENT 0x0002U
+
+#define MHD_LOCK 0x0004U
+#define MHD_SOLID 0x0008U
+#define MHD_PACK_COMMENT 0x0010U
+#define MHD_NEWNUMBERING 0x0010U
+#define MHD_AV 0x0020U
+#define MHD_PROTECT 0x0040U
+#define MHD_PASSWORD 0x0080U
+#define MHD_FIRSTVOLUME 0x0100U
+
+#define LHD_SPLIT_BEFORE 0x0001U
+#define LHD_SPLIT_AFTER 0x0002U
+#define LHD_PASSWORD 0x0004U
+
+// Old style file comment embed into file header. Must not be used
+// in new archives anymore.
+#define LHD_COMMENT 0x0008U
+
+// For non-file subheaders it denotes 'subblock having a parent file' flag.
+#define LHD_SOLID 0x0010U
+
+
+#define LHD_WINDOWMASK 0x00e0U
+#define LHD_WINDOW64 0x0000U
+#define LHD_WINDOW128 0x0020U
+#define LHD_WINDOW256 0x0040U
+#define LHD_WINDOW512 0x0060U
+#define LHD_WINDOW1024 0x0080U
+#define LHD_WINDOW2048 0x00a0U
+#define LHD_WINDOW4096 0x00c0U
+#define LHD_DIRECTORY 0x00e0U
+
+#define LHD_LARGE 0x0100U
+#define LHD_UNICODE 0x0200U
+#define LHD_SALT 0x0400U
+#define LHD_VERSION 0x0800U
+#define LHD_EXTTIME 0x1000U
+
+#define SKIP_IF_UNKNOWN 0x4000U
+#define LONG_BLOCK 0x8000U
+
+#define EARC_NEXT_VOLUME 0x0001U // Not last volume.
+#define EARC_DATACRC 0x0002U // Store CRC32 of RAR archive (now is used only in volumes).
+#define EARC_REVSPACE 0x0004U // Reserve space for end of REV file 7 byte record.
+#define EARC_VOLNUMBER 0x0008U // Store a number of current volume.
enum HEADER_TYPE {
- MARK_HEAD=0x72,MAIN_HEAD=0x73,FILE_HEAD=0x74,COMM_HEAD=0x75,AV_HEAD=0x76,
- SUB_HEAD=0x77,PROTECT_HEAD=0x78,SIGN_HEAD=0x79,NEWSUB_HEAD=0x7a,
- ENDARC_HEAD=0x7b
+ // RAR 5.0 header types.
+ HEAD_MARK=0x00, HEAD_MAIN=0x01, HEAD_FILE=0x02, HEAD_SERVICE=0x03,
+ HEAD_CRYPT=0x04, HEAD_ENDARC=0x05, HEAD_UNKNOWN=0xff,
+
+ // RAR 1.5 - 4.x header types.
+ HEAD3_MARK=0x72,HEAD3_MAIN=0x73,HEAD3_FILE=0x74,HEAD3_CMT=0x75,
+ HEAD3_AV=0x76,HEAD3_OLDSERVICE=0x77,HEAD3_PROTECT=0x78,HEAD3_SIGN=0x79,
+ HEAD3_SERVICE=0x7a,HEAD3_ENDARC=0x7b
};
+
+// RAR 2.9 and earlier.
enum { EA_HEAD=0x100,UO_HEAD=0x101,MAC_HEAD=0x102,BEEA_HEAD=0x103,
NTACL_HEAD=0x104,STREAM_HEAD=0x105 };
+
+// Internal implementation, depends on archive format version.
enum HOST_SYSTEM {
+ // RAR 5.0 host OS
+ HOST5_WINDOWS=0,HOST5_UNIX=1,
+
+ // RAR 3.0 host OS.
HOST_MSDOS=0,HOST_OS2=1,HOST_WIN32=2,HOST_UNIX=3,HOST_MACOS=4,
HOST_BEOS=5,HOST_MAX
};
-#define SUBHEAD_TYPE_CMT "CMT"
-#define SUBHEAD_TYPE_ACL "ACL"
-#define SUBHEAD_TYPE_STREAM "STM"
-#define SUBHEAD_TYPE_UOWNER "UOW"
-#define SUBHEAD_TYPE_AV "AV"
-#define SUBHEAD_TYPE_RR "RR"
-#define SUBHEAD_TYPE_OS2EA "EA2"
-#define SUBHEAD_TYPE_BEOSEA "EABE"
-
-/* new file inherits a subblock when updating a host file */
-#define SUBHEAD_FLAGS_INHERITED 0x80000000
+// Unified archive format independent implementation.
+enum HOST_SYSTEM_TYPE {
+ HSYS_WINDOWS, HSYS_UNIX, HSYS_UNKNOWN
+};
-#define SUBHEAD_FLAGS_CMT_UNICODE 0x00000001
-struct OldMainHeader
-{
- byte Mark[4];
- ushort HeadSize;
- byte Flags;
+// We also use these values in extra field, so do not modify them.
+enum FILE_SYSTEM_REDIRECT {
+ FSREDIR_NONE=0, FSREDIR_UNIXSYMLINK, FSREDIR_WINSYMLINK, FSREDIR_JUNCTION,
+ FSREDIR_HARDLINK, FSREDIR_FILECOPY
};
-struct OldFileHeader
-{
- uint PackSize;
- uint UnpSize;
- ushort FileCRC;
- ushort HeadSize;
- uint FileTime;
- byte FileAttr;
- byte Flags;
- byte UnpVer;
- byte NameSize;
- byte Method;
-};
+#define SUBHEAD_TYPE_CMT L"CMT"
+#define SUBHEAD_TYPE_QOPEN L"QO"
+#define SUBHEAD_TYPE_ACL L"ACL"
+#define SUBHEAD_TYPE_STREAM L"STM"
+#define SUBHEAD_TYPE_UOWNER L"UOW"
+#define SUBHEAD_TYPE_AV L"AV"
+#define SUBHEAD_TYPE_RR L"RR"
+#define SUBHEAD_TYPE_OS2EA L"EA2"
+
+/* new file inherits a subblock when updating a host file */
+#define SUBHEAD_FLAGS_INHERITED 0x80000000
+
+#define SUBHEAD_FLAGS_CMT_UNICODE 0x00000001
struct MarkHeader
{
- byte Mark[7];
+ byte Mark[8];
+
+ // Following fields are virtual and not present in real blocks.
+ uint HeadSize;
};
struct BaseBlock
{
- ushort HeadCRC;
- HEADER_TYPE HeadType;//byte
- ushort Flags;
- ushort HeadSize;
+ uint HeadCRC; // 'ushort' for RAR 1.5.
+ HEADER_TYPE HeaderType; // 1 byte for RAR 1.5.
+ uint Flags; // 'ushort' for RAR 1.5.
+ uint HeadSize; // 'ushort' for RAR 1.5, up to 2 MB for RAR 5.0.
- bool IsSubBlock()
+ bool SkipIfUnknown;
+
+ void Reset()
{
- if (HeadType==SUB_HEAD)
- return(true);
- if (HeadType==NEWSUB_HEAD && (Flags & LHD_SOLID)!=0)
- return(true);
- return(false);
+ SkipIfUnknown=false;
}
};
+
struct BlockHeader:BaseBlock
{
- union {
- uint DataSize;
- uint PackSize;
- };
+ uint DataSize;
};
@@ -156,75 +160,145 @@ struct MainHeader:BaseBlock
{
ushort HighPosAV;
uint PosAV;
- byte EncryptVer;
+ bool CommentInHeader;
+ bool PackComment; // For RAR 1.4 archive format only.
+ bool Locator;
+ uint64 QOpenOffset; // Offset of quick list record.
+ uint64 QOpenMaxSize; // Maximum size of QOpen offset in locator extra field.
+ uint64 RROffset; // Offset of recovery record.
+ uint64 RRMaxSize; // Maximum size of RR offset in locator extra field.
+ size_t MetaNameMaxSize; // Maximum size of archive name in metadata extra field.
+ std::wstring OrigName; // Original archive name.
+ RarTime OrigTime; // Original archive time.
+
+ void Reset();
};
-#define SALT_SIZE 8
-
struct FileHeader:BlockHeader
{
- uint UnpSize;
byte HostOS;
- uint FileCRC;
- uint FileTime;
- byte UnpVer;
+ uint UnpVer; // It is 1 byte in RAR29 and bit field in RAR5.
byte Method;
- ushort NameSize;
union {
uint FileAttr;
uint SubFlags;
};
-/* optional */
- uint HighPackSize;
- uint HighUnpSize;
-/* names */
- char FileName[NM];
- wchar FileNameW[NM];
-/* optional */
+ wchar FileName[NM];
+
Array<byte> SubData;
- byte Salt[SALT_SIZE];
RarTime mtime;
RarTime ctime;
RarTime atime;
- RarTime arctime;
-/* dummy */
- Int64 FullPackSize;
- Int64 FullUnpSize;
- void Clear(int SubDataSize)
- {
- SubData.Alloc(SubDataSize);
- Flags=LONG_BLOCK;
- SubFlags=0;
- }
+ int64 PackSize;
+ int64 UnpSize;
+ int64 MaxSize; // Reserve packed and unpacked size bytes for vint of this size.
+
+ HashValue FileHash;
+
+ uint FileFlags;
+
+ bool SplitBefore;
+ bool SplitAfter;
+
+ bool UnknownUnpSize;
- bool CmpName(const char *Name)
+ bool Encrypted;
+ CRYPT_METHOD CryptMethod;
+ bool SaltSet;
+ byte Salt[SIZE_SALT50];
+ byte InitV[SIZE_INITV];
+ bool UsePswCheck;
+ byte PswCheck[SIZE_PSWCHECK];
+
+ // Use HMAC calculated from HashKey and checksum instead of plain checksum.
+ bool UseHashKey;
+
+ // Key to convert checksum to HMAC. Derived from password with PBKDF2
+ // using additional iterations.
+ byte HashKey[SHA256_DIGEST_SIZE];
+
+ uint Lg2Count; // Log2 of PBKDF2 repetition count.
+
+ bool Solid;
+ bool Dir;
+ bool CommentInHeader; // RAR 2.0 file comment.
+ bool Version; // name.ext;ver file name containing the version number.
+ size_t WinSize;
+ bool Inherited; // New file inherits a subblock when updating a host file (for subblocks only).
+
+ // 'true' if file sizes use 8 bytes instead of 4. Not used in RAR 5.0.
+ bool LargeFile;
+
+ // 'true' for HEAD_SERVICE block, which is a child of preceding file block.
+ // RAR 4.x uses 'solid' flag to indicate children subheader blocks in archives.
+ bool SubBlock;
+
+ HOST_SYSTEM_TYPE HSType;
+
+ FILE_SYSTEM_REDIRECT RedirType;
+ wchar RedirName[NM];
+ bool DirTarget;
+
+ bool UnixOwnerSet,UnixOwnerNumeric,UnixGroupNumeric;
+ char UnixOwnerName[256],UnixGroupName[256];
+#ifdef _UNIX
+ uid_t UnixOwnerID;
+ gid_t UnixGroupID;
+#else // Need these Unix fields in Windows too for 'list' command.
+ uint UnixOwnerID;
+ uint UnixGroupID;
+#endif
+
+ void Reset(size_t SubDataSize=0);
+
+ bool CmpName(const wchar *Name)
{
- return(strcmp(FileName,Name)==0);
+ return(wcscmp(FileName,Name)==0);
}
- FileHeader& operator = (FileHeader &hd)
+ FileHeader& operator = (FileHeader &hd);
+};
+
+
+struct EndArcHeader:BaseBlock
+{
+ // Optional CRC32 of entire archive up to start of EndArcHeader block.
+ // Present in RAR 4.x archives if EARC_DATACRC flag is set.
+ uint ArcDataCRC;
+
+ uint VolNumber; // Optional number of current volume.
+
+ // 7 additional zero bytes can be stored here if EARC_REVSPACE is set.
+
+ bool NextVolume; // Not last volume.
+ bool DataCRC;
+ bool RevSpace;
+ bool StoreVolNumber;
+ void Reset()
{
- SubData.Reset();
- memcpy(this,&hd,sizeof(*this));
- SubData.CleanData();
- SubData=hd.SubData;
- return(*this);
+ BaseBlock::Reset();
+ NextVolume=false;
+ DataCRC=false;
+ RevSpace=false;
+ StoreVolNumber=false;
}
};
-struct EndArcHeader:BaseBlock
+struct CryptHeader:BaseBlock
{
- uint ArcDataCRC; // optional archive CRC32
- ushort VolNumber; // optional current volume number
+ bool UsePswCheck;
+ uint Lg2Count; // Log2 of PBKDF2 repetition count.
+ byte Salt[SIZE_SALT50];
+ byte PswCheck[SIZE_PSWCHECK];
};
// SubBlockHeader and its successors were used in RAR 2.x format.
-// RAR 3.x uses FileHeader with NEWSUB_HEAD HeadType for subblocks.
+// RAR 4.x uses FileHeader with HEAD_SERVICE HeaderType for subblocks.
struct SubBlockHeader:BlockHeader
{
ushort SubType;
@@ -250,33 +324,6 @@ struct ProtectHeader:BlockHeader
};
-struct AVHeader:BaseBlock
-{
- byte UnpVer;
- byte Method;
- byte AVVer;
- uint AVInfoCRC;
-};
-
-
-struct SignHeader:BaseBlock
-{
- uint CreationTime;
- ushort ArcNameSize;
- ushort UserNameSize;
-};
-
-
-struct UnixOwnersHeader:SubBlockHeader
-{
- ushort OwnerNameSize;
- ushort GroupNameSize;
-/* dummy */
- char OwnerName[NM];
- char GroupName[NM];
-};
-
-
struct EAHeader:SubBlockHeader
{
uint UnpSize;
@@ -293,15 +340,7 @@ struct StreamHeader:SubBlockHeader
byte Method;
uint StreamCRC;
ushort StreamNameSize;
-/* dummy */
- byte StreamName[NM];
-};
-
-
-struct MacFInfoHeader:SubBlockHeader
-{
- uint fileType;
- uint fileCreator;
+ char StreamName[260];
};
diff --git a/unrar/unrar/headers5.hpp b/unrar/unrar/headers5.hpp
new file mode 100644
index 0000000..50f5955
--- /dev/null
+++ b/unrar/unrar/headers5.hpp
@@ -0,0 +1,107 @@
+#ifndef _RAR_HEADERS5_
+#define _RAR_HEADERS5_
+
+#define SIZEOF_MARKHEAD5 8 // RAR 5.0 signature length.
+#define SIZEOF_SHORTBLOCKHEAD5 7 // Smallest RAR 5.0 block size.
+
+// RAR 5.0 block flags common for all blocks.
+
+// Additional extra area is present in the end of block header.
+#define HFL_EXTRA 0x0001
+// Additional data area is present in the end of block header.
+#define HFL_DATA 0x0002
+// Unknown blocks with this flag must be skipped when updating an archive.
+#define HFL_SKIPIFUNKNOWN 0x0004
+// Data area of this block is continuing from previous volume.
+#define HFL_SPLITBEFORE 0x0008
+// Data area of this block is continuing in next volume.
+#define HFL_SPLITAFTER 0x0010
+// Block depends on preceding file block.
+#define HFL_CHILD 0x0020
+// Preserve a child block if host is modified.
+#define HFL_INHERITED 0x0040
+
+// RAR 5.0 main archive header specific flags.
+#define MHFL_VOLUME 0x0001 // Volume.
+#define MHFL_VOLNUMBER 0x0002 // Volume number field is present. True for all volumes except first.
+#define MHFL_SOLID 0x0004 // Solid archive.
+#define MHFL_PROTECT 0x0008 // Recovery record is present.
+#define MHFL_LOCK 0x0010 // Locked archive.
+
+// RAR 5.0 file header specific flags.
+#define FHFL_DIRECTORY 0x0001 // Directory.
+#define FHFL_UTIME 0x0002 // Time field in Unix format is present.
+#define FHFL_CRC32 0x0004 // CRC32 field is present.
+#define FHFL_UNPUNKNOWN 0x0008 // Unknown unpacked size.
+
+// RAR 5.0 end of archive header specific flags.
+#define EHFL_NEXTVOLUME 0x0001 // Not last volume.
+
+// RAR 5.0 archive encryption header specific flags.
+#define CHFL_CRYPT_PSWCHECK 0x0001 // Password check data is present.
+
+
+// RAR 5.0 file compression flags.
+#define FCI_ALGO_BIT0 0x0001 // Version of compression algorithm.
+#define FCI_ALGO_BIT1 0x0002 // 0 .. 63.
+#define FCI_ALGO_BIT2 0x0004
+#define FCI_ALGO_BIT3 0x0008
+#define FCI_ALGO_BIT4 0x0010
+#define FCI_ALGO_BIT5 0x0020
+#define FCI_SOLID 0x0040 // Solid flag.
+#define FCI_METHOD_BIT0 0x0080 // Compression method.
+#define FCI_METHOD_BIT1 0x0100 // 0 .. 5 (6 and 7 are not used).
+#define FCI_METHOD_BIT2 0x0200
+#define FCI_DICT_BIT0 0x0400 // Dictionary size.
+#define FCI_DICT_BIT1 0x0800 // 128 KB .. 4 GB.
+#define FCI_DICT_BIT2 0x1000
+#define FCI_DICT_BIT3 0x2000
+
+// Main header extra field values.
+#define MHEXTRA_LOCATOR 0x01 // Position of quick list and other blocks.
+#define MHEXTRA_METADATA 0x02 // Archive metadata.
+
+// Flags for MHEXTRA_LOCATOR.
+#define MHEXTRA_LOCATOR_QLIST 0x01 // Quick open offset is present.
+#define MHEXTRA_LOCATOR_RR 0x02 // Recovery record offset is present.
+
+// Flags for MHEXTRA_METADATA.
+#define MHEXTRA_METADATA_NAME 0x01 // Archive name is present.
+#define MHEXTRA_METADATA_CTIME 0x02 // Archive creation time is present.
+#define MHEXTRA_METADATA_UNIXTIME 0x04 // Use Unix nanosecond time format.
+#define MHEXTRA_METADATA_UNIX_NS 0x08 // Unix format with nanosecond precision.
+
+// File and service header extra field values.
+#define FHEXTRA_CRYPT 0x01 // Encryption parameters.
+#define FHEXTRA_HASH 0x02 // File hash.
+#define FHEXTRA_HTIME 0x03 // High precision file time.
+#define FHEXTRA_VERSION 0x04 // File version information.
+#define FHEXTRA_REDIR 0x05 // File system redirection (links, etc.).
+#define FHEXTRA_UOWNER 0x06 // Unix owner and group information.
+#define FHEXTRA_SUBDATA 0x07 // Service header subdata array.
+
+
+// Hash type values for FHEXTRA_HASH.
+#define FHEXTRA_HASH_BLAKE2 0x00
+
+// Flags for FHEXTRA_HTIME.
+#define FHEXTRA_HTIME_UNIXTIME 0x01 // Use Unix time_t format.
+#define FHEXTRA_HTIME_MTIME 0x02 // mtime is present.
+#define FHEXTRA_HTIME_CTIME 0x04 // ctime is present.
+#define FHEXTRA_HTIME_ATIME 0x08 // atime is present.
+#define FHEXTRA_HTIME_UNIX_NS 0x10 // Unix format with nanosecond precision.
+
+// Flags for FHEXTRA_CRYPT.
+#define FHEXTRA_CRYPT_PSWCHECK 0x01 // Store password check data.
+#define FHEXTRA_CRYPT_HASHMAC 0x02 // Use MAC for unpacked data checksums.
+
+// Flags for FHEXTRA_REDIR.
+#define FHEXTRA_REDIR_DIR 0x01 // Link target is directory.
+
+// Flags for FHEXTRA_UOWNER.
+#define FHEXTRA_UOWNER_UNAME 0x01 // User name string is present.
+#define FHEXTRA_UOWNER_GNAME 0x02 // Group name string is present.
+#define FHEXTRA_UOWNER_NUMUID 0x04 // Numeric user ID is present.
+#define FHEXTRA_UOWNER_NUMGID 0x08 // Numeric group ID is present.
+
+#endif
diff --git a/unrar/unrar/int64.cpp b/unrar/unrar/int64.cpp
deleted file mode 100644
index 996d0ea..0000000
--- a/unrar/unrar/int64.cpp
+++ /dev/null
@@ -1,274 +0,0 @@
-#include "rar.hpp"
-
-#ifndef NATIVE_INT64
-
-Int64::Int64()
-{
-}
-
-
-Int64::Int64(uint n)
-{
- HighPart=0;
- LowPart=n;
-}
-
-
-Int64::Int64(uint HighPart,uint LowPart)
-{
- Int64::HighPart=HighPart;
- Int64::LowPart=LowPart;
-}
-
-
-/*
-Int64 Int64::operator = (Int64 n)
-{
- HighPart=n.HighPart;
- LowPart=n.LowPart;
- return(*this);
-}
-*/
-
-
-Int64 Int64::operator << (int n)
-{
- Int64 res=*this;
- while (n--)
- {
- res.HighPart<<=1;
- if (res.LowPart & 0x80000000)
- res.HighPart|=1;
- res.LowPart<<=1;
- }
- return(res);
-}
-
-
-Int64 Int64::operator >> (int n)
-{
- Int64 res=*this;
- while (n--)
- {
- res.LowPart>>=1;
- if (res.HighPart & 1)
- res.LowPart|=0x80000000;
- res.HighPart>>=1;
- }
- return(res);
-}
-
-
-Int64 operator / (Int64 n1,Int64 n2)
-{
- if (n1.HighPart==0 && n2.HighPart==0)
- return(Int64(0,n1.LowPart/n2.LowPart));
- int ShiftCount=0;
- while (n1>n2)
- {
- n2=n2<<1;
- if (++ShiftCount>64)
- return(0);
- }
- Int64 res=0;
- while (ShiftCount-- >= 0)
- {
- res=res<<1;
- if (n1>=n2)
- {
- n1-=n2;
- ++res;
- }
- n2=n2>>1;
- }
- return(res);
-}
-
-
-Int64 operator * (Int64 n1,Int64 n2)
-{
- if (n1<0x10000 && n2<0x10000)
- return(Int64(0,n1.LowPart*n2.LowPart));
- Int64 res=0;
- for (int I=0;I<64;I++)
- {
- if (n2.LowPart & 1)
- res+=n1;
- n1=n1<<1;
- n2=n2>>1;
- }
- return(res);
-}
-
-
-Int64 operator % (Int64 n1,Int64 n2)
-{
- if (n1.HighPart==0 && n2.HighPart==0)
- return(Int64(0,n1.LowPart%n2.LowPart));
- return(n1-n1/n2*n2);
-}
-
-
-Int64 operator + (Int64 n1,Int64 n2)
-{
- n1.LowPart+=n2.LowPart;
- if (n1.LowPart<n2.LowPart)
- n1.HighPart++;
- n1.HighPart+=n2.HighPart;
- return(n1);
-}
-
-
-Int64 operator - (Int64 n1,Int64 n2)
-{
- if (n1.LowPart<n2.LowPart)
- n1.HighPart--;
- n1.LowPart-=n2.LowPart;
- n1.HighPart-=n2.HighPart;
- return(n1);
-}
-
-
-Int64 operator += (Int64 &n1,Int64 n2)
-{
- n1=n1+n2;
- return(n1);
-}
-
-
-Int64 operator -= (Int64 &n1,Int64 n2)
-{
- n1=n1-n2;
- return(n1);
-}
-
-
-Int64 operator *= (Int64 &n1,Int64 n2)
-{
- n1=n1*n2;
- return(n1);
-}
-
-
-Int64 operator /= (Int64 &n1,Int64 n2)
-{
- n1=n1/n2;
- return(n1);
-}
-
-
-Int64 operator | (Int64 n1,Int64 n2)
-{
- n1.LowPart|=n2.LowPart;
- n1.HighPart|=n2.HighPart;
- return(n1);
-}
-
-
-Int64 operator & (Int64 n1,Int64 n2)
-{
- n1.LowPart&=n2.LowPart;
- n1.HighPart&=n2.HighPart;
- return(n1);
-}
-
-
-/*
-inline void operator -= (Int64 &n1,unsigned int n2)
-{
- if (n1.LowPart<n2)
- n1.HighPart--;
- n1.LowPart-=n2;
-}
-
-
-inline void operator ++ (Int64 &n)
-{
- if (++n.LowPart == 0)
- ++n.HighPart;
-}
-
-
-inline void operator -- (Int64 &n)
-{
- if (n.LowPart-- == 0)
- n.HighPart--;
-}
-*/
-
-bool operator == (Int64 n1,Int64 n2)
-{
- return(n1.LowPart==n2.LowPart && n1.HighPart==n2.HighPart);
-}
-
-
-bool operator > (Int64 n1,Int64 n2)
-{
- return((int)n1.HighPart>(int)n2.HighPart || n1.HighPart==n2.HighPart && n1.LowPart>n2.LowPart);
-}
-
-
-bool operator < (Int64 n1,Int64 n2)
-{
- return((int)n1.HighPart<(int)n2.HighPart || n1.HighPart==n2.HighPart && n1.LowPart<n2.LowPart);
-}
-
-
-bool operator != (Int64 n1,Int64 n2)
-{
- return(n1.LowPart!=n2.LowPart || n1.HighPart!=n2.HighPart);
-}
-
-
-bool operator >= (Int64 n1,Int64 n2)
-{
- return(n1>n2 || n1==n2);
-}
-
-
-bool operator <= (Int64 n1,Int64 n2)
-{
- return(n1<n2 || n1==n2);
-}
-
-
-void Int64::Set(uint HighPart,uint LowPart)
-{
- Int64::HighPart=HighPart;
- Int64::LowPart=LowPart;
-}
-#endif
-
-void itoa(Int64 n,char *Str)
-{
- if (n<=0xffffffff)
- {
- sprintf(Str,"%u",int64to32(n));
- return;
- }
-
- char NumStr[50];
- int Pos=0;
-
- do
- {
- NumStr[Pos++]=int64to32(n%10)+'0';
- n=n/10;
- } while (n!=0);
-
- for (int I=0;I<Pos;I++)
- Str[I]=NumStr[Pos-I-1];
- Str[Pos]=0;
-}
-
-
-Int64 atoil(char *Str)
-{
- Int64 n=0;
- while (*Str>='0' && *Str<='9')
- {
- n=n*10+*Str-'0';
- Str++;
- }
- return(n);
-}
diff --git a/unrar/unrar/int64.hpp b/unrar/unrar/int64.hpp
deleted file mode 100644
index d2f34bb..0000000
--- a/unrar/unrar/int64.hpp
+++ /dev/null
@@ -1,86 +0,0 @@
-#ifndef _RAR_INT64_
-#define _RAR_INT64_
-
-#if defined(__BORLANDC__) || defined(_MSC_VER)
-#define NATIVE_INT64
-typedef __int64 Int64;
-#endif
-
-#if defined(__GNUC__) || defined(__HP_aCC) || defined(__SUNPRO_CC)
-#define NATIVE_INT64
-typedef long long Int64;
-#endif
-
-#ifdef NATIVE_INT64
-
-#define int64to32(x) ((uint)(x))
-#define int32to64(high,low) ((((Int64)(high))<<32)+(low))
-#define is64plus(x) (x>=0)
-
-#else
-
-class Int64
-{
- public:
- Int64();
- Int64(uint n);
- Int64(uint HighPart,uint LowPart);
-
-// Int64 operator = (Int64 n);
- Int64 operator << (int n);
- Int64 operator >> (int n);
-
- friend Int64 operator / (Int64 n1,Int64 n2);
- friend Int64 operator * (Int64 n1,Int64 n2);
- friend Int64 operator % (Int64 n1,Int64 n2);
- friend Int64 operator + (Int64 n1,Int64 n2);
- friend Int64 operator - (Int64 n1,Int64 n2);
- friend Int64 operator += (Int64 &n1,Int64 n2);
- friend Int64 operator -= (Int64 &n1,Int64 n2);
- friend Int64 operator *= (Int64 &n1,Int64 n2);
- friend Int64 operator /= (Int64 &n1,Int64 n2);
- friend Int64 operator | (Int64 n1,Int64 n2);
- friend Int64 operator & (Int64 n1,Int64 n2);
- inline friend void operator -= (Int64 &n1,unsigned int n2)
- {
- if (n1.LowPart<n2)
- n1.HighPart--;
- n1.LowPart-=n2;
- }
- inline friend void operator ++ (Int64 &n)
- {
- if (++n.LowPart == 0)
- ++n.HighPart;
- }
- inline friend void operator -- (Int64 &n)
- {
- if (n.LowPart-- == 0)
- n.HighPart--;
- }
- friend bool operator == (Int64 n1,Int64 n2);
- friend bool operator > (Int64 n1,Int64 n2);
- friend bool operator < (Int64 n1,Int64 n2);
- friend bool operator != (Int64 n1,Int64 n2);
- friend bool operator >= (Int64 n1,Int64 n2);
- friend bool operator <= (Int64 n1,Int64 n2);
-
- void Set(uint HighPart,uint LowPart);
- uint GetLowPart() {return(LowPart);}
-
- uint LowPart;
- uint HighPart;
-};
-
-inline uint int64to32(Int64 n) {return(n.GetLowPart());}
-#define int32to64(high,low) (Int64((high),(low)))
-#define is64plus(x) ((int)(x).HighPart>=0)
-
-#endif
-
-#define INT64ERR int32to64(0x80000000,0)
-#define INT64MAX int32to64(0x7fffffff,0)
-
-void itoa(Int64 n,char *Str);
-Int64 atoil(char *Str);
-
-#endif
diff --git a/unrar/unrar/isnt.cpp b/unrar/unrar/isnt.cpp
index 493a9eb..3cc876b 100644
--- a/unrar/unrar/isnt.cpp
+++ b/unrar/unrar/isnt.cpp
@@ -1,9 +1,9 @@
#include "rar.hpp"
-#ifdef _WIN_32
-int WinNT()
+DWORD WinNT()
{
- static int dwPlatformId=-1,dwMajorVersion;
+ static int dwPlatformId=-1;
+ static DWORD dwMajorVersion,dwMinorVersion;
if (dwPlatformId==-1)
{
OSVERSIONINFO WinVer;
@@ -11,7 +11,104 @@ int WinNT()
GetVersionEx(&WinVer);
dwPlatformId=WinVer.dwPlatformId;
dwMajorVersion=WinVer.dwMajorVersion;
+ dwMinorVersion=WinVer.dwMinorVersion;
+
+ }
+ DWORD Result=0;
+ if (dwPlatformId==VER_PLATFORM_WIN32_NT)
+ Result=dwMajorVersion*0x100+dwMinorVersion;
+
+
+ return Result;
+}
+
+
+// Replace it with documented Windows 11 check when available.
+#include <comdef.h>
+#include <Wbemidl.h>
+#pragma comment(lib, "wbemuuid.lib")
+
+static bool WMI_IsWindows10()
+{
+ IWbemLocator *pLoc = NULL;
+
+ HRESULT hres = CoCreateInstance(CLSID_WbemLocator,0,CLSCTX_INPROC_SERVER,
+ IID_IWbemLocator,(LPVOID *)&pLoc);
+
+ if (FAILED(hres))
+ return false;
+
+ IWbemServices *pSvc = NULL;
+
+ hres = pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"),NULL,NULL,NULL,NULL,0,0,&pSvc);
+
+ if (FAILED(hres))
+ {
+ pLoc->Release();
+ return false;
+ }
+
+ hres = CoSetProxyBlanket(pSvc,RPC_C_AUTHN_WINNT,RPC_C_AUTHZ_NONE,NULL,
+ RPC_C_AUTHN_LEVEL_CALL,RPC_C_IMP_LEVEL_IMPERSONATE,NULL,EOAC_NONE);
+
+ if (FAILED(hres))
+ {
+ pSvc->Release();
+ pLoc->Release();
+ return false;
+ }
+
+ IEnumWbemClassObject *pEnumerator = NULL;
+ hres = pSvc->ExecQuery(bstr_t("WQL"), bstr_t("SELECT * FROM Win32_OperatingSystem"),
+ WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator);
+
+ if (FAILED(hres))
+ {
+ pSvc->Release();
+ pLoc->Release();
+ return false;
+ }
+
+ IWbemClassObject *pclsObj = NULL;
+ ULONG uReturn = 0;
+
+ bool Win10=false;
+ while (pEnumerator!=NULL)
+ {
+ HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
+
+ if (uReturn==0)
+ break;
+
+ VARIANT vtProp;
+
+ hr = pclsObj->Get(L"Name", 0, &vtProp, 0, 0);
+ Win10|=wcsstr(vtProp.bstrVal,L"Windows 10")!=NULL;
+ VariantClear(&vtProp);
+
+ pclsObj->Release();
+ }
+
+ pSvc->Release();
+ pLoc->Release();
+ pEnumerator->Release();
+
+ return Win10;
+}
+
+
+// Replace it with actual check when available.
+bool IsWindows11OrGreater()
+{
+ static bool IsSet=false,IsWin11=false;
+ if (!IsSet)
+ {
+ OSVERSIONINFO WinVer;
+ WinVer.dwOSVersionInfoSize=sizeof(WinVer);
+ GetVersionEx(&WinVer);
+ IsWin11=WinVer.dwMajorVersion>10 ||
+ WinVer.dwMajorVersion==10 && WinVer.dwBuildNumber >= 22000 && !WMI_IsWindows10();
+ IsSet=true;
}
- return(dwPlatformId==VER_PLATFORM_WIN32_NT ? dwMajorVersion:0);
+ return IsWin11;
}
-#endif
diff --git a/unrar/unrar/isnt.hpp b/unrar/unrar/isnt.hpp
index 0265236..fed0b51 100644
--- a/unrar/unrar/isnt.hpp
+++ b/unrar/unrar/isnt.hpp
@@ -1,6 +1,16 @@
#ifndef _RAR_ISNT_
#define _RAR_ISNT_
-int WinNT();
+enum WINNT_VERSION {
+ WNT_NONE=0,WNT_NT351=0x0333,WNT_NT4=0x0400,WNT_W2000=0x0500,
+ WNT_WXP=0x0501,WNT_W2003=0x0502,WNT_VISTA=0x0600,WNT_W7=0x0601,
+ WNT_W8=0x0602,WNT_W81=0x0603,WNT_W10=0x0a00
+};
+
+DWORD WinNT();
+
+
+// Replace it with actual check when available.
+bool IsWindows11OrGreater();
#endif
diff --git a/unrar/unrar/license.txt b/unrar/unrar/license.txt
index ec35e09..0811276 100644
--- a/unrar/unrar/license.txt
+++ b/unrar/unrar/license.txt
@@ -10,13 +10,15 @@
1. All copyrights to RAR and the utility UnRAR are exclusively
owned by the author - Alexander Roshal.
- 2. The UnRAR sources may be used in any software to handle RAR
- archives without limitations free of charge, but cannot be used
- to re-create the RAR compression algorithm, which is proprietary.
- Distribution of modified UnRAR sources in separate form or as a
- part of other software is permitted, provided that it is clearly
- stated in the documentation and source comments that the code may
- not be used to develop a RAR (WinRAR) compatible archiver.
+ 2. UnRAR source code may be used in any software to handle
+ RAR archives without limitations free of charge, but cannot be
+ used to develop RAR (WinRAR) compatible archiver and to
+ re-create RAR compression algorithm, which is proprietary.
+ Distribution of modified UnRAR source code in separate form
+ or as a part of other software is permitted, provided that
+ full text of this paragraph, starting from "UnRAR source code"
+ words, is included in license, or in documentation if license
+ is not available, and in source code comments of resulting package.
3. The UnRAR utility may be freely distributed. It is allowed
to distribute UnRAR inside of other software packages.
@@ -37,4 +39,4 @@
Thank you for your interest in RAR and UnRAR.
- Alexander L. Roshal \ No newline at end of file
+ Alexander L. Roshal
diff --git a/unrar/unrar/list.cpp b/unrar/unrar/list.cpp
index 99d730f..e4444e1 100644
--- a/unrar/unrar/list.cpp
+++ b/unrar/unrar/list.cpp
@@ -1,140 +1,153 @@
#include "rar.hpp"
-static void ListFileHeader(FileHeader &hd,bool Verbose,bool Technical,bool &TitleShown,bool Bare);
+static void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bool Technical,bool Bare,bool DisableNames);
static void ListSymLink(Archive &Arc);
-static void ListFileAttr(uint A,int HostOS);
+static void ListFileAttr(uint A,HOST_SYSTEM_TYPE HostType,wchar *AttrStr,size_t AttrSize);
static void ListOldSubHeader(Archive &Arc);
-static void ListNewSubHeader(CommandData *Cmd,Archive &Arc,bool Technical);
+static void ListNewSubHeader(CommandData *Cmd,Archive &Arc);
void ListArchive(CommandData *Cmd)
{
- Int64 SumPackSize=0,SumUnpSize=0;
+ int64 SumPackSize=0,SumUnpSize=0;
uint ArcCount=0,SumFileCount=0;
bool Technical=(Cmd->Command[1]=='T');
+ bool ShowService=Technical && Cmd->Command[2]=='A';
bool Bare=(Cmd->Command[1]=='B');
- bool Verbose=(*Cmd->Command=='V');
+ bool Verbose=(Cmd->Command[0]=='V');
- char ArcName[NM];
- wchar ArcNameW[NM];
-
- while (Cmd->GetArcName(ArcName,ArcNameW,sizeof(ArcName)))
+ wchar ArcName[NM];
+ while (Cmd->GetArcName(ArcName,ASIZE(ArcName)))
{
+ if (Cmd->ManualPassword)
+ Cmd->Password.Clean(); // Clean user entered password before processing next archive.
+
Archive Arc(Cmd);
-#ifdef _WIN_32
- Arc.RemoveSequentialFlag();
-#endif
- if (!Arc.WOpen(ArcName,ArcNameW))
+ if (!Arc.WOpen(ArcName))
continue;
bool FileMatched=true;
- while (1)
+ while (true)
{
- Int64 TotalPackSize=0,TotalUnpSize=0;
+ int64 TotalPackSize=0,TotalUnpSize=0;
uint FileCount=0;
if (Arc.IsArchive(true))
{
- if (!Arc.IsOpened())
- break;
bool TitleShown=false;
if (!Bare)
{
Arc.ViewComment();
+ mprintf(L"\n%s: %s",St(MListArchive),Arc.FileName);
- // RAR can close a corrupt encrypted archive
- if (!Arc.IsOpened())
- break;
-
- mprintf("\n");
+ mprintf(L"\n%s: ",St(MListDetails));
+ uint SetCount=0;
+ const wchar *Fmt=Arc.Format==RARFMT14 ? L"RAR 1.4":(Arc.Format==RARFMT15 ? L"RAR 4":L"RAR 5");
+ mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", Fmt);
if (Arc.Solid)
- mprintf(St(MListSolid));
+ mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListSolid));
if (Arc.SFXSize>0)
- mprintf(St(MListSFX));
+ mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListSFX));
if (Arc.Volume)
- if (Arc.Solid)
- mprintf(St(MListVol1));
- else
- mprintf(St(MListVol2));
- else
- if (Arc.Solid)
- mprintf(St(MListArc1));
+ if (Arc.Format==RARFMT50)
+ {
+ // RAR 5.0 archives store the volume number in main header,
+ // so it is already available now.
+ if (SetCount++ > 0)
+ mprintf(L", ");
+ mprintf(St(MVolumeNumber),Arc.VolNumber+1);
+ }
else
- mprintf(St(MListArc2));
- mprintf(" %s\n",Arc.FileName);
- if (Technical)
+ mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListVolume));
+ if (Arc.Protected)
+ mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListRR));
+ if (Arc.Locked)
+ mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListLock));
+ if (Arc.Encrypted)
+ mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListEncHead));
+
+ if (!Arc.MainHead.OrigName.empty())
+ mprintf(L"\n%s: %s",St(MOrigName),Arc.MainHead.OrigName.c_str());
+ if (Arc.MainHead.OrigTime.IsSet())
{
- if (Arc.Protected)
- mprintf(St(MListRecRec));
- if (Arc.Locked)
- mprintf(St(MListLock));
+ wchar DateStr[50];
+ Arc.MainHead.OrigTime.GetText(DateStr,ASIZE(DateStr),Technical);
+ mprintf(L"\n%s: %s",St(MOriginalTime),DateStr);
}
+
+ mprintf(L"\n");
}
- while(Arc.ReadHeader()>0)
+
+ wchar VolNumText[50];
+ *VolNumText=0;
+ while (Arc.ReadHeader()>0)
{
- int HeaderType=Arc.GetHeaderType();
- if (HeaderType==ENDARC_HEAD)
+ Wait(); // Allow quit listing with Ctrl+C.
+ HEADER_TYPE HeaderType=Arc.GetHeaderType();
+ if (HeaderType==HEAD_ENDARC)
+ {
+#ifndef SFX_MODULE
+ // Only RAR 1.5 archives store the volume number in end record.
+ if (Arc.EndArcHead.StoreVolNumber && Arc.Format==RARFMT15)
+ swprintf(VolNumText,ASIZE(VolNumText),L"%.10ls %u",St(MListVolume),Arc.VolNumber+1);
+#endif
+ if (Technical && ShowService)
+ {
+ mprintf(L"\n%12ls: %ls",St(MListService),L"EOF");
+ if (*VolNumText!=0)
+ mprintf(L"\n%12ls: %ls",St(MListFlags),VolNumText);
+ mprintf(L"\n");
+ }
break;
+ }
switch(HeaderType)
{
- case FILE_HEAD:
- IntToExt(Arc.NewLhd.FileName,Arc.NewLhd.FileName);
- if ((FileMatched=Cmd->IsProcessFile(Arc.NewLhd))==true)
+ case HEAD_FILE:
+ FileMatched=Cmd->IsProcessFile(Arc.FileHead,NULL,MATCH_WILDSUBPATH,0,NULL,0)!=0;
+ if (FileMatched)
{
- ListFileHeader(Arc.NewLhd,Verbose,Technical,TitleShown,Bare);
- if (!(Arc.NewLhd.Flags & LHD_SPLIT_BEFORE))
+ ListFileHeader(Arc,Arc.FileHead,TitleShown,Verbose,Technical,Bare,Cmd->DisableNames);
+ if (!Arc.FileHead.SplitBefore)
{
- TotalUnpSize+=Arc.NewLhd.FullUnpSize;
+ TotalUnpSize+=Arc.FileHead.UnpSize;
FileCount++;
}
- TotalPackSize+=Arc.NewLhd.FullPackSize;
- if (Technical)
- ListSymLink(Arc);
-#ifndef SFX_MODULE
- if (Verbose)
- Arc.ViewFileComment();
-#endif
+ TotalPackSize+=Arc.FileHead.PackSize;
}
break;
-#ifndef SFX_MODULE
- case SUB_HEAD:
- if (Technical && FileMatched && !Bare)
- ListOldSubHeader(Arc);
- break;
-#endif
- case NEWSUB_HEAD:
+ case HEAD_SERVICE:
if (FileMatched && !Bare)
{
- if (Technical)
- ListFileHeader(Arc.SubHead,Verbose,true,TitleShown,false);
- ListNewSubHeader(Cmd,Arc,Technical);
+ if (Technical && ShowService)
+ ListFileHeader(Arc,Arc.SubHead,TitleShown,Verbose,true,false,Cmd->DisableNames);
}
break;
}
Arc.SeekToNext();
}
- if (!Bare)
+ if (!Bare && !Technical)
if (TitleShown)
{
- mprintf("\n");
- for (int I=0;I<79;I++)
- mprintf("-");
- char UnpSizeText[20];
- itoa(TotalUnpSize,UnpSizeText);
+ wchar UnpSizeText[20];
+ itoa(TotalUnpSize,UnpSizeText,ASIZE(UnpSizeText));
- char PackSizeText[20];
- itoa(TotalPackSize,PackSizeText);
+ wchar PackSizeText[20];
+ itoa(TotalPackSize,PackSizeText,ASIZE(PackSizeText));
- mprintf("\n%5lu %16s %8s %3d%%",FileCount,UnpSizeText,
- PackSizeText,ToPercentUnlim(TotalPackSize,TotalUnpSize));
+ if (Verbose)
+ {
+ mprintf(L"\n----------- --------- -------- ----- ---------- ----- -------- ----");
+ mprintf(L"\n%21ls %9ls %3d%% %-27ls %u",UnpSizeText,
+ PackSizeText,ToPercentUnlim(TotalPackSize,TotalUnpSize),
+ VolNumText,FileCount);
+ }
+ else
+ {
+ mprintf(L"\n----------- --------- ---------- ----- ----");
+ mprintf(L"\n%21ls %-16ls %u",UnpSizeText,VolNumText,FileCount);
+ }
+
SumFileCount+=FileCount;
SumUnpSize+=TotalUnpSize;
SumPackSize+=TotalPackSize;
-#ifndef SFX_MODULE
- if (Arc.EndArcHead.Flags & EARC_VOLNUMBER)
- {
- mprintf(" ");
- mprintf(St(MVolumeNumber),Arc.EndArcHead.VolNumber+1);
- }
-#endif
- mprintf("\n");
+ mprintf(L"\n");
}
else
mprintf(St(MListNoFiles));
@@ -142,254 +155,339 @@ void ListArchive(CommandData *Cmd)
ArcCount++;
#ifndef NOVOLUME
- if (Cmd->VolSize!=0 && ((Arc.NewLhd.Flags & LHD_SPLIT_AFTER) ||
- Arc.GetHeaderType()==ENDARC_HEAD &&
- (Arc.EndArcHead.Flags & EARC_NEXT_VOLUME)!=0) &&
- MergeArchive(Arc,NULL,false,*Cmd->Command))
- {
+ if (Cmd->VolSize!=0 && (Arc.FileHead.SplitAfter ||
+ Arc.GetHeaderType()==HEAD_ENDARC && Arc.EndArcHead.NextVolume) &&
+ MergeArchive(Arc,NULL,false,Cmd->Command[0]))
Arc.Seek(0,SEEK_SET);
- }
else
#endif
break;
}
else
{
- if (Cmd->ArcNames->ItemsCount()<2 && !Bare)
+ if (Cmd->ArcNames.ItemsCount()<2 && !Bare)
mprintf(St(MNotRAR),Arc.FileName);
break;
}
}
}
- if (ArcCount>1 && !Bare)
+
+ // Clean user entered password. Not really required, just for extra safety.
+ if (Cmd->ManualPassword)
+ Cmd->Password.Clean();
+
+ if (ArcCount>1 && !Bare && !Technical)
{
- char UnpSizeText[20],PackSizeText[20];
- itoa(SumUnpSize,UnpSizeText);
- itoa(SumPackSize,PackSizeText);
- mprintf("\n%5lu %16s %8s %3d%%\n",SumFileCount,UnpSizeText,
- PackSizeText,ToPercentUnlim(SumPackSize,SumUnpSize));
+ wchar UnpSizeText[20],PackSizeText[20];
+ itoa(SumUnpSize,UnpSizeText,ASIZE(UnpSizeText));
+ itoa(SumPackSize,PackSizeText,ASIZE(PackSizeText));
+
+ if (Verbose)
+ mprintf(L"%21ls %9ls %3d%% %28ls %u",UnpSizeText,PackSizeText,
+ ToPercentUnlim(SumPackSize,SumUnpSize),L"",SumFileCount);
+ else
+ mprintf(L"%21ls %18s %lu",UnpSizeText,L"",SumFileCount);
}
}
-void ListFileHeader(FileHeader &hd,bool Verbose,bool Technical,bool &TitleShown,bool Bare)
+enum LISTCOL_TYPE {
+ LCOL_NAME,LCOL_ATTR,LCOL_SIZE,LCOL_PACKED,LCOL_RATIO,LCOL_CSUM,LCOL_ENCR
+};
+
+
+void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bool Technical,bool Bare,bool DisableNames)
{
- if (!Bare)
+ if (!TitleShown && !Technical && !Bare)
{
- if (!TitleShown)
+ if (Verbose)
{
- if (Verbose)
- mprintf(St(MListPathComm));
- else
- mprintf(St(MListName));
- mprintf(St(MListTitle));
- if (Technical)
- mprintf(St(MListTechTitle));
- for (int I=0;I<79;I++)
- mprintf("-");
- TitleShown=true;
+ mprintf(L"\n%ls",St(MListTitleV));
+ if (!DisableNames)
+ mprintf(L"\n----------- --------- -------- ----- ---------- ----- -------- ----");
}
-
- if (hd.HeadType==NEWSUB_HEAD)
- mprintf(St(MSubHeadType),hd.FileName);
-
- mprintf("\n%c",(hd.Flags & LHD_PASSWORD) ? '*' : ' ');
+ else
+ {
+ mprintf(L"\n%ls",St(MListTitleL));
+ if (!DisableNames)
+ mprintf(L"\n----------- --------- ---------- ----- ----");
+ }
+ // Must be set even in DisableNames mode to suppress "0 files" output
+ // unless no files are matched.
+ TitleShown=true;
}
+ if (DisableNames)
+ return;
- char *Name=hd.FileName;
-
-#ifdef UNICODE_SUPPORTED
- char ConvertedName[NM];
- if ((hd.Flags & LHD_UNICODE)!=0 && *hd.FileNameW!=0 && UnicodeEnabled())
- {
- if (WideToChar(hd.FileNameW,ConvertedName) && *ConvertedName!=0)
- Name=ConvertedName;
- }
-#endif
+ wchar *Name=hd.FileName;
+ RARFORMAT Format=Arc.Format;
if (Bare)
{
- mprintf("%s\n",Verbose ? Name:PointToName(Name));
+ mprintf(L"%s\n",Name);
return;
}
- if (Verbose)
- mprintf("%s\n%12s ",Name,"");
+ wchar UnpSizeText[30],PackSizeText[30];
+ if (hd.UnpSize==INT64NDF)
+ wcsncpyz(UnpSizeText,L"?",ASIZE(UnpSizeText));
else
- mprintf("%-12s",PointToName(Name));
+ itoa(hd.UnpSize,UnpSizeText,ASIZE(UnpSizeText));
+ itoa(hd.PackSize,PackSizeText,ASIZE(PackSizeText));
- char UnpSizeText[20],PackSizeText[20];
- if (hd.FullUnpSize==INT64MAX)
- strcpy(UnpSizeText,"?");
+ wchar AttrStr[30];
+ if (hd.HeaderType==HEAD_SERVICE)
+ swprintf(AttrStr,ASIZE(AttrStr),L"%cB",hd.Inherited ? 'I' : '.');
else
- itoa(hd.FullUnpSize,UnpSizeText);
- itoa(hd.FullPackSize,PackSizeText);
+ ListFileAttr(hd.FileAttr,hd.HSType,AttrStr,ASIZE(AttrStr));
- mprintf(" %8s %8s ",UnpSizeText,PackSizeText);
+ wchar RatioStr[10];
- if ((hd.Flags & LHD_SPLIT_BEFORE) && (hd.Flags & LHD_SPLIT_AFTER))
- mprintf(" <->");
+ if (hd.SplitBefore && hd.SplitAfter)
+ wcsncpyz(RatioStr,L"<->",ASIZE(RatioStr));
else
- if (hd.Flags & LHD_SPLIT_BEFORE)
- mprintf(" <--");
+ if (hd.SplitBefore)
+ wcsncpyz(RatioStr,L"<--",ASIZE(RatioStr));
else
- if (hd.Flags & LHD_SPLIT_AFTER)
- mprintf(" -->");
+ if (hd.SplitAfter)
+ wcsncpyz(RatioStr,L"-->",ASIZE(RatioStr));
else
- mprintf("%3d%%",ToPercentUnlim(hd.FullPackSize,hd.FullUnpSize));
+ swprintf(RatioStr,ASIZE(RatioStr),L"%d%%",ToPercentUnlim(hd.PackSize,hd.UnpSize));
- char DateStr[50];
- hd.mtime.GetText(DateStr,false);
- mprintf(" %s ",DateStr);
+ wchar DateStr[50];
+ hd.mtime.GetText(DateStr,ASIZE(DateStr),Technical);
- if (hd.HeadType==NEWSUB_HEAD)
- mprintf(" %c....B ",(hd.SubFlags & SUBHEAD_FLAGS_INHERITED) ? 'I' : '.');
- else
- ListFileAttr(hd.FileAttr,hd.HostOS);
+ if (Technical)
+ {
+ mprintf(L"\n%12s: %s",St(MListName),Name);
- mprintf(" %8.8X",hd.FileCRC);
- mprintf(" m%d",hd.Method-0x30);
- if ((hd.Flags & LHD_WINDOWMASK)<=6*32)
- mprintf("%c",((hd.Flags&LHD_WINDOWMASK)>>5)+'a');
- else
- mprintf(" ");
- mprintf(" %d.%d",hd.UnpVer/10,hd.UnpVer%10);
+ bool FileBlock=hd.HeaderType==HEAD_FILE;
+
+ if (!FileBlock && Arc.SubHead.CmpName(SUBHEAD_TYPE_STREAM))
+ {
+ mprintf(L"\n%12ls: %ls",St(MListType),St(MListStream));
+ wchar StreamName[NM];
+ GetStreamNameNTFS(Arc,StreamName,ASIZE(StreamName));
+ mprintf(L"\n%12ls: %ls",St(MListTarget),StreamName);
+ }
+ else
+ {
+ const wchar *Type=St(FileBlock ? (hd.Dir ? MListDir:MListFile):MListService);
+
+ if (hd.RedirType!=FSREDIR_NONE)
+ switch(hd.RedirType)
+ {
+ case FSREDIR_UNIXSYMLINK:
+ Type=St(MListUSymlink); break;
+ case FSREDIR_WINSYMLINK:
+ Type=St(MListWSymlink); break;
+ case FSREDIR_JUNCTION:
+ Type=St(MListJunction); break;
+ case FSREDIR_HARDLINK:
+ Type=St(MListHardlink); break;
+ case FSREDIR_FILECOPY:
+ Type=St(MListCopy); break;
+ }
+ mprintf(L"\n%12ls: %ls",St(MListType),Type);
+ if (hd.RedirType!=FSREDIR_NONE)
+ if (Format==RARFMT15)
+ {
+ char LinkTargetA[NM];
+ if (Arc.FileHead.Encrypted)
+ {
+ // Link data are encrypted. We would need to ask for password
+ // and initialize decryption routine to display the link target.
+ strncpyz(LinkTargetA,"*<-?->",ASIZE(LinkTargetA));
+ }
+ else
+ {
+ int DataSize=(int)Min(hd.PackSize,ASIZE(LinkTargetA)-1);
+ Arc.Read(LinkTargetA,DataSize);
+ LinkTargetA[DataSize > 0 ? DataSize : 0] = 0;
+ }
+ wchar LinkTarget[NM];
+ CharToWide(LinkTargetA,LinkTarget,ASIZE(LinkTarget));
+ mprintf(L"\n%12ls: %ls",St(MListTarget),LinkTarget);
+ }
+ else
+ mprintf(L"\n%12ls: %ls",St(MListTarget),hd.RedirName);
+ }
+ if (!hd.Dir)
+ {
+ mprintf(L"\n%12ls: %ls",St(MListSize),UnpSizeText);
+ mprintf(L"\n%12ls: %ls",St(MListPacked),PackSizeText);
+ mprintf(L"\n%12ls: %ls",St(MListRatio),RatioStr);
+ }
+ bool WinTitles=false;
+#ifdef _WIN_ALL
+ WinTitles=true;
+#endif
+ if (hd.mtime.IsSet())
+ mprintf(L"\n%12ls: %ls",St(WinTitles ? MListModified:MListMtime),DateStr);
+ if (hd.ctime.IsSet())
+ {
+ hd.ctime.GetText(DateStr,ASIZE(DateStr),true);
+ mprintf(L"\n%12ls: %ls",St(WinTitles ? MListCreated:MListCtime),DateStr);
+ }
+ if (hd.atime.IsSet())
+ {
+ hd.atime.GetText(DateStr,ASIZE(DateStr),true);
+ mprintf(L"\n%12ls: %ls",St(WinTitles ? MListAccessed:MListAtime),DateStr);
+ }
+ mprintf(L"\n%12ls: %ls",St(MListAttr),AttrStr);
+ if (hd.FileHash.Type==HASH_CRC32)
+ mprintf(L"\n%12ls: %8.8X",
+ hd.UseHashKey ? L"CRC32 MAC":hd.SplitAfter ? L"Pack-CRC32":L"CRC32",
+ hd.FileHash.CRC32);
+ if (hd.FileHash.Type==HASH_BLAKE2)
+ {
+ wchar BlakeStr[BLAKE2_DIGEST_SIZE*2+1];
+ BinToHex(hd.FileHash.Digest,BLAKE2_DIGEST_SIZE,NULL,BlakeStr,ASIZE(BlakeStr));
+ mprintf(L"\n%12ls: %ls",
+ hd.UseHashKey ? L"BLAKE2 MAC":hd.SplitAfter ? L"Pack-BLAKE2":L"BLAKE2",
+ BlakeStr);
+ }
- static const char *RarOS[]={
- "DOS","OS/2","Win95/NT","Unix","MacOS","BeOS","WinCE","","",""
- };
+ const wchar *HostOS=L"";
+ if (Format==RARFMT50 && hd.HSType!=HSYS_UNKNOWN)
+ HostOS=hd.HSType==HSYS_WINDOWS ? L"Windows":L"Unix";
+ if (Format==RARFMT15)
+ {
+ static const wchar *RarOS[]={
+ L"DOS",L"OS/2",L"Windows",L"Unix",L"Mac OS",L"BeOS",L"WinCE",L"",L"",L""
+ };
+ if (hd.HostOS<ASIZE(RarOS))
+ HostOS=RarOS[hd.HostOS];
+ }
+ if (*HostOS!=0)
+ mprintf(L"\n%12ls: %ls",St(MListHostOS),HostOS);
- if (Technical)
- mprintf("\n%22s %8s %4s",
- (hd.HostOS<sizeof(RarOS)/sizeof(RarOS[0]) ? RarOS[hd.HostOS]:""),
- (hd.Flags & LHD_SOLID) ? St(MYes):St(MNo),
- (hd.Flags & LHD_VERSION) ? St(MYes):St(MNo));
-}
+ mprintf(L"\n%12ls: RAR %ls(v%d) -m%d -md=%d%s",St(MListCompInfo),
+ Format==RARFMT15 ? L"1.5":L"5.0",
+ hd.UnpVer==VER_UNKNOWN ? 0 : hd.UnpVer,hd.Method,
+ hd.WinSize>=0x100000 ? hd.WinSize/0x100000:hd.WinSize/0x400,
+ hd.WinSize>=0x100000 ? L"M":L"K");
+ if (hd.Solid || hd.Encrypted)
+ {
+ mprintf(L"\n%12ls: ",St(MListFlags));
+ if (hd.Solid)
+ mprintf(L"%ls ",St(MListSolid));
+ if (hd.Encrypted)
+ mprintf(L"%ls ",St(MListEnc));
+ }
-void ListSymLink(Archive &Arc)
-{
- if (Arc.NewLhd.HostOS==HOST_UNIX && (Arc.NewLhd.FileAttr & 0xF000)==0xA000)
- if ((Arc.NewLhd.Flags & LHD_PASSWORD)==0)
+ if (hd.Version)
{
- char FileName[NM];
- int DataSize=Min(Arc.NewLhd.PackSize,sizeof(FileName)-1);
- Arc.Read(FileName,DataSize);
- FileName[DataSize]=0;
- mprintf("\n%22s %s","-->",FileName);
+ uint Version=ParseVersionFileName(Name,false);
+ if (Version!=0)
+ mprintf(L"\n%12ls: %u",St(MListFileVer),Version);
}
+
+ if (hd.UnixOwnerSet)
+ {
+ mprintf(L"\n%12ls: ",L"Unix owner");
+ if (*hd.UnixOwnerName!=0)
+ mprintf(L"%ls",GetWide(hd.UnixOwnerName));
+ else
+ if (hd.UnixOwnerNumeric)
+ mprintf(L"#%d",hd.UnixOwnerID);
+ mprintf(L":");
+ if (*hd.UnixGroupName!=0)
+ mprintf(L"%ls",GetWide(hd.UnixGroupName));
+ else
+ if (hd.UnixGroupNumeric)
+ mprintf(L"#%d",hd.UnixGroupID);
+ }
+
+ mprintf(L"\n");
+ return;
+ }
+
+ mprintf(L"\n%c%10ls %9ls ",hd.Encrypted ? '*' : ' ',AttrStr,UnpSizeText);
+
+ if (Verbose)
+ mprintf(L"%9ls %4ls ",PackSizeText,RatioStr);
+
+ mprintf(L" %ls ",DateStr);
+
+ if (Verbose)
+ {
+ if (hd.FileHash.Type==HASH_CRC32)
+ mprintf(L"%8.8X ",hd.FileHash.CRC32);
else
+ if (hd.FileHash.Type==HASH_BLAKE2)
+ {
+ byte *S=hd.FileHash.Digest;
+ mprintf(L"%02x%02x..%02x ",S[0],S[1],S[31]);
+ }
+ else
+ mprintf(L"???????? ");
+ }
+ mprintf(L"%ls",Name);
+}
+
+/*
+void ListSymLink(Archive &Arc)
+{
+ if (Arc.FileHead.HSType==HSYS_UNIX && (Arc.FileHead.FileAttr & 0xF000)==0xA000)
+ if (Arc.FileHead.Encrypted)
{
// Link data are encrypted. We would need to ask for password
// and initialize decryption routine to display the link target.
- mprintf("\n%22s %s","-->","*<-?->");
+ mprintf(L"\n%22ls %ls",L"-->",L"*<-?->");
+ }
+ else
+ {
+ char FileName[NM];
+ uint DataSize=(uint)Min(Arc.FileHead.PackSize,sizeof(FileName)-1);
+ Arc.Read(FileName,DataSize);
+ FileName[DataSize]=0;
+ mprintf(L"\n%22ls %ls",L"-->",GetWide(FileName));
}
}
+*/
-
-void ListFileAttr(uint A,int HostOS)
+void ListFileAttr(uint A,HOST_SYSTEM_TYPE HostType,wchar *AttrStr,size_t AttrSize)
{
- switch(HostOS)
+ switch(HostType)
{
- case HOST_MSDOS:
- case HOST_OS2:
- case HOST_WIN32:
- case HOST_MACOS:
- mprintf(" %c%c%c%c%c%c%c ",
- (A & 0x08) ? 'V' : '.',
- (A & 0x10) ? 'D' : '.',
- (A & 0x01) ? 'R' : '.',
- (A & 0x02) ? 'H' : '.',
- (A & 0x04) ? 'S' : '.',
- (A & 0x20) ? 'A' : '.',
- (A & 0x800) ? 'C' : '.');
+ case HSYS_WINDOWS:
+ swprintf(AttrStr,AttrSize,L"%c%c%c%c%c%c%c",
+ (A & 0x2000)!=0 ? 'I' : '.', // Not content indexed.
+ (A & 0x0800)!=0 ? 'C' : '.', // Compressed.
+ (A & 0x0020)!=0 ? 'A' : '.', // Archive.
+ (A & 0x0010)!=0 ? 'D' : '.', // Directory.
+ (A & 0x0004)!=0 ? 'S' : '.', // System.
+ (A & 0x0002)!=0 ? 'H' : '.', // Hidden.
+ (A & 0x0001)!=0 ? 'R' : '.'); // Read-only.
break;
- case HOST_UNIX:
- case HOST_BEOS:
+ case HSYS_UNIX:
switch (A & 0xF000)
{
case 0x4000:
- mprintf("d");
+ AttrStr[0]='d';
break;
case 0xA000:
- mprintf("l");
+ AttrStr[0]='l';
break;
default:
- mprintf("-");
+ AttrStr[0]='-';
break;
}
- mprintf("%c%c%c%c%c%c%c%c%c",
+ swprintf(AttrStr+1,AttrSize-1,L"%c%c%c%c%c%c%c%c%c",
(A & 0x0100) ? 'r' : '-',
(A & 0x0080) ? 'w' : '-',
- (A & 0x0040) ? ((A & 0x0800) ? 's':'x'):((A & 0x0800) ? 'S':'-'),
+ (A & 0x0040) ? ((A & 0x0800)!=0 ? 's':'x'):((A & 0x0800)!=0 ? 'S':'-'),
(A & 0x0020) ? 'r' : '-',
(A & 0x0010) ? 'w' : '-',
- (A & 0x0008) ? ((A & 0x0400) ? 's':'x'):((A & 0x0400) ? 'S':'-'),
+ (A & 0x0008) ? ((A & 0x0400)!=0 ? 's':'x'):((A & 0x0400)!=0 ? 'S':'-'),
(A & 0x0004) ? 'r' : '-',
(A & 0x0002) ? 'w' : '-',
- (A & 0x0001) ? 'x' : '-');
- break;
- }
-}
-
-
-#ifndef SFX_MODULE
-void ListOldSubHeader(Archive &Arc)
-{
- switch(Arc.SubBlockHead.SubType)
- {
- case EA_HEAD:
- mprintf(St(MListEAHead));
+ (A & 0x0001) ? ((A & 0x200)!=0 ? 't' : 'x') : '-');
break;
- case UO_HEAD:
- mprintf(St(MListUOHead),Arc.UOHead.OwnerName,Arc.UOHead.GroupName);
+ case HSYS_UNKNOWN:
+ wcsncpyz(AttrStr,L"?",AttrSize);
break;
- case MAC_HEAD:
- mprintf(St(MListMACHead1),Arc.MACHead.fileType>>24,Arc.MACHead.fileType>>16,Arc.MACHead.fileType>>8,Arc.MACHead.fileType);
- mprintf(St(MListMACHead2),Arc.MACHead.fileCreator>>24,Arc.MACHead.fileCreator>>16,Arc.MACHead.fileCreator>>8,Arc.MACHead.fileCreator);
- break;
- case BEEA_HEAD:
- mprintf(St(MListBeEAHead));
- break;
- case NTACL_HEAD:
- mprintf(St(MListNTACLHead));
- break;
- case STREAM_HEAD:
- mprintf(St(MListStrmHead),Arc.StreamHead.StreamName);
- break;
- default:
- mprintf(St(MListUnkHead),Arc.SubBlockHead.SubType);
- break;
- }
-}
-#endif
-
-
-void ListNewSubHeader(CommandData *Cmd,Archive &Arc,bool Technical)
-{
- if (Arc.SubHead.CmpName(SUBHEAD_TYPE_CMT) &&
- (Arc.SubHead.Flags & LHD_SPLIT_BEFORE)==0 && !Cmd->DisableComment)
- {
- Array<byte> CmtData;
- int ReadSize=Arc.ReadCommentData(&CmtData,NULL);
- if (ReadSize!=0)
- {
- mprintf(St(MFileComment));
- OutComment((char *)&CmtData[0],ReadSize);
- }
- }
- if (Arc.SubHead.CmpName(SUBHEAD_TYPE_STREAM) &&
- (Arc.SubHead.Flags & LHD_SPLIT_BEFORE)==0)
- {
- int DestSize=Arc.SubHead.SubData.Size()/2;
- wchar DestNameW[NM];
- char DestName[NM];
- if (DestSize<sizeof(DestName))
- {
- RawToWide(&Arc.SubHead.SubData[0],DestNameW,DestSize);
- DestNameW[DestSize]=0;
- WideToChar(DestNameW,DestName);
- mprintf("\n %s",DestName);
- }
}
}
diff --git a/unrar/unrar/loclang.hpp b/unrar/unrar/loclang.hpp
index 37da3a0..1e23ece 100644
--- a/unrar/unrar/loclang.hpp
+++ b/unrar/unrar/loclang.hpp
@@ -1,352 +1,401 @@
-#define MYesNo "_Yes_No"
-#define MYesNoAll "_Yes_No_All"
-#define MYesNoAllQ "_Yes_No_All_nEver_Quit"
-#define MYesNoAllRenQ "_Yes_No_All_nEver_Rename_Quit"
-#define MContinueQuit "_Continue_Quit"
-#define MRetryAbort "_Retry_Abort"
-#define MCopyright "\nRAR %s Copyright (c) 1993-%d Alexander Roshal %d %s %d"
-#define MRegTo "\nRegistered to %s\n"
-#define MShare "\nShareware version Type RAR -? for help\n"
-#define MUCopyright "\nUNRAR %s freeware Copyright (c) 1993-%d Alexander Roshal\n"
-#define MBeta "beta"
-#define MMonthJan "Jan"
-#define MMonthFeb "Feb"
-#define MMonthMar "Mar"
-#define MMonthApr "Apr"
-#define MMonthMay "May"
-#define MMonthJun "Jun"
-#define MMonthJul "Jul"
-#define MMonthAug "Aug"
-#define MMonthSep "Sep"
-#define MMonthOct "Oct"
-#define MMonthNov "Nov"
-#define MMonthDec "Dec"
-#define MRARTitle1 "\nUsage: rar <command> -<switch 1> -<switch N> <archive> <files...>"
-#define MUNRARTitle1 "\nUsage: unrar <command> -<switch 1> -<switch N> <archive> <files...>"
-#define MRARTitle2 "\n <@listfiles...> <path_to_extract\\>"
-#define MCHelpCmd "\n\n<Commands>"
-#define MCHelpCmdA "\n a Add files to archive"
-#define MCHelpCmdC "\n c Add archive comment"
-#define MCHelpCmdCF "\n cf Add files comment"
-#define MCHelpCmdCH "\n ch Change archive parameters"
-#define MCHelpCmdCW "\n cw Write archive comment to file"
-#define MCHelpCmdD "\n d Delete files from archive"
-#define MCHelpCmdE "\n e Extract files to current directory"
-#define MCHelpCmdF "\n f Freshen files in archive"
-#define MCHelpCmdI "\n i[par]=<str> Find string in archives"
-#define MCHelpCmdK "\n k Lock archive"
-#define MCHelpCmdL "\n l[t,b] List archive [technical, bare]"
-#define MCHelpCmdM "\n m[f] Move to archive [files only]"
-#define MCHelpCmdP "\n p Print file to stdout"
-#define MCHelpCmdR "\n r Repair archive"
-#define MCHelpCmdRC "\n rc Reconstruct missing volumes"
-#define MCHelpCmdRN "\n rn Rename archived files"
-#define MCHelpCmdRR "\n rr[N] Add data recovery record"
-#define MCHelpCmdRV "\n rv[N] Create recovery volumes"
-#define MCHelpCmdS "\n s[name|-] Convert archive to or from SFX"
-#define MCHelpCmdT "\n t Test archive files"
-#define MCHelpCmdU "\n u Update files in archive"
-#define MCHelpCmdV "\n v[t,b] Verbosely list archive [technical,bare]"
-#define MCHelpCmdX "\n x Extract files with full path"
-#define MCHelpSw "\n\n<Switches>"
-#define MCHelpSwm "\n - Stop switches scanning"
-#define MCHelpSwAC "\n ac Clear Archive attribute after compression or extraction"
-#define MCHelpSwAD "\n ad Append archive name to destination path"
-#define MCHelpSwAG "\n ag[format] Generate archive name using the current date"
-#define MCHelpSwAO "\n ao Add files with Archive attribute set"
-#define MCHelpSwAP "\n ap<path> Set path inside archive"
-#define MCHelpSwAS "\n as Synchronize archive contents"
-#define MCHelpSwAV "\n av Put authenticity verification (registered versions only)"
-#define MCHelpSwAVm "\n av- Disable authenticity verification check"
-#define MCHelpSwCm "\n c- Disable comments show"
-#define MCHelpSwCFGm "\n cfg- Disable read configuration"
-#define MCHelpSwCL "\n cl Convert names to lower case"
-#define MCHelpSwCU "\n cu Convert names to upper case"
-#define MCHelpSwDF "\n df Delete files after archiving"
-#define MCHelpSwDH "\n dh Open shared files"
-#define MCHelpSwDR "\n dr Delete files to Recycle Bin"
-#define MCHelpSwDS "\n ds Disable name sort for solid archive"
-#define MCHelpSwDW "\n dw Wipe files after archiving"
-#define MCHelpSwEa "\n e[+]<attr> Set file exclude and include attributes"
-#define MCHelpSwED "\n ed Do not add empty directories"
-#define MCHelpSwEE "\n ee Do not save and extract extended attributes"
-#define MCHelpSwEN "\n en Do not put 'end of archive' block"
-#define MCHelpSwEP "\n ep Exclude paths from names"
-#define MCHelpSwEP1 "\n ep1 Exclude base directory from names"
-#define MCHelpSwEP2 "\n ep2 Expand paths to full"
-#define MCHelpSwEP3 "\n ep3 Expand paths to full including the drive letter"
-#define MCHelpSwF "\n f Freshen files"
-#define MCHelpSwHP "\n hp[password] Encrypt both file data and headers"
-#define MCHelpSwIDP "\n id[c,d,p,q] Disable messages"
-#define MCHelpSwIEML "\n ieml[addr] Send archive by email"
-#define MCHelpSwIERR "\n ierr Send all messages to stderr"
-#define MCHelpSwILOG "\n ilog[name] Log errors to file (registered versions only)"
-#define MCHelpSwINUL "\n inul Disable all messages"
-#define MCHelpSwIOFF "\n ioff Turn PC off after completing an operation"
-#define MCHelpSwISND "\n isnd Enable sound"
-#define MCHelpSwK "\n k Lock archive"
-#define MCHelpSwKB "\n kb Keep broken extracted files"
-#define MCHelpSwMn "\n m<0..5> Set compression level (0-store...3-default...5-maximal)"
-#define MCHelpSwMC "\n mc<par> Set advanced compression parameters"
-#define MCHelpSwMD "\n md<size> Dictionary size in KB (64,128,256,512,1024,2048,4096 or A-G)"
-#define MCHelpSwMS "\n ms[ext;ext] Specify file types to store"
-#define MCHelpSwMT "\n mt<threads> Set the number of threads"
-#define MCHelpSwN "\n n<file> Include only specified file"
-#define MCHelpSwNa "\n n@ Read file names to include from stdin"
-#define MCHelpSwNal "\n n@<list> Include files in specified list file"
-#define MCHelpSwO "\n o[+|-] Set the overwrite mode"
-#define MCHelpSwOC "\n oc Set NTFS Compressed attribute"
-#define MCHelpSwOL "\n ol Save symbolic links as the link instead of the file"
-#define MCHelpSwOR "\n or Rename files automatically"
-#define MCHelpSwOS "\n os Save NTFS streams"
-#define MCHelpSwOW "\n ow Save or restore file owner and group"
-#define MCHelpSwP "\n p[password] Set password"
-#define MCHelpSwPm "\n p- Do not query password"
-#define MCHelpSwR "\n r Recurse subdirectories"
-#define MCHelpSwR0 "\n r0 Recurse subdirectories for wildcard names only"
-#define MCHelpSwRI "\n ri<P>[:<S>] Set priority (0-default,1-min..15-max) and sleep time in ms"
-#define MCHelpSwRR "\n rr[N] Add data recovery record"
-#define MCHelpSwRV "\n rv[N] Create recovery volumes"
-#define MCHelpSwS "\n s[<N>,v[-],e] Create solid archive"
-#define MCHelpSwSm "\n s- Disable solid archiving"
-#define MCHelpSwSC "\n sc<chr>[obj] Specify the character set"
-#define MCHelpSwSFX "\n sfx[name] Create SFX archive"
-#define MCHelpSwSI "\n si[name] Read data from standard input (stdin)"
-#define MCHelpSwSL "\n sl<size> Process files with size less than specified"
-#define MCHelpSwSM "\n sm<size> Process files with size more than specified"
-#define MCHelpSwT "\n t Test files after archiving"
-#define MCHelpSwTK "\n tk Keep original archive time"
-#define MCHelpSwTL "\n tl Set archive time to latest file"
-#define MCHelpSwTN "\n tn<time> Process files newer than <time>"
-#define MCHelpSwTO "\n to<time> Process files older than <time>"
-#define MCHelpSwTA "\n ta<date> Process files modified after <date> in YYYYMMDDHHMMSS format"
-#define MCHelpSwTB "\n tb<date> Process files modified before <date> in YYYYMMDDHHMMSS format"
-#define MCHelpSwTS "\n ts<m,c,a>[N] Save or restore file time (modification, creation, access)"
-#define MCHelpSwU "\n u Update files"
-#define MCHelpSwV "\n v Create volumes with size autodetection or list all volumes"
-#define MCHelpSwVUnr "\n v List all volumes"
-#define MCHelpSwVn "\n v<size>[k,b] Create volumes with size=<size>*1000 [*1024, *1]"
-#define MCHelpSwVD "\n vd Erase disk contents before creating volume"
-#define MCHelpSwVER "\n ver[n] File version control"
-#define MCHelpSwVN "\n vn Use the old style volume naming scheme"
-#define MCHelpSwVP "\n vp Pause before each volume"
-#define MCHelpSwW "\n w<path> Assign work directory"
-#define MCHelpSwX "\n x<file> Exclude specified file"
-#define MCHelpSwXa "\n x@ Read file names to exclude from stdin"
-#define MCHelpSwXal "\n x@<list> Exclude files in specified list file"
-#define MCHelpSwY "\n y Assume Yes on all queries"
-#define MCHelpSwZ "\n z[file] Read archive comment from file"
-#define MBadArc "\nERROR: Bad archive %s\n"
-#define MAskPsw "Enter password (will not be echoed)"
-#define MAskPswEcho "Enter password"
-#define MReAskPsw "\nReenter password: "
-#define MFor " for "
-#define MNotMatchPsw "\nERROR: Passwords do not match\n"
-#define MErrWrite "Write error in the file %s"
-#define MErrRead "Read error in the file %s"
-#define MErrSeek "Seek error in the file %s"
-#define MErrFClose "Cannot close the file %s"
-#define MErrOutMem "Not enough memory"
-#define MErrBrokenArc "Corrupt archive - use 'Repair' command"
-#define MProgAborted "Program aborted"
-#define MErrRename "\nCannot rename %s to %s"
-#define MAbsNextVol "\nCannot find volume %s"
-#define MBreak "\nUser break\n"
-#define MAskCreatVol "\nCreate next volume ?"
-#define MAskNextDisk "\nDisk full. Insert next"
-#define MCreatVol "\n\nCreating %sarchive %s\n"
-#define MAskNextVol "\nInsert disk with %s"
-#define MTestVol "\n\nTesting archive %s\n"
-#define MExtrVol "\n\nExtracting from %s\n"
-#define MConverting "\nConverting %s"
-#define MCvtToSFX "\nConvert archives to SFX"
-#define MCvtFromSFX "\nRemoving SFX module"
-#define MNotSFX "\n%s is not SFX archive"
-#define MNotRAR "\n%s is not RAR archive"
-#define MNotFirstVol "\n%s is not the first volume"
-#define MCvtOldFormat "\n%s - cannot convert to SFX archive with old format"
-#define MCannotCreate "\nCannot create %s"
-#define MCannotOpen "\nCannot open %s"
-#define MUnknownMeth "\nUnknown method in %s"
-#define MVerRequired "\nYou need RAR %d.%d to unpack it"
-#define MOk " OK"
-#define MDone "\nDone"
-#define MLockingArc "\nLocking archive"
-#define MNotMdfOld "\n\nERROR: Cannot modify old format archive"
-#define MNotMdfLock "\n\nERROR: Locked archive"
-#define MNotMdfVol "\n\nERROR: Cannot modify volume"
-#define MVerifyAV "\nVerifying authenticity information ... "
-#define MFailedAV " Failed\n"
-#define MStrAV1 "\n\nArchive %s"
-#define MStrAV2 "\ncreated at %s"
-#define MStrAV3 "\nby %s\n"
-#define MLogFailedAV "Invalid authenticity information"
-#define MAddingAV "\nAdding authenticity verification "
-#define MAVOldStyle "\n\nOld style authenticity information"
-#define MPackAskReg "\nEvaluation copy. Please register.\n"
-#define MCreateArchive "\nCreating %sarchive %s\n"
-#define MUpdateArchive "\nUpdating %sarchive %s\n"
-#define MAddSolid "solid "
-#define MAddFile "\nAdding %-58s "
-#define MUpdFile "\nUpdating %-58s "
-#define MAddPoints "\n... %-58s "
-#define MCannotUpdPswSolid "\nERROR: Cannot update solid archives with password\n"
-#define MMoveDelFiles "\n\nDeleting files %s..."
-#define MMoveDelDirs "and directories"
-#define MMoveDelFile "\nDeleting %-30s"
-#define MMoveDeleted " deleted"
-#define MMoveNotDeleted " NOT DELETED"
-#define MClearAttrib "\n\nClearing attributes..."
-#define MMoveDelDir "\nDeleting directory %-30s"
-#define MWarErrFOpen "\nWARNING: Cannot open %d %s"
-#define MErrOpenFiles "files"
-#define MErrOpenFile "file"
-#define MAddNoFiles "\nWARNING: No files"
-#define MMdfEncrSol "\n%s: encrypted"
-#define MCannotMdfEncrSol "\nCannot modify solid archive containing encrypted files"
-#define MAddAnalyze "\nAnalyzing archived files: "
-#define MRepacking "\nRepacking archived files: "
-#define MCRCFailed "\n%-20s - CRC failed"
-#define MExtrTest "\n\nTesting archive %s\n"
-#define MExtracting "\n\nExtracting from %s\n"
-#define MUseCurPsw "\n%s - use current password ?"
-#define MCreatDir "\nCreating %-56s"
-#define MExtrSkipFile "\nSkipping %-56s"
-#define MExtrTestFile "\nTesting %-56s"
-#define MExtrFile "\nExtracting %-56s"
-#define MExtrPoints "\n... %-56s"
-#define MExtrErrMkDir "\nCannot create directory %s"
-#define MExtrPrinting "\n------ Printing %s\n\n"
-#define MEncrBadCRC "\nEncrypted file: CRC failed in %s (password incorrect ?)"
-#define MExtrNoFiles "\nNo files to extract"
-#define MExtrAllOk "\nAll OK"
-#define MExtrTotalErr "\nTotal errors: %ld"
-#define MFileExists "\n\n%s already exists. Overwrite it ?"
-#define MAskOverwrite "\nOverwrite %s ?"
-#define MAskNewName "\nEnter new name: "
-#define MLogMainHead "\nThe archive header is corrupt"
-#define MLogFileHead "\n%s - the file header is corrupt"
-#define MLogCommHead "\nThe comment header is corrupt\n"
-#define MLogProtectHead "The data recovery header is corrupt"
-#define MReadStdinCmt "\nReading comment from stdin\n"
-#define MReadCommFrom "\nReading comment from %s"
-#define MDelComment "\nDeleting comment from %s"
-#define MAddComment "\nAdding comment to %s"
-#define MFCommAdd "\nAdding file comments"
-#define MAskFComm "\n\nReading comment for %s : %s from stdin\n"
-#define MLogCommBrk "\nThe archive comment is corrupt"
-#define MCommAskCont "\nPress 'Enter' to continue or 'Q' to quit:"
-#define MLogBrokFCmt "\nThe file comment is corrupt"
-#define MAbsDestName "\nDestination file name required"
-#define MWriteCommTo "\nWrite comment to %s"
-#define MCommNotPres "\nComment is not present"
-#define MDelFrom "\nDeleting from %s"
-#define MDeleting "\nDeleting %s"
-#define MEraseArc "\nErasing empty archive %s"
-#define MNoDelFiles "\nNo files to delete"
-#define MLogTitle "\n\n-------- %2d %s %d, archive %s\n"
-#define MPathTooLong "\nERROR: Path too long\n"
-#define MListSolid "Solid "
-#define MListSFX "SFX "
-#define MListVol1 "volume"
-#define MListVol2 "Volume"
-#define MListArc1 "archive"
-#define MListArc2 "Archive"
-#define MListRecRec "\nRecovery record is present\n"
-#define MListLock "\nLock is present\n"
-#define MListPathComm "\nPathname/Comment\n "
-#define MListName "\n Name "
-#define MListTitle " Size Packed Ratio Date Time Attr CRC Meth Ver\n"
-#define MListTechTitle " Host OS Solid Old\n"
-#define MListEAHead "\n OS/2 extended attributes"
-#define MListUOHead "\n Unix Owner/Group data: %-14s %-14s"
-#define MListBeEAHead "\n BeOS extended attributes"
-#define MListNTACLHead "\n NTFS security data"
-#define MListStrmHead "\n NTFS stream: %s"
-#define MListUnkHead "\n Unknown subheader type: 0x%04x"
-#define MFileComment "\nComment: "
-#define MYes "Yes"
-#define MNo "No"
-#define MListNoFiles " 0 files\n"
-#define MRprReconstr "\nReconstructing %s"
-#define MRprBuild "\nBuilding %s"
-#define MRprOldFormat "\nCannot repair archive with old format"
-#define MRprFind "\nFound %s"
-#define MRprAskIsSol "\nThe archive header is corrupt. Mark archive as solid ?"
-#define MRprNoFiles "\nNo files found"
-#define MRprSuspEntry "\n\nSuspicious entry %s"
-#define MRprDir "\nDirectory"
-#define MRprSuspSize "\nSize %ld Packed %ld"
-#define MRprSuspAdd "\nAdd it to archive ?"
-#define MLogUnexpEOF "\nUnexpected end of archive"
-#define MRepAskReconst "\nReconstruct archive structure ?"
-#define MRecScanning "\nScanning..."
-#define MRecRNotFound "\nData recovery record not found"
-#define MRecRFound "\nData recovery record found"
-#define MRecSecDamage "\nSector %ld (offsets %lX...%lX) damaged"
-#define MRecCorrected " - data recovered"
-#define MRecFailed " - cannot recover data"
-#define MAddRecRec "\nAdding data recovery record"
-#define MEraseForVolume "\n\nErasing contents of drive %c:\n"
-#define MGetOwnersError "\nWARNING: Cannot get %s owner and group\n"
-#define MErrGetOwnerID "\nWARNING: Cannot get owner %s ID\n"
-#define MErrGetGroupID "\nWARNING: Cannot get group %s ID\n"
-#define MOwnersBroken "\nERROR: %s group and owner data are corrupt\n"
-#define MSetOwnersError "\nWARNING: Cannot set %s owner and group\n"
-#define MErrLnkRead "\nWARNING: Cannot read symbolic link %s"
-#define MErrCreateLnk "\nWARNING: Cannot create link %s"
-#define MSymLinkExists "\nWARNING: Symbolic link %s already exists"
-#define MAskRetryCreate "\nCannot create %s. Retry ?"
-#define MListMACHead1 "\n MacOS file type: %c%c%c%c ; "
-#define MListMACHead2 "file creator: %c%c%c%c\n"
-#define MDataBadCRC "\n%-20s : packed data CRC failed in volume %s"
-#define MFileRO "\n%s is read-only"
-#define MACLGetError "\nWARNING: Cannot get %s security data\n"
-#define MACLSetError "\nWARNING: Cannot set %s security data\n"
-#define MACLBroken "\nERROR: %s security data are corrupt\n"
-#define MACLUnknown "\nWARNING: Unknown format of %s security data\n"
-#define MStreamBroken "\nERROR: %s stream data are corrupt\n"
-#define MStreamUnknown "\nWARNING: Unknown format of %s stream data\n"
-#define MInvalidName "\nERROR: Invalid file name %s"
-#define MEABroken "\nERROR: %s extended attributes are corrupt\n"
-#define MEAUnknHeader "\nWARNING: %s - unknown format of extended attributes\n"
-#define MCannotSetEA "\nWARNING: cannot set extended attributes to %s\n"
-#define MCannotGetEA "\nERROR: Cannot get extended attributes of %s\n"
-#define MShowEA " (+EA)"
-#define MSkipEA "\n...skipping extended attributes"
-#define MProcessArc "\n\nProcessing archive %s"
-#define MSyncScanError "\nFile search errors, cannot synchronize archive"
-#define MCorrectingName "\nWARNING: Attempting to correct the invalid file name"
-#define MUnpCannotMerge "\nWARNING: You need to start extraction from a previous volume to unpack %s"
-#define MUnknownOption "\nERROR: Unknown option: %s"
-#define MSubHeadCorrupt "\nERROR: Corrupt data header found, ignored"
-#define MSubHeadUnknown "\nWARNING: Unknown data header format, ignored"
-#define MSubHeadDataCRC "\nERROR: Corrupt %s data block"
-#define MSubHeadType "\nData header type: %s"
-#define MScanError "\nCannot read contents of %s"
-#define MNotVolume "\n%s is not volume"
-#define MRecVolDiffSets "\nERROR: %s and %s belong to different sets"
-#define MRecVolMissing "\n%d volumes missing"
-#define MRecVolFound "\n%d recovery volumes found"
-#define MRecVolAllExist "\nNothing to reconstruct"
-#define MRecVolCannotFix "\nReconstruction impossible"
-#define MReconstructing "\nReconstructing..."
-#define MCreating "\nCreating %s"
-#define MRenaming "\nRenaming %s to %s"
-#define MNTFSRequired "\nWrite error: only NTFS file system supports files larger than 4 GB"
-#define MErrChangeAttr "\nWARNING: Cannot change attributes of %s"
-#define MWrongSFXVer "\nERROR: default SFX module does not support RAR %d.%d archives"
-#define MCannotEncName "\nCannot encrypt archive already contained encrypted files"
-#define MCannotEmail "\nCannot email the file %s"
-#define MCopyrightS "\nRAR SFX archive"
-#define MSHelpCmd "\n\n<Commands>"
-#define MSHelpCmdE "\n -x Extract from archive (default)"
-#define MSHelpCmdT "\n -t Test archive files"
-#define MSHelpCmdV "\n -v Verbosely list contents of archive"
-#define MMaxPathLimit "\nTotal path and file name length must not exceed %d characters"
-#define MRecVolLimit "\nTotal number of usual and recovery volumes must not exceed 255"
-#define MVolumeNumber "volume %d"
-#define MCannotDelete "\nCannot delete %s"
-#define MCalcCRC "\nCalculating the control sum"
-#define MTooLargeSFXArc "\nWARNING: Too large SFX archive. Windows cannot run the executable file exceeding 4 GB."
-#define MCalcCRCAllVol "\nCalculating control sums of all volumes."
+#define MYesNo L"_Yes_No"
+#define MYesNoAll L"_Yes_No_All"
+#define MYesNoAllQ L"_Yes_No_All_nEver_Quit"
+#define MYesNoAllRenQ L"_Yes_No_All_nEver_Rename_Quit"
+#define MContinueQuit L"_Continue_Quit"
+#define MRetryAbort L"_Retry_Abort"
+#define MIgnoreAllRetryQuit L"_Ignore_iGnore all_Retry_Quit"
+#define MCopyright L"\nRAR %s Copyright (c) 1993-%d Alexander Roshal %d %s %d"
+#define MRegTo L"\nRegistered to %s\n"
+#define MShare L"\nTrial version Type 'rar -?' for help\n"
+#define MRegKeyWarning L"\nAvailable license key is valid only for %s\n"
+#define MUCopyright L"\nUNRAR %s freeware Copyright (c) 1993-%d Alexander Roshal\n"
+#define MBeta L"beta"
+#define Mx86 L"x86"
+#define Mx64 L"x64"
+#define MMonthJan L"Jan"
+#define MMonthFeb L"Feb"
+#define MMonthMar L"Mar"
+#define MMonthApr L"Apr"
+#define MMonthMay L"May"
+#define MMonthJun L"Jun"
+#define MMonthJul L"Jul"
+#define MMonthAug L"Aug"
+#define MMonthSep L"Sep"
+#define MMonthOct L"Oct"
+#define MMonthNov L"Nov"
+#define MMonthDec L"Dec"
+#define MRARTitle1 L"\nUsage: rar <command> -<switch 1> -<switch N> <archive> <files...>"
+#define MUNRARTitle1 L"\nUsage: unrar <command> -<switch 1> -<switch N> <archive> <files...>"
+#define MRARTitle2 L"\n <@listfiles...> <path_to_extract\\>"
+#define MFwrSlTitle2 L"\n <@listfiles...> <path_to_extract/>"
+#define MCHelpCmd L"\n\n<Commands>"
+#define MCHelpCmdA L"\n a Add files to archive"
+#define MCHelpCmdC L"\n c Add archive comment"
+#define MCHelpCmdCH L"\n ch Change archive parameters"
+#define MCHelpCmdCW L"\n cw Write archive comment to file"
+#define MCHelpCmdD L"\n d Delete files from archive"
+#define MCHelpCmdE L"\n e Extract files without archived paths"
+#define MCHelpCmdF L"\n f Freshen files in archive"
+#define MCHelpCmdI L"\n i[par]=<str> Find string in archives"
+#define MCHelpCmdK L"\n k Lock archive"
+#define MCHelpCmdL L"\n l[t[a],b] List archive contents [technical[all], bare]"
+#define MCHelpCmdM L"\n m[f] Move to archive [files only]"
+#define MCHelpCmdP L"\n p Print file to stdout"
+#define MCHelpCmdR L"\n r Repair archive"
+#define MCHelpCmdRC L"\n rc Reconstruct missing volumes"
+#define MCHelpCmdRN L"\n rn Rename archived files"
+#define MCHelpCmdRR L"\n rr[N] Add the data recovery record"
+#define MCHelpCmdRV L"\n rv[N] Create recovery volumes"
+#define MCHelpCmdS L"\n s[name|-] Convert archive to or from SFX"
+#define MCHelpCmdT L"\n t Test archive files"
+#define MCHelpCmdU L"\n u Update files in archive"
+#define MCHelpCmdV L"\n v[t[a],b] Verbosely list archive contents [technical[all],bare]"
+#define MCHelpCmdX L"\n x Extract files with full path"
+#define MCHelpSw L"\n\n<Switches>"
+#define MCHelpSwm L"\n - Stop switches scanning"
+#define MCHelpSwAT L"\n @[+] Disable [enable] file lists"
+#define MCHelpSwAC L"\n ac Clear Archive attribute after compression or extraction"
+#define MCHelpSwAD L"\n ad[1,2] Alternate destination path"
+#define MCHelpSwAG L"\n ag[format] Generate archive name using the current date"
+#define MCHelpSwAI L"\n ai Ignore file attributes"
+#define MCHelpSwAM L"\n am[s,r] Archive name and time [save, restore]"
+#define MCHelpSwAO L"\n ao Add files with Archive attribute set"
+#define MCHelpSwAP L"\n ap<path> Set path inside archive"
+#define MCHelpSwAS L"\n as Synchronize archive contents"
+#define MCHelpSwCm L"\n c- Disable comments show"
+#define MCHelpSwCFGm L"\n cfg- Disable read configuration"
+#define MCHelpSwCL L"\n cl Convert names to lower case"
+#define MCHelpSwCU L"\n cu Convert names to upper case"
+#define MCHelpSwDF L"\n df Delete files after archiving"
+#define MCHelpSwDH L"\n dh Open shared files"
+#define MCHelpSwDR L"\n dr Delete files to Recycle Bin"
+#define MCHelpSwDS L"\n ds Disable name sort for solid archive"
+#define MCHelpSwDW L"\n dw Wipe files after archiving"
+#define MCHelpSwEa L"\n e[+]<attr> Set file exclude and include attributes"
+#define MCHelpSwED L"\n ed Do not add empty directories"
+#define MCHelpSwEP L"\n ep Exclude paths from names"
+#define MCHelpSwEP1 L"\n ep1 Exclude base directory from names"
+#define MCHelpSwEP2 L"\n ep2 Expand paths to full"
+#define MCHelpSwEP3 L"\n ep3 Expand paths to full including the drive letter"
+#define MCHelpSwEP4 L"\n ep4<path> Exclude the path prefix from names"
+#define MCHelpSwF L"\n f Freshen files"
+#define MCHelpSwHP L"\n hp[password] Encrypt both file data and headers"
+#define MCHelpSwHT L"\n ht[b|c] Select hash type [BLAKE2,CRC32] for file checksum"
+#define MCHelpSwIDP L"\n id[c,d,n,p,q] Display or disable messages"
+#define MCHelpSwIEML L"\n ieml[addr] Send archive by email"
+#define MCHelpSwIERR L"\n ierr Send all messages to stderr"
+#define MCHelpSwILOG L"\n ilog[name] Log errors to file"
+#define MCHelpSwINUL L"\n inul Disable all messages"
+#define MCHelpSwIOFF L"\n ioff[n] Turn PC off after completing an operation"
+#define MCHelpSwISND L"\n isnd[-] Control notification sounds"
+#define MCHelpSwIVER L"\n iver Display the version number"
+#define MCHelpSwK L"\n k Lock archive"
+#define MCHelpSwKB L"\n kb Keep broken extracted files"
+#define MCHelpSwLog L"\n log[f][=name] Write names to log file"
+#define MCHelpSwMn L"\n m<0..5> Set compression level (0-store...3-default...5-maximal)"
+#define MCHelpSwMA L"\n ma[4|5] Specify a version of archiving format"
+#define MCHelpSwMC L"\n mc<par> Set advanced compression parameters"
+#define MCHelpSwMD L"\n md<n>[k,m,g] Dictionary size in KB, MB or GB"
+#define MCHelpSwME L"\n me[par] Set encryption parameters"
+#define MCHelpSwMS L"\n ms[ext;ext] Specify file types to store"
+#define MCHelpSwMT L"\n mt<threads> Set the number of threads"
+#define MCHelpSwN L"\n n<file> Additionally filter included files"
+#define MCHelpSwNa L"\n n@ Read additional filter masks from stdin"
+#define MCHelpSwNal L"\n n@<list> Read additional filter masks from list file"
+#define MCHelpSwO L"\n o[+|-] Set the overwrite mode"
+#define MCHelpSwOC L"\n oc Set NTFS Compressed attribute"
+#define MCHelpSwOH L"\n oh Save hard links as the link instead of the file"
+#define MCHelpSwOI L"\n oi[0-4][:min] Save identical files as references"
+#define MCHelpSwOL L"\n ol[a] Process symbolic links as the link [absolute paths]"
+#define MCHelpSwONI L"\n oni Allow potentially incompatible names"
+#define MCHelpSwOP L"\n op<path> Set the output path for extracted files"
+#define MCHelpSwOR L"\n or Rename files automatically"
+#define MCHelpSwOS L"\n os Save NTFS streams"
+#define MCHelpSwOW L"\n ow Save or restore file owner and group"
+#define MCHelpSwP L"\n p[password] Set password"
+#define MCHelpSwQO L"\n qo[-|+] Add quick open information [none|force]"
+#define MCHelpSwR L"\n r Recurse subdirectories"
+#define MCHelpSwRm L"\n r- Disable recursion"
+#define MCHelpSwR0 L"\n r0 Recurse subdirectories for wildcard names only"
+#define MCHelpSwRI L"\n ri<P>[:<S>] Set priority (0-default,1-min..15-max) and sleep time in ms"
+#define MCHelpSwRR L"\n rr[N] Add data recovery record"
+#define MCHelpSwRV L"\n rv[N] Create recovery volumes"
+#define MCHelpSwS L"\n s[<N>,v[-],e] Create solid archive"
+#define MCHelpSwSm L"\n s- Disable solid archiving"
+#define MCHelpSwSC L"\n sc<chr>[obj] Specify the character set"
+#define MCHelpSwSFX L"\n sfx[name] Create SFX archive"
+#define MCHelpSwSI L"\n si[name] Read data from standard input (stdin)"
+#define MCHelpSwSL L"\n sl<size> Process files with size less than specified"
+#define MCHelpSwSM L"\n sm<size> Process files with size more than specified"
+#define MCHelpSwT L"\n t Test files after archiving"
+#define MCHelpSwTK L"\n tk Keep original archive time"
+#define MCHelpSwTL L"\n tl Set archive time to latest file"
+#define MCHelpSwTN L"\n tn[mcao]<t> Process files newer than <t> time"
+#define MCHelpSwTO L"\n to[mcao]<t> Process files older than <t> time"
+#define MCHelpSwTA L"\n ta[mcao]<d> Process files modified after <d> YYYYMMDDHHMMSS date"
+#define MCHelpSwTB L"\n tb[mcao]<d> Process files modified before <d> YYYYMMDDHHMMSS date"
+#define MCHelpSwTS L"\n ts[m,c,a,p] Save or restore time (modification, creation, access, preserve)"
+#define MCHelpSwU L"\n u Update files"
+#define MCHelpSwV L"\n v Create volumes with size autodetection or list all volumes"
+#define MCHelpSwVUnr L"\n v List all volumes"
+#define MCHelpSwVn L"\n v<size>[k,b] Create volumes with size=<size>*1000 [*1024, *1]"
+#define MCHelpSwVD L"\n vd Erase disk contents before creating volume"
+#define MCHelpSwVER L"\n ver[n] File version control"
+#define MCHelpSwVN L"\n vn Use the old style volume naming scheme"
+#define MCHelpSwVP L"\n vp Pause before each volume"
+#define MCHelpSwW L"\n w<path> Assign work directory"
+#define MCHelpSwX L"\n x<file> Exclude specified file"
+#define MCHelpSwXa L"\n x@ Read file names to exclude from stdin"
+#define MCHelpSwXal L"\n x@<list> Exclude files listed in specified list file"
+#define MCHelpSwY L"\n y Assume Yes on all queries"
+#define MCHelpSwZ L"\n z[file] Read archive comment from file"
+#define MBadArc L"\nERROR: Bad archive %s\n"
+#define MAskPsw L"Enter password (will not be echoed)"
+#define MAskPswFor L"\nEnter password (will not be echoed) for %s: "
+#define MReAskPsw L"\nReenter password: "
+#define MNotMatchPsw L"\nERROR: Passwords do not match\n"
+#define MErrWrite L"Write error in the file %s"
+#define MErrRead L"Read error in the file %s"
+#define MErrSeek L"Seek error in the file %s"
+#define MErrFClose L"Cannot close the file %s"
+#define MErrOutMem L"Not enough memory"
+#define MErrBrokenArc L"Corrupt archive - use 'Repair' command"
+#define MProgAborted L"Program aborted"
+#define MErrRename L"\nCannot rename %s to %s"
+#define MAbsNextVol L"\nCannot find volume %s"
+#define MBreak L"\nUser break\n"
+#define MAskCreatVol L"\nCreate next volume ?"
+#define MAskNextDisk L"\nDisk full. Insert next"
+#define MCreatVol L"\n\nCreating %sarchive %s\n"
+#define MAskNextVol L"\nInsert disk with %s"
+#define MTestVol L"\n\nTesting archive %s\n"
+#define MExtrVol L"\n\nExtracting from %s\n"
+#define MConverting L"\nConverting %s"
+#define MCvtToSFX L"\nConvert archives to SFX"
+#define MCvtFromSFX L"\nRemoving SFX module"
+#define MNotSFX L"\n%s is not SFX archive"
+#define MNotRAR L"\n%s is not RAR archive"
+#define MNotFirstVol L"\n%s is not the first volume"
+#define MCvtOldFormat L"\n%s - cannot convert to SFX archive with old format"
+#define MCannotCreate L"\nCannot create %s"
+#define MCannotOpen L"\nCannot open %s"
+#define MUnknownMeth L"\nUnknown method in %s"
+#define MNewRarFormat L"\nUnsupported archive format. Please update RAR to a newer version."
+#define MOk L" OK"
+#define MDone L"\nDone"
+#define MLockingArc L"\nLocking archive"
+#define MNotMdfOld L"\n\nERROR: Cannot modify old format archive"
+#define MNotMdfLock L"\n\nERROR: Locked archive"
+#define MNotMdfVol L"\n\nERROR: Cannot modify volume"
+#define MPackAskReg L"\nEvaluation copy. Please register.\n"
+#define MCreateArchive L"\nCreating %sarchive %s\n"
+#define MUpdateArchive L"\nUpdating %sarchive %s\n"
+#define MAddSolid L"solid "
+#define MAddFile L"\nAdding %-58s "
+#define MUpdFile L"\nUpdating %-58s "
+#define MAddPoints L"\n... %-58s "
+#define MMoveDelFiles L"\n\nDeleting files %s..."
+#define MMoveDelDirs L"and directories"
+#define MMoveDelFile L"\nDeleting %-30s"
+#define MMoveDeleted L" deleted"
+#define MMoveNotDeleted L" NOT DELETED"
+#define MClearAttrib L"\n\nClearing attributes..."
+#define MMoveDelDir L"\nDeleting directory %-30s"
+#define MWarErrFOpen L"\nWARNING: Cannot open %d %s"
+#define MErrOpenFiles L"files"
+#define MErrOpenFile L"file"
+#define MAddNoFiles L"\nWARNING: No files"
+#define MMdfEncrSol L"\n%s: encrypted"
+#define MAddAnalyze L"\nAnalyzing archived files: "
+#define MRepacking L"\nRepacking archived files: "
+#define MCRCFailed L"\n%-20s - checksum error"
+#define MExtrTest L"\n\nTesting archive %s\n"
+#define MExtracting L"\n\nExtracting from %s\n"
+#define MUseCurPsw L"\n%s - use current password ?"
+#define MCreatDir L"\nCreating %-56s"
+#define MExtrSkipFile L"\nSkipping %-56s"
+#define MExtrTestFile L"\nTesting %-56s"
+#define MExtrFile L"\nExtracting %-56s"
+#define MExtrPoints L"\n... %-56s"
+#define MExtrErrMkDir L"\nCannot create directory %s"
+#define MExtrPrinting L"\n------ Printing %s\n\n"
+#define MEncrBadCRC L"\nChecksum error in the encrypted file %s. Corrupt file or wrong password."
+#define MExtrNoFiles L"\nNo files to extract"
+#define MExtrAllOk L"\nAll OK"
+#define MExtrTotalErr L"\nTotal errors: %ld"
+#define MAskReplace L"\n\nWould you like to replace the existing file %s\n%6s bytes, modified on %s\nwith a new one\n%6s bytes, modified on %s\n"
+#define MAskOverwrite L"\nOverwrite %s ?"
+#define MAskNewName L"\nEnter new name: "
+#define MHeaderBroken L"\nCorrupt header is found"
+#define MMainHeaderBroken L"\nMain archive header is corrupt"
+#define MLogFileHead L"\n%s - the file header is corrupt"
+#define MLogProtectHead L"The data recovery header is corrupt"
+#define MReadStdinCmt L"\nReading comment from stdin\n"
+#define MReadCommFrom L"\nReading comment from %s"
+#define MDelComment L"\nDeleting comment from %s"
+#define MAddComment L"\nAdding comment to %s"
+#define MFCommAdd L"\nAdding file comments"
+#define MAskFComm L"\n\nReading comment for %s : %s from stdin\n"
+#define MLogCommBrk L"\nThe archive comment is corrupt"
+#define MCommAskCont L"\nPress 'Enter' to continue or 'Q' to quit:"
+#define MWriteCommTo L"\nWrite comment to %s"
+#define MCommNotPres L"\nComment is not present"
+#define MDelFrom L"\nDeleting from %s"
+#define MDeleting L"\nDeleting %s"
+#define MEraseArc L"\nErasing empty archive %s"
+#define MNoDelFiles L"\nNo files to delete"
+#define MLogTitle L"-------- %2d %s %d, archive %s"
+#define MPathTooLong L"\nERROR: Path too long\n"
+#define MListArchive L"Archive"
+#define MListDetails L"Details"
+#define MListSolid L"solid"
+#define MListSFX L"SFX"
+#define MListVolume L"volume"
+#define MListRR L"recovery record"
+#define MListLock L"lock"
+#define MListEnc L"encrypted"
+#define MListEncHead L"encrypted headers"
+#define MListTitleL L" Attributes Size Date Time Name"
+#define MListTitleV L" Attributes Size Packed Ratio Date Time Checksum Name"
+#define MListName L"Name"
+#define MListType L"Type"
+#define MListFile L"File"
+#define MListDir L"Directory"
+#define MListUSymlink L"Unix symbolic link"
+#define MListWSymlink L"Windows symbolic link"
+#define MListJunction L"NTFS junction point"
+#define MListHardlink L"Hard link"
+#define MListCopy L"File reference"
+#define MListStream L"NTFS alternate data stream"
+#define MListTarget L"Target"
+#define MListSize L"Size"
+#define MListPacked L"Packed size"
+#define MListRatio L"Ratio"
+#define MListMtime L"mtime"
+#define MListCtime L"ctime"
+#define MListAtime L"atime"
+#define MListModified L"Modified"
+#define MListCreated L"Created"
+#define MListAccessed L"Accessed"
+#define MListAttr L"Attributes"
+#define MListFlags L"Flags"
+#define MListCompInfo L"Compression"
+#define MListHostOS L"Host OS"
+#define MListFileVer L"File version"
+#define MListService L"Service"
+#define MListUOHead L"\n Unix Owner/Group data: %-14s %-14s"
+#define MListNTACLHead L"\n NTFS security data"
+#define MListStrmHead L"\n NTFS stream: %s"
+#define MListUnkHead L"\n Unknown subheader type: 0x%04x"
+#define MFileComment L"\nComment: "
+#define MYes L"Yes"
+#define MNo L"No"
+#define MListNoFiles L" 0 files\n"
+#define MRprReconstr L"\nReconstructing %s"
+#define MRprBuild L"\nBuilding %s"
+#define MRprOldFormat L"\nCannot repair archive with old format"
+#define MRprFind L"\nFound %s"
+#define MRprAskIsSol L"\nThe archive header is corrupt. Mark archive as solid ?"
+#define MRprNoFiles L"\nNo files found"
+#define MLogUnexpEOF L"\nUnexpected end of archive"
+#define MRepAskReconst L"\nReconstruct archive structure ?"
+#define MRRSearch L"\nSearching for recovery record"
+#define MAnalyzeFileData L"\nAnalyzing file data"
+#define MRecRNotFound L"\nData recovery record not found"
+#define MRecRFound L"\nData recovery record found"
+#define MRecSecDamage L"\nSector %ld (offsets %lX...%lX) damaged"
+#define MRecCorrected L" - data recovered"
+#define MRecFailed L" - cannot recover data"
+#define MAddRecRec L"\nAdding data recovery record"
+#define MEraseForVolume L"\n\nErasing contents of drive %c:\n"
+#define MGetOwnersError L"\nWARNING: Cannot get %s owner and group\n"
+#define MErrGetOwnerID L"\nWARNING: Cannot get owner %s ID\n"
+#define MErrGetGroupID L"\nWARNING: Cannot get group %s ID\n"
+#define MOwnersBroken L"\nERROR: %s group and owner data are corrupt\n"
+#define MSetOwnersError L"\nWARNING: Cannot set %s owner and group\n"
+#define MErrLnkRead L"\nWARNING: Cannot read symbolic link %s"
+#define MSymLinkExists L"\nWARNING: Symbolic link %s already exists"
+#define MAskRetryCreate L"\nCannot create %s. Retry ?"
+#define MDataBadCRC L"\n%-20s : packed data checksum error in volume %s"
+#define MFileRO L"\n%s is read-only"
+#define MACLGetError L"\nWARNING: Cannot get %s security data\n"
+#define MACLSetError L"\nWARNING: Cannot set %s security data\n"
+#define MACLBroken L"\nERROR: %s security data are corrupt\n"
+#define MACLUnknown L"\nWARNING: Unknown format of %s security data\n"
+#define MStreamBroken L"\nERROR: %s stream data are corrupt\n"
+#define MStreamUnknown L"\nWARNING: Unknown format of %s stream data\n"
+#define MInvalidName L"\nERROR: Invalid file name %s"
+#define MProcessArc L"\n\nProcessing archive %s"
+#define MCorrectingName L"\nWARNING: Attempting to correct the invalid file or directory name"
+#define MUnpCannotMerge L"\nWARNING: You need to start extraction from a previous volume to unpack %s"
+#define MUnknownOption L"\nERROR: Unknown option: %s"
+#define MSubHeadCorrupt L"\nERROR: Corrupt data header found, ignored"
+#define MSubHeadUnknown L"\nWARNING: Unknown data header format, ignored"
+#define MSubHeadDataCRC L"\nERROR: Corrupt %s data block"
+#define MSubHeadType L"\nData header type: %s"
+#define MScanError L"\nCannot read contents of %s"
+#define MNotVolume L"\n%s is not volume"
+#define MRecVolDiffSets L"\nERROR: %s and %s belong to different sets"
+#define MRecVolMissing L"\n%d volumes missing"
+#define MRecVolFound L"\n%d recovery volumes found"
+#define MRecVolAllExist L"\nNothing to reconstruct"
+#define MRecVolCannotFix L"\nReconstruction impossible"
+#define MReconstructing L"\nReconstructing..."
+#define MCreating L"\nCreating %s"
+#define MRenaming L"\nRenaming %s to %s"
+#define MNTFSRequired L"\nWrite error: only NTFS file system supports files larger than 4 GB"
+#define MFAT32Size L"\nWARNING: FAT32 file system does not support 4 GB or larger files"
+#define MErrChangeAttr L"\nWARNING: Cannot change attributes of %s"
+#define MWrongSFXVer L"\nERROR: default SFX module does not support RAR %d.%d archives"
+#define MHeadEncMismatch L"\nCannot change the header encryption mode in already encrypted archive"
+#define MCannotEmail L"\nCannot email the file %s"
+#define MCopyrightS L"\nRAR SFX archive"
+#define MSHelpCmd L"\n\n<Commands>"
+#define MSHelpCmdE L"\n -x Extract from archive (default)"
+#define MSHelpCmdT L"\n -t Test archive files"
+#define MSHelpCmdV L"\n -v Verbosely list contents of archive"
+#define MRecVolLimit L"\nTotal number of usual and recovery volumes must not exceed %d"
+#define MVolumeNumber L"volume %d"
+#define MCannotDelete L"\nCannot delete %s"
+#define MRecycleFailed L"\nCannot move some files and directories to Recycle Bin"
+#define MCalcCRC L"\nCalculating the checksum"
+#define MTooLargeSFXArc L"\nToo large SFX archive. Windows cannot run the executable file exceeding 4 GB."
+#define MCalcCRCAllVol L"\nCalculating checksums of all volumes."
+#define MNotEnoughDisk L"\nERROR: Not enough disk space for %s."
+#define MNewerRAR L"\nYou may need a newer version of RAR."
+#define MUnkEncMethod L"\nUnknown encryption method in %s"
+#define MWrongPassword L"\nThe specified password is incorrect."
+#define MWrongFilePassword L"\nIncorrect password for %s"
+#define MAreaDamaged L"\nCorrupt %d bytes at %08x %08x"
+#define MBlocksRecovered L"\n%u blocks are recovered, %u blocks are relocated"
+#define MRRDamaged L"\nRecovery record is corrupt."
+#define MTestingRR L"\nTesting the recovery record"
+#define MFailed L"Failed"
+#define MIncompatSwitch L"\n%s switch is not supported for RAR %d.x archive format."
+#define MSearchDupFiles L"\nSearching for identical files"
+#define MNumFound L"%d found."
+#define MUnknownExtra L"\nUnknown extra field in %s."
+#define MCorruptExtra L"\nCorrupt %s extra field in %s."
+#define MCopyError L"\nCannot copy %s to %s."
+#define MCopyErrorHint L"\nYou need to unpack the entire archive to create file reference entries."
+#define MCopyingData L"\nCopying data"
+#define MErrCreateLnkS L"\nCannot create symbolic link %s"
+#define MErrCreateLnkH L"\nCannot create hard link %s"
+#define MErrLnkTarget L"\nYou need to unpack the link target first"
+#define MNeedAdmin L"\nYou may need to run RAR as administrator"
+#define MDictOutMem L"\nNot enough memory for %d MB compression dictionary, changed to %d MB."
+#define MUseSmalllerDict L"\nPlease use a smaller compression dictionary."
+#define MOpenErrAtime L"\nYou may need to remove -tsp switch to open this file."
+#define MErrReadInfo L"\nChoose 'Ignore' to continue with the already read file part only, 'Ignore all' to do it for all read errors, 'Retry' to repeat read and 'Quit' to abort."
+#define MErrReadTrunc L"\n%s is archived incompletely because of read error.\n"
+#define MErrReadCount L"\n%u files are archived incompletely because of read errors."
+#define MDirNameExists L"\nDirectory with such name already exists"
+#define MStdinNoInput L"\nKeyboard input is not allowed when reading data from stdin"
+#define MTruncPsw L"\nPassword exceeds the maximum allowed length of %u characters and will be truncated."
+#define MAdjustValue L"\nAdjusting %s value to %s."
+#define MOpFailed L"\nOperation failed"
+#define MSkipEncArc L"\nSkipping the encrypted archive %s"
+#define MOrigName L"Original name"
+#define MOriginalTime L"Original time"
+#define MFileRenamed L"\n%s is renamed to %s"
diff --git a/unrar/unrar/log.cpp b/unrar/unrar/log.cpp
index 966e9de..8bbe8ee 100644
--- a/unrar/unrar/log.cpp
+++ b/unrar/unrar/log.cpp
@@ -1,23 +1,36 @@
#include "rar.hpp"
-static char LogName[NM];
+static wchar LogName[NM];
+static RAR_CHARSET LogCharset=RCH_DEFAULT;
-void InitLogOptions(char *LogName)
+void InitLogOptions(const wchar *LogFileName,RAR_CHARSET CSet)
{
- strcpy(::LogName,LogName);
+ wcsncpyz(LogName,LogFileName,ASIZE(LogName));
+ LogCharset=CSet;
}
#ifndef SILENT
-void Log(const char *ArcName,const char *Format,...)
+void Log(const wchar *ArcName,const wchar *fmt,...)
{
- safebuf char Msg[2*NM+1024];
- va_list ArgPtr;
- va_start(ArgPtr,Format);
- vsprintf(Msg,Format,ArgPtr);
- va_end(ArgPtr);
- eprintf("%s",Msg);
+ // Preserve the error code for possible following system error message.
+ int Code=ErrHandler.GetSystemErrorCode();
+
+ uiAlarm(UIALARM_ERROR);
+
+ // This buffer is for format string only, not for entire output,
+ // so it can be short enough.
+ wchar fmtw[1024];
+ PrintfPrepareFmt(fmt,fmtw,ASIZE(fmtw));
+
+ safebuf wchar Msg[2*NM+1024];
+ va_list arglist;
+ va_start(arglist,fmt);
+ vswprintf(Msg,ASIZE(Msg),fmtw,arglist);
+ va_end(arglist);
+ eprintf(L"%ls",Msg);
+ ErrHandler.SetSystemErrorCode(Code);
}
#endif
diff --git a/unrar/unrar/log.hpp b/unrar/unrar/log.hpp
index 52d6b8d..008ef11 100644
--- a/unrar/unrar/log.hpp
+++ b/unrar/unrar/log.hpp
@@ -1,18 +1,12 @@
#ifndef _RAR_LOG_
#define _RAR_LOG_
-void InitLogOptions(char *LogName);
-
-#ifndef SILENT
-void Log(const char *ArcName,const char *Format,...);
-#endif
+void InitLogOptions(const wchar *LogFileName,RAR_CHARSET CSet);
#ifdef SILENT
-#ifdef __GNUC__
-#define Log(args...)
+inline void Log(const wchar *ArcName,const wchar *fmt,...) {}
#else
-inline void Log(const char *a,const char *b,const char *c=NULL,const char *d=NULL) {}
-#endif
+void Log(const wchar *ArcName,const wchar *fmt,...);
#endif
#endif
diff --git a/unrar/unrar/makefile.unix b/unrar/unrar/makefile
index d22ed58..55af49b 100644
--- a/unrar/unrar/makefile.unix
+++ b/unrar/unrar/makefile
@@ -1,15 +1,14 @@
#
# Makefile for UNIX - unrar
-#
-# Note: you have to 'make clean' before you can build
-# the sfx module
-#
# Linux using GCC
-CXX=g++
-CXXFLAGS=-O2
-DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
+CXX=c++
+CXXFLAGS=-O2 -Wno-logical-op-parentheses -Wno-switch -Wno-dangling-else
+LIBFLAGS=-fPIC
+DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -DRAR_SMP
STRIP=strip
+AR=ar
+LDFLAGS=-pthread
DESTDIR=/usr
# Linux using LCC
@@ -17,6 +16,17 @@ DESTDIR=/usr
#CXXFLAGS=-O2
#DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
#STRIP=strip
+#AR=ar
+#DESTDIR=/usr
+
+# CYGWIN using GCC
+#CXX=c++
+#CXXFLAGS=-O2
+#LIBFLAGS=
+#DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -DRAR_SMP
+#STRIP=strip
+#AR=ar
+#LDFLAGS=-pthread
#DESTDIR=/usr
# HP UX using aCC
@@ -24,6 +34,7 @@ DESTDIR=/usr
#CXXFLAGS=-AA +O2 +Onolimit
#DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
#STRIP=strip
+#AR=ar
#DESTDIR=/usr
# IRIX using GCC
@@ -31,13 +42,15 @@ DESTDIR=/usr
#CXXFLAGS=-O2
#DEFINES=-DBIG_ENDIAN -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_BSD_COMPAT -D_XOPEN_SOURCE -D_XOPEN_SOURCE_EXTENDED=1
#STRIP=strip
+#AR=ar
#DESTDIR=/usr
# IRIX using MIPSPro (experimental)
#CXX=CC
#CXXFLAGS=-O2 -mips3 -woff 1234,1156,3284 -LANG:std
-#DEFINES=-DBIG_ENDIAN -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_BSD_COMPAT -DNATIVE_INT64 -DInt64=int64_t
+#DEFINES=-DBIG_ENDIAN -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_BSD_COMPAT -Dint64=int64_t
#STRIP=strip
+#AR=ar
#DESTDIR=/usr
# AIX using xlC (IBM VisualAge C++ 5.0)
@@ -46,13 +59,15 @@ DESTDIR=/usr
#DEFINES=-D_LARGE_FILES -D_LARGE_FILE_API
#LIBS=-lbsd
#STRIP=strip
+#AR=ar
#DESTDIR=/usr
-# Solaris using CC (SUN Forte Developer 7 C++)
+# Solaris using CC
#CXX=CC
-#CXXFLAGS=-xO2 -xbuiltin=%all -xinline=%auto
-#DEFINES=-DBIG_ENDIAN -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
+#CXXFLAGS=-fast -erroff=wvarhidemem
+#DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
#STRIP=strip
+#AR=ar
#DESTDIR=/usr
# Solaris using GCC (optimized for UltraSPARC 1 CPU)
@@ -60,19 +75,22 @@ DESTDIR=/usr
#CXXFLAGS=-O3 -mcpu=v9 -mtune=ultrasparc -m32
#DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
#STRIP=/usr/ccs/bin/strip
+#AR=/usr/ccs/bin/ar
#DESTDIR=/usr
# Tru64 5.1B using GCC3
#CXX=g++
#CXXFLAGS=-O2 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_XOPEN_SOURCE=500
#STRIP=strip
+#AR=ar
#LDFLAGS=-rpath /usr/local/gcc/lib
#DESTDIR=/usr
# Tru64 5.1B using DEC C++
#CXX=cxx
-#CXXFLAGS=-O4 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -DNATIVE_INT64 -DInt64=long
+#CXXFLAGS=-O4 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -Dint64=long
#STRIP=strip
+#AR=ar
#LDFLAGS=
#DESTDIR=/usr
@@ -80,6 +98,7 @@ DESTDIR=/usr
#CXX=g++
#CXXFLAGS=-O2 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -fexceptions
#STRIP=strip
+#AR=ar
#LDFLAGS=-fexceptions
#DESTDIR=/usr
@@ -89,21 +108,25 @@ DESTDIR=/usr
#CXXFLAGS=-O2
#DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
#STRIP=arm-linux-strip
+#AR=arm-linux-ar
#LDFLAGS=-static
#DESTDIR=/usr
##########################
-COMPILE=$(CXX) $(CXXFLAGS) $(DEFINES)
+COMPILE=$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(DEFINES)
LINK=$(CXX)
-UNRAR_OBJ=filestr.o recvol.o rs.o scantree.o
-LIB_OBJ=filestr.o scantree.o dll.o
+WHAT=UNRAR
-OBJECTS=rar.o strlist.o strfn.o pathfn.o int64.o savepos.o global.o file.o filefn.o filcreat.o \
- archive.o arcread.o unicode.o system.o isnt.o crypt.o crc.o rawread.o encname.o \
- resource.o match.o timefn.o rdwrfn.o consio.o options.o ulinks.o errhnd.o rarvm.o \
- rijndael.o getbits.o sha1.o extinfo.o extract.o volume.o list.o find.o unpack.o cmddata.o
+UNRAR_OBJ=filestr.o recvol.o rs.o scantree.o qopen.o
+LIB_OBJ=filestr.o scantree.o dll.o qopen.o
+
+OBJECTS=rar.o strlist.o strfn.o pathfn.o smallfn.o global.o file.o filefn.o filcreat.o \
+ archive.o arcread.o unicode.o system.o crypt.o crc.o rawread.o encname.o \
+ resource.o match.o timefn.o rdwrfn.o consio.o options.o errhnd.o rarvm.o secpassword.o \
+ rijndael.o getbits.o sha1.o sha256.o blake2s.o hash.o extinfo.o extract.o volume.o \
+ list.o find.o unpack.o headers.o threadpool.o rs16.o cmddata.o ui.o
.cpp.o:
$(COMPILE) -D$(WHAT) -c $<
@@ -115,9 +138,13 @@ install: install-unrar
uninstall: uninstall-unrar
clean:
- @rm -f *.o *.bak *~
+ @rm -f *.bak *~
+ @rm -f $(OBJECTS) $(UNRAR_OBJ) $(LIB_OBJ)
+ @rm -f unrar libunrar.*
+
+# We removed 'clean' from dependencies, because it prevented parallel
+# 'make -Jn' builds.
-unrar: WHAT=UNRAR
unrar: $(OBJECTS) $(UNRAR_OBJ)
@rm -f unrar
$(LINK) -o unrar $(LDFLAGS) $(OBJECTS) $(UNRAR_OBJ) $(LIBS)
@@ -130,18 +157,21 @@ sfx: $(OBJECTS)
$(STRIP) default.sfx
lib: WHAT=RARDLL
+lib: CXXFLAGS+=$(LIBFLAGS)
lib: $(OBJECTS) $(LIB_OBJ)
- @rm -f libunrar.so
+ @rm -f libunrar.*
$(LINK) -shared -o libunrar.so $(LDFLAGS) $(OBJECTS) $(LIB_OBJ)
+ $(AR) rcs libunrar.a $(OBJECTS) $(LIB_OBJ)
install-unrar:
- install unrar $(DESTDIR)/bin
+ install -D unrar $(DESTDIR)/bin/unrar
uninstall-unrar:
rm -f $(DESTDIR)/bin/unrar
install-lib:
install libunrar.so $(DESTDIR)/lib
+ install libunrar.a $(DESTDIR)/lib
uninstall-lib:
rm -f $(DESTDIR)/lib/libunrar.so
diff --git a/unrar/unrar/makefile.bcc b/unrar/unrar/makefile.bcc
deleted file mode 100644
index 7b833c6..0000000
--- a/unrar/unrar/makefile.bcc
+++ /dev/null
@@ -1,501 +0,0 @@
-.AUTODEPEND
-
-basepath = $(BASEPATHCC)
-binpath = $(basepath)\bin
-libpath = $(basepath)\lib
-rarpath = .
-incpath = $(basepath)\include;$(rarpath)
-
-cc = $(binpath)\bcc32
-link = $(binpath)\ilink32
-
-objpath = .
-guiopt = -WC -H=$(objpath)\rar.csm
-
-!ifndef RARDLL
-!ifndef GUI
-guiopt=$(guiopt) -x-
-!endif
-!ifdef SFX_MODULE
-guiopt=$(guiopt) -x-
-!endif
-!endif
-
-!ifdef DEBUG
-optdeb=-Od -k -vi- -DDEBUG
-!else
-# -O is not safe to use with -pr and int64 return values, so let's turn it off
-optdeb=-O1 -O- -k-
-#optdeb=-Ob -Oe -Og -Oi -Ol -Om -Op -OS -Ov -Z -Oc
-!endif
-
-
-optunrar=-DUNRAR
-linkdest=unrar.exe
-
-!ifdef SFX_MODULE
-optunrar=-DUNRAR -DSFX_MODULE
-linkdest=sfx.exe
-!endif
-
-linkopt = -L$(libpath) -ap -c -v -s -V4.0 -Gn
-compopt = -P -c -I$(incpath) -R -v -vi -w-pch -w-par -K -f-\
- -ff- -a4 -pr -RT- $(optdeb) $(guiopt) $(optunrar) -d -w-8072
-
-!ifdef RARDLL
-SILENT=true
-linkdest=unrar.dll
-linkopt=$(linkopt) -Tpd
-compopt=$(compopt) -DRARDLL
-!else
-linkopt=$(linkopt) -Tpe -B:0x400000
-!endif
-
-!ifdef SILENT
-compopt=$(compopt) -DSILENT
-!endif
-
-
-rar: $(linkdest)
-
-Dep_SFX= \
- $(objpath)\strlist.obj\
- $(objpath)\strfn.obj\
- $(objpath)\pathfn.obj\
- $(objpath)\cmddata.obj\
- $(objpath)\consio.obj\
- $(objpath)\int64.obj\
- $(objpath)\savepos.obj\
- $(objpath)\file.obj\
- $(objpath)\filefn.obj\
- $(objpath)\filcreat.obj\
- $(objpath)\sha1.obj\
- $(objpath)\archive.obj\
- $(objpath)\arcread.obj\
- $(objpath)\unicode.obj\
- $(objpath)\system.obj\
- $(objpath)\isnt.obj\
- $(objpath)\crc.obj\
- $(objpath)\crypt.obj\
- $(objpath)\rijndael.obj\
- $(objpath)\rawread.obj\
- $(objpath)\encname.obj\
- $(objpath)\resource.obj\
- $(objpath)\match.obj\
- $(objpath)\find.obj\
- $(objpath)\timefn.obj\
- $(objpath)\getbits.obj\
- $(objpath)\rarvm.obj\
- $(objpath)\rdwrfn.obj\
- $(objpath)\options.obj\
- $(objpath)\ulinks.obj\
- $(objpath)\errhnd.obj\
- $(objpath)\volume.obj\
- $(objpath)\rs.obj\
- $(objpath)\recvol.obj\
- $(objpath)\extinfo.obj\
- $(objpath)\extract.obj\
- $(objpath)\unpack.obj\
- $(objpath)\rar.obj\
- $(objpath)\global.obj
-
-Dep_Unrar = \
- $(objpath)\filestr.obj\
- $(objpath)\scantree.obj
-
-Dep_Dll = \
- $(objpath)\dll.obj
-
-#Dep_SFXOnly = $(objpath)\rtl.obj
-
-!ifndef GUI
-!ifndef SILENT
-Dep_Console = \
- $(objpath)\list.obj
-!endif
-!endif
-
-!ifdef SFX_MODULE
-Dep = $(Dep_SFX) $(Dep_SFXOnly)
-!else
-Dep = $(Dep_SFX) $(Dep_Unrar)
-!endif
-
-!ifndef GUI
-Dep = $(Dep) $(Dep_Console)
-!endif
-
-!ifdef RARDLL
-Dep = $(Dep) $(Dep_Dll)
-!endif
-
-!ifdef GUI
-$(linkdest) : $(Dep)
- echo Done
-!else
-$(linkdest) : $(Dep)
- $(link) @&&|
- $(linkopt) +
-#!ifdef SFX_MODULE
-#$(objpath)\dummy.obj+
-#$(objpath)\ll.obj+
-#$(objpath)\rtl.obj+
-#!else
-!ifdef RARDLL
-$(libpath)\c0d32.obj+
-!else
-$(libpath)\c0x32.obj+
-!endif
-#!endif
-$(objpath)\strlist.obj+
-$(objpath)\strfn.obj+
-$(objpath)\pathfn.obj+
-$(objpath)\int64.obj+
-$(objpath)\savepos.obj+
-$(objpath)\global.obj+
-$(objpath)\file.obj+
-$(objpath)\filefn.obj+
-$(objpath)\filcreat.obj+
-$(objpath)\sha1.obj+
-$(objpath)\archive.obj+
-$(objpath)\arcread.obj+
-$(objpath)\unicode.obj+
-$(objpath)\system.obj+
-$(objpath)\isnt.obj+
-$(objpath)\crc.obj+
-$(objpath)\crypt.obj+
-$(objpath)\rijndael.obj+
-$(objpath)\rawread.obj+
-$(objpath)\encname.obj+
-$(objpath)\resource.obj+
-$(objpath)\match.obj+
-$(objpath)\find.obj+
-!ifndef SFX_MODULE
-$(objpath)\filestr.obj+
-$(objpath)\scantree.obj+
-!endif
-$(objpath)\timefn.obj+
-$(objpath)\getbits.obj+
-$(objpath)\rarvm.obj+
-$(objpath)\rdwrfn.obj+
-$(objpath)\consio.obj+
-$(objpath)\cmddata.obj+
-$(objpath)\options.obj+
-$(objpath)\ulinks.obj+
-$(objpath)\volume.obj+
-$(objpath)\extinfo.obj+
-$(objpath)\extract.obj+
-$(objpath)\rs.obj+
-$(objpath)\recvol.obj+
-!ifndef SILENT
-!ifndef GUI
-$(objpath)\list.obj+
-!endif
-!endif
-!ifdef RARDLL
-$(objpath)\dll.obj+
-!endif
-$(objpath)\errhnd.obj+
-$(objpath)\unpack.obj+
-$(objpath)\rar.obj
-$<,$*
-$(libpath)\cw32.lib+
-$(libpath)\import32.lib
-!ifdef RARDLL
-$(rarpath)\dll.def
-!else
-
-!endif
-|
-!endif
-
-$(objpath)\rar.obj : $(rarpath)\rar.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\rar.cpp
-|
-
-$(objpath)\strlist.obj : $(rarpath)\strlist.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\strlist.cpp
-|
-
-$(objpath)\strfn.obj : $(rarpath)\strfn.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\strfn.cpp
-|
-
-$(objpath)\pathfn.obj : $(rarpath)\pathfn.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\pathfn.cpp
-|
-
-$(objpath)\int64.obj : $(rarpath)\int64.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\int64.cpp
-|
-
-$(objpath)\savepos.obj : $(rarpath)\savepos.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\savepos.cpp
-|
-
-$(objpath)\global.obj : $(rarpath)\global.cpp
- $(cc) -q @&&|
- $(compopt) -H- -o$@ $(rarpath)\global.cpp
-|
-
-$(objpath)\file.obj : $(rarpath)\file.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\file.cpp
-|
-
-$(objpath)\filefn.obj : $(rarpath)\filefn.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\filefn.cpp
-|
-
-$(objpath)\filestr.obj : $(rarpath)\filestr.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\filestr.cpp
-|
-
-$(objpath)\filcreat.obj : $(rarpath)\filcreat.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\filcreat.cpp
-|
-
-$(objpath)\sha1.obj : $(rarpath)\sha1.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\sha1.cpp
-|
-
-$(objpath)\ec.obj : $(rarpath)\ec.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\ec.cpp
-|
-
-$(objpath)\av.obj : $(rarpath)\av.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\av.cpp
-|
-
-$(objpath)\archive.obj : $(rarpath)\archive.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\archive.cpp
-|
-
-$(objpath)\arcread.obj : $(rarpath)\arcread.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\arcread.cpp
-|
-
-$(objpath)\unicode.obj : $(rarpath)\unicode.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\unicode.cpp
-|
-
-$(objpath)\system.obj : $(rarpath)\system.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\system.cpp
-|
-
-$(objpath)\isnt.obj : $(rarpath)\isnt.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\isnt.cpp
-|
-
-$(objpath)\crc.obj : $(rarpath)\crc.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\crc.cpp
-|
-
-$(objpath)\crypt.obj : $(rarpath)\crypt.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\crypt.cpp
-|
-
-$(objpath)\rijndael.obj : $(rarpath)\rijndael.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\rijndael.cpp
-|
-
-$(objpath)\rawread.obj : $(rarpath)\rawread.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\rawread.cpp
-|
-
-$(objpath)\rawwrite.obj : $(rarpath)\rawwrite.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\rawwrite.cpp
-|
-
-$(objpath)\encname.obj : $(rarpath)\encname.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\encname.cpp
-|
-
-$(objpath)\resource.obj : $(rarpath)\resource.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\resource.cpp
-|
-
-$(objpath)\match.obj : $(rarpath)\match.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\match.cpp
-|
-
-$(objpath)\find.obj : $(rarpath)\find.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\find.cpp
-|
-
-$(objpath)\scantree.obj : $(rarpath)\scantree.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\scantree.cpp
-|
-
-$(objpath)\timefn.obj : $(rarpath)\timefn.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\timefn.cpp
-|
-
-$(objpath)\getbits.obj : $(rarpath)\getbits.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\getbits.cpp
-|
-
-$(objpath)\rarvm.obj : $(rarpath)\rarvm.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\rarvm.cpp
-|
-
-$(objpath)\putbits.obj : $(rarpath)\putbits.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\putbits.cpp
-|
-
-$(objpath)\pack.obj : $(rarpath)\pack.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\pack.cpp
-|
-
-$(objpath)\packbord.obj : $(rarpath)\packbord.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\packbord.cpp
-|
-
-$(objpath)\packanlz.obj : $(rarpath)\packanlz.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\packanlz.cpp
-|
-
-$(objpath)\cblock.obj : $(rarpath)\cblock.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\cblock.cpp
-|
-
-$(objpath)\add.obj : $(rarpath)\add.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\add.cpp
-|
-
-$(objpath)\addlist.obj : $(rarpath)\addlist.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\addlist.cpp
-|
-
-$(objpath)\procarc.obj : $(rarpath)\procarc.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\procarc.cpp
-|
-
-$(objpath)\sfx.obj : $(rarpath)\sfx.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\sfx.cpp
-|
-
-$(objpath)\comment.obj : $(rarpath)\comment.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\comment.cpp
-|
-
-$(objpath)\rs.obj : $(rarpath)\rs.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\rs.cpp
-|
-
-$(objpath)\recvol.obj : $(rarpath)\recvol.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\recvol.cpp
-|
-
-$(objpath)\repair.obj : $(rarpath)\repair.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\repair.cpp
-|
-
-$(objpath)\rdwrfn.obj : $(rarpath)\rdwrfn.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\rdwrfn.cpp
-|
-
-$(objpath)\consio.obj : $(rarpath)\consio.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\consio.cpp
-|
-
-$(objpath)\cmddata.obj : $(rarpath)\cmddata.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\cmddata.cpp
-|
-
-$(objpath)\options.obj : $(rarpath)\options.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\options.cpp
-|
-
-$(objpath)\ulinks.obj : $(rarpath)\ulinks.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\ulinks.cpp
-|
-
-$(objpath)\errhnd.obj : $(rarpath)\errhnd.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\errhnd.cpp
-|
-
-$(objpath)\volume.obj : $(rarpath)\volume.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\volume.cpp
-|
-
-$(objpath)\extinfo.obj : $(rarpath)\extinfo.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\extinfo.cpp
-|
-
-
-$(objpath)\extract.obj : $(rarpath)\extract.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\extract.cpp
-|
-
-$(objpath)\list.obj : $(rarpath)\list.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\list.cpp
-|
-
-$(objpath)\rtl.obj : $(rarpath)\rtl.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\rtl.cpp
-|
-
-$(objpath)\unpack.obj : $(rarpath)\unpack.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\unpack.cpp
-|
-
-$(objpath)\dll.obj : $(rarpath)\dll.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\dll.cpp
-| \ No newline at end of file
diff --git a/unrar/unrar/makefile.cygmin b/unrar/unrar/makefile.cygmin
deleted file mode 100644
index 9e0cfa8..0000000
--- a/unrar/unrar/makefile.cygmin
+++ /dev/null
@@ -1,54 +0,0 @@
-#
-# Makefile for cygmin/mingw - unrar
-#
-# Note: you have to 'make clean' before you can build
-# the sfx module
-#
-
-# POSIX using Cygmin GCC 3.3.1
-#CXX = g++
-#CXXFLAGS = -O2 -Wno-deprecated
-#DEFINES = -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -DLITTLE_ENDIAN
-
-# Win32 using Cygmin GCC 3.3.1
-CXX = g++ -mno-cygwin
-CXXFLAGS = -O2 -Wno-deprecated
-DEFINES = -D_MSC_VER -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
-
-# Win32 using Mingw32 GCC 3.3.2
-#CXX = g++
-#CXXFLAGS = -O2 -Wno-deprecated
-#DEFINES = -D_MSC_VER -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
-
-##########################
-
-COMPILE=$(CXX) $(CXXFLAGS) $(DEFINES)
-LINK=$(CXX)
-
-UNRAR_OBJ=filestr.o recvol.o rs.o scantree.o
-
-OBJECTS=rar.o strlist.o strfn.o pathfn.o int64.o savepos.o global.o \
- file.o filefn.o filcreat.o archive.o arcread.o unicode.o \
- system.o isnt.o crypt.o crc.o rawread.o encname.o \
- resource.o match.o timefn.o rdwrfn.o consio.o options.o \
- ulinks.o errhnd.o rarvm.o rijndael.o getbits.o sha1.o \
- extinfo.o extract.o volume.o list.o find.o unpack.o cmddata.o
-
-.cpp.o:
- $(COMPILE) -D$(WHAT) -c $<
-
-all: unrar
-
-clean:
- @rm -f *.o *.bak *~
-
-unrar: WHAT=UNRAR
-unrar: $(OBJECTS) $(UNRAR_OBJ)
- @rm -f makeunrar
- $(LINK) -Wl,-s -o unrar $(LDFLAGS) $(OBJECTS) $(UNRAR_OBJ) $(LIBS)
-
-sfx: WHAT=SFX_MODULE
-sfx: $(OBJECTS)
- @rm -f default.sfx
- $(LINK) -Wl,-s -o default.sfx $(LDFLAGS) $(OBJECTS) -DSFX_MODULE
-
diff --git a/unrar/unrar/makefile.dj b/unrar/unrar/makefile.dj
deleted file mode 100644
index 4d9dae5..0000000
--- a/unrar/unrar/makefile.dj
+++ /dev/null
@@ -1,50 +0,0 @@
-#
-# Makefile for DJGPP - unrar
-#
-# Note: you have to 'make clean' before you can build
-# the sfx module
-#
-
-# DOS32 using DJGPP 2.03 Patchlevel 2 and GCC 2.95.3
-CXX = gpp
-CXXFLAGS = -O2 -Wno-deprecated
-#DEFINES = -D_MSC_VER -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
-
-##########################
-
-.PHONY: all clean veryclean
-
-COMPILE=$(CXX) $(CXXFLAGS) $(DEFINES)
-LINK=$(CXX)
-
-UNRAR_OBJ=filestr.o recvol.o rs.o scantree.o
-
-OBJECTS=rar.o strlist.o strfn.o pathfn.o int64.o savepos.o global.o \
- file.o filefn.o filcreat.o archive.o arcread.o unicode.o \
- system.o isnt.o crypt.o crc.o rawread.o encname.o \
- resource.o match.o timefn.o rdwrfn.o consio.o options.o \
- ulinks.o errhnd.o rarvm.o rijndael.o getbits.o sha1.o \
- extinfo.o extract.o volume.o list.o find.o unpack.o cmddata.o
-
-.cpp.o:
- $(COMPILE) -D$(WHAT) -c $<
-
-all: unrar
-
-unrar: WHAT=UNRAR
-unrar: $(OBJECTS) $(UNRAR_OBJ)
- $(LINK) -Wl,-s -o unrar.exe $(LDFLAGS) $(OBJECTS) $(UNRAR_OBJ) $(LIBS)
- exe2coff unrar.exe
- cp -u $(DJDIR)/bin/cwsdstub.exe .
- copy /b cwsdstub.exe+unrar unrar.exe
- -upx --ultra-brute unrar.exe
-
-sfx: WHAT=SFX_MODULE
-sfx: $(OBJECTS)
- $(LINK) -Wl,-s -o default.sfx $(LDFLAGS) $(OBJECTS) -DSFX_MODULE
-
-clean:
- $(RM) $(OBJECTS) $(UNRAR_OBJ)
-
-veryclean: clean
- $(RM) unrar.exe default.sfx cwsdstub.exe unrar
diff --git a/unrar/unrar/makefile.dmc b/unrar/unrar/makefile.dmc
deleted file mode 100644
index f0e1198..0000000
--- a/unrar/unrar/makefile.dmc
+++ /dev/null
@@ -1,54 +0,0 @@
-# Makefile for Digital Mars C++ Compiler
-# http://www.rarlab.com
-# http://www.digitalmars.com
-#
-# DEFINES: UNRAR RARDLL GUI SFX_MODULE SILENT
-
-NAME = unrar
-EXT = exe
-
-CPP = dmc
-
-LINK = link
-
-# --------------
-# Release Build
-# --------------
-DEFINES = -DNDEBUG -D_MSC_VER -DUNRAR
-CPPFLAGS = -o+all -ff -Nc -g- -Ae
-LNKFLAGS = /EXETYPE:NT /MACHINE:i386 /SUBSYSTEM:CONSOLE /NOLOGO /NODEBUG /NOCODEVIEW /PACKFUNCTIONS
-
-# --------------
-# Debug Build
-# --------------
-#DEFINES = -D_DEBUG -D_MSC_VER -DUNRAR
-#CPPFLAGS = -o+none -Nc -S -gf -Ae
-#LNKFLAGS = /EXETYPE:NT /MACHINE:i386 /SUBSYSTEM:CONSOLE /NOLOGO /DEBUG
-
-OBJ = rar.obj strlist.obj strfn.obj pathfn.obj int64.obj savepos.obj global.obj \
- file.obj filefn.obj filcreat.obj archive.obj arcread.obj unicode.obj \
- system.obj isnt.obj crypt.obj crc.obj rawread.obj encname.obj \
- resource.obj match.obj timefn.obj rdwrfn.obj consio.obj options.obj \
- ulinks.obj errhnd.obj rarvm.obj rijndael.obj getbits.obj sha1.obj \
- extinfo.obj extract.obj volume.obj find.obj unpack.obj cmddata.obj \
- filestr.obj recvol.obj rs.obj scantree.obj \
- list.obj \
-# dll.obj \
-
-LIB = kernel32.lib+user32.lib+advapi32.lib
-
-#DEF = dll.def
-
-link: $(OBJ)
- $(LINK) $(LNKFLAGS) $(OBJ), $(NAME).$(EXT), $(NAME).map, $(LIB), $(DEF)
-
-.c.obj:
- $(CPP) $(CPPFLAGS) $(DEFINES) -c $< -o $@
-
-.cpp.obj:
- $(CPP) $(CPPFLAGS) $(DEFINES) -c $< -o $@
-
-clean:
- del $(OBJ)
- del $(NAME).$(EXT)
- del $(NAME).map
diff --git a/unrar/unrar/makefile.msc b/unrar/unrar/makefile.msc
deleted file mode 100644
index 77d82ef..0000000
--- a/unrar/unrar/makefile.msc
+++ /dev/null
@@ -1,564 +0,0 @@
-# Microsoft Developer Studio Generated NMAKE File, Based on unrar.dsp
-!IF "$(CFG)" == ""
-CFG=unrar - Win32 Release
-!MESSAGE No configuration specified. Defaulting to unrar - Win32 Release.
-!ENDIF
-
-!IF "$(CFG)" != "unrar - Win32 Release" && "$(CFG)" != "unrar - Win32 Debug"
-!MESSAGE Invalid configuration "$(CFG)" specified.
-!MESSAGE You can specify a configuration when running NMAKE
-!MESSAGE by defining the macro CFG on the command line. For example:
-!MESSAGE
-!MESSAGE NMAKE /f "unrar.mak" CFG="unrar - Win32 Debug"
-!MESSAGE
-!MESSAGE Possible choices for configuration are:
-!MESSAGE
-!MESSAGE "unrar - Win32 Release" (based on "Win32 (x86) Console Application")
-!MESSAGE "unrar - Win32 Debug" (based on "Win32 (x86) Console Application")
-!MESSAGE
-!ERROR An invalid configuration is specified.
-!ENDIF
-
-!IF "$(OS)" == "Windows_NT"
-NULL=
-!ELSE
-NULL=nul
-!ENDIF
-
-CPP=cl.exe
-RSC=rc.exe
-
-!IF "$(CFG)" == "unrar - Win32 Release"
-
-OUTDIR=.\Release
-INTDIR=.\Release
-# Begin Custom Macros
-OutDir=.\Release
-# End Custom Macros
-
-ALL : "$(OUTDIR)\unrar.exe"
-
-
-CLEAN :
- -@erase "$(INTDIR)\archive.obj"
- -@erase "$(INTDIR)\arcread.obj"
- -@erase "$(INTDIR)\cmddata.obj"
- -@erase "$(INTDIR)\consio.obj"
- -@erase "$(INTDIR)\crc.obj"
- -@erase "$(INTDIR)\crypt.obj"
- -@erase "$(INTDIR)\encname.obj"
- -@erase "$(INTDIR)\errhnd.obj"
- -@erase "$(INTDIR)\extinfo.obj"
- -@erase "$(INTDIR)\extract.obj"
- -@erase "$(INTDIR)\filcreat.obj"
- -@erase "$(INTDIR)\file.obj"
- -@erase "$(INTDIR)\filefn.obj"
- -@erase "$(INTDIR)\filestr.obj"
- -@erase "$(INTDIR)\find.obj"
- -@erase "$(INTDIR)\getbits.obj"
- -@erase "$(INTDIR)\global.obj"
- -@erase "$(INTDIR)\int64.obj"
- -@erase "$(INTDIR)\isnt.obj"
- -@erase "$(INTDIR)\list.obj"
- -@erase "$(INTDIR)\match.obj"
- -@erase "$(INTDIR)\options.obj"
- -@erase "$(INTDIR)\pathfn.obj"
- -@erase "$(INTDIR)\rar.obj"
- -@erase "$(INTDIR)\rarvm.obj"
- -@erase "$(INTDIR)\rawread.obj"
- -@erase "$(INTDIR)\rdwrfn.obj"
- -@erase "$(INTDIR)\recvol.obj"
- -@erase "$(INTDIR)\resource.obj"
- -@erase "$(INTDIR)\rijndael.obj"
- -@erase "$(INTDIR)\rs.obj"
- -@erase "$(INTDIR)\savepos.obj"
- -@erase "$(INTDIR)\scantree.obj"
- -@erase "$(INTDIR)\sha1.obj"
- -@erase "$(INTDIR)\strfn.obj"
- -@erase "$(INTDIR)\strlist.obj"
- -@erase "$(INTDIR)\system.obj"
- -@erase "$(INTDIR)\timefn.obj"
- -@erase "$(INTDIR)\ulinks.obj"
- -@erase "$(INTDIR)\unicode.obj"
- -@erase "$(INTDIR)\unpack.obj"
- -@erase "$(INTDIR)\vc60.idb"
- -@erase "$(INTDIR)\volume.obj"
- -@erase "$(OUTDIR)\unrar.exe"
- -@erase "$(OUTDIR)\unrar.map"
-
-"$(OUTDIR)" :
- if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
-
-CPP_PROJ=/nologo /ML /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "UNRAR" /Fp"$(INTDIR)\unrar.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c
-BSC32=bscmake.exe
-BSC32_FLAGS=/nologo /o"$(OUTDIR)\unrar.bsc"
-BSC32_SBRS= \
-
-LINK32=link.exe
-LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /incremental:no /pdb:"$(OUTDIR)\unrar.pdb" /map:"$(INTDIR)\unrar.map" /machine:I386 /out:"$(OUTDIR)\unrar.exe"
-LINK32_OBJS= \
- "$(INTDIR)\rar.obj" \
- "$(INTDIR)\strlist.obj" \
- "$(INTDIR)\strfn.obj" \
- "$(INTDIR)\pathfn.obj" \
- "$(INTDIR)\int64.obj" \
- "$(INTDIR)\savepos.obj" \
- "$(INTDIR)\global.obj" \
- "$(INTDIR)\file.obj" \
- "$(INTDIR)\filefn.obj" \
- "$(INTDIR)\filcreat.obj" \
- "$(INTDIR)\archive.obj" \
- "$(INTDIR)\arcread.obj" \
- "$(INTDIR)\unicode.obj" \
- "$(INTDIR)\system.obj" \
- "$(INTDIR)\isnt.obj" \
- "$(INTDIR)\crypt.obj" \
- "$(INTDIR)\crc.obj" \
- "$(INTDIR)\rawread.obj" \
- "$(INTDIR)\encname.obj" \
- "$(INTDIR)\resource.obj" \
- "$(INTDIR)\match.obj" \
- "$(INTDIR)\timefn.obj" \
- "$(INTDIR)\rdwrfn.obj" \
- "$(INTDIR)\consio.obj" \
- "$(INTDIR)\options.obj" \
- "$(INTDIR)\ulinks.obj" \
- "$(INTDIR)\errhnd.obj" \
- "$(INTDIR)\rarvm.obj" \
- "$(INTDIR)\rijndael.obj" \
- "$(INTDIR)\getbits.obj" \
- "$(INTDIR)\sha1.obj" \
- "$(INTDIR)\extinfo.obj" \
- "$(INTDIR)\extract.obj" \
- "$(INTDIR)\volume.obj" \
- "$(INTDIR)\list.obj" \
- "$(INTDIR)\find.obj" \
- "$(INTDIR)\unpack.obj" \
- "$(INTDIR)\cmddata.obj" \
- "$(INTDIR)\filestr.obj" \
- "$(INTDIR)\recvol.obj" \
- "$(INTDIR)\rs.obj" \
- "$(INTDIR)\scantree.obj"
-
-"$(OUTDIR)\unrar.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
- $(LINK32) @<<
- $(LINK32_FLAGS) $(LINK32_OBJS)
-<<
-
-!ELSEIF "$(CFG)" == "unrar - Win32 Debug"
-
-OUTDIR=.\Debug
-INTDIR=.\Debug
-# Begin Custom Macros
-OutDir=.\Debug
-# End Custom Macros
-
-ALL : "$(OUTDIR)\unrar.exe"
-
-
-CLEAN :
- -@erase "$(INTDIR)\archive.obj"
- -@erase "$(INTDIR)\arcread.obj"
- -@erase "$(INTDIR)\cmddata.obj"
- -@erase "$(INTDIR)\consio.obj"
- -@erase "$(INTDIR)\crc.obj"
- -@erase "$(INTDIR)\crypt.obj"
- -@erase "$(INTDIR)\encname.obj"
- -@erase "$(INTDIR)\errhnd.obj"
- -@erase "$(INTDIR)\extinfo.obj"
- -@erase "$(INTDIR)\extract.obj"
- -@erase "$(INTDIR)\filcreat.obj"
- -@erase "$(INTDIR)\file.obj"
- -@erase "$(INTDIR)\filefn.obj"
- -@erase "$(INTDIR)\filestr.obj"
- -@erase "$(INTDIR)\find.obj"
- -@erase "$(INTDIR)\getbits.obj"
- -@erase "$(INTDIR)\global.obj"
- -@erase "$(INTDIR)\int64.obj"
- -@erase "$(INTDIR)\isnt.obj"
- -@erase "$(INTDIR)\list.obj"
- -@erase "$(INTDIR)\match.obj"
- -@erase "$(INTDIR)\options.obj"
- -@erase "$(INTDIR)\pathfn.obj"
- -@erase "$(INTDIR)\rar.obj"
- -@erase "$(INTDIR)\rarvm.obj"
- -@erase "$(INTDIR)\rawread.obj"
- -@erase "$(INTDIR)\rdwrfn.obj"
- -@erase "$(INTDIR)\recvol.obj"
- -@erase "$(INTDIR)\resource.obj"
- -@erase "$(INTDIR)\rijndael.obj"
- -@erase "$(INTDIR)\rs.obj"
- -@erase "$(INTDIR)\savepos.obj"
- -@erase "$(INTDIR)\scantree.obj"
- -@erase "$(INTDIR)\sha1.obj"
- -@erase "$(INTDIR)\strfn.obj"
- -@erase "$(INTDIR)\strlist.obj"
- -@erase "$(INTDIR)\system.obj"
- -@erase "$(INTDIR)\timefn.obj"
- -@erase "$(INTDIR)\ulinks.obj"
- -@erase "$(INTDIR)\unicode.obj"
- -@erase "$(INTDIR)\unpack.obj"
- -@erase "$(INTDIR)\vc60.idb"
- -@erase "$(INTDIR)\vc60.pdb"
- -@erase "$(INTDIR)\volume.obj"
- -@erase "$(OUTDIR)\unrar.exe"
- -@erase "$(OUTDIR)\unrar.ilk"
- -@erase "$(OUTDIR)\unrar.pdb"
-
-"$(OUTDIR)" :
- if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
-
-CPP_PROJ=/nologo /MLd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "UNRAR" /Fp"$(INTDIR)\unrar.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c
-BSC32=bscmake.exe
-BSC32_FLAGS=/nologo /o"$(OUTDIR)\unrar.bsc"
-BSC32_SBRS= \
-
-LINK32=link.exe
-LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /incremental:yes /pdb:"$(OUTDIR)\unrar.pdb" /debug /machine:I386 /out:"$(OUTDIR)\unrar.exe" /pdbtype:sept
-LINK32_OBJS= \
- "$(INTDIR)\rar.obj" \
- "$(INTDIR)\strlist.obj" \
- "$(INTDIR)\strfn.obj" \
- "$(INTDIR)\pathfn.obj" \
- "$(INTDIR)\int64.obj" \
- "$(INTDIR)\savepos.obj" \
- "$(INTDIR)\global.obj" \
- "$(INTDIR)\file.obj" \
- "$(INTDIR)\filefn.obj" \
- "$(INTDIR)\filcreat.obj" \
- "$(INTDIR)\archive.obj" \
- "$(INTDIR)\arcread.obj" \
- "$(INTDIR)\unicode.obj" \
- "$(INTDIR)\system.obj" \
- "$(INTDIR)\isnt.obj" \
- "$(INTDIR)\crypt.obj" \
- "$(INTDIR)\crc.obj" \
- "$(INTDIR)\rawread.obj" \
- "$(INTDIR)\encname.obj" \
- "$(INTDIR)\resource.obj" \
- "$(INTDIR)\match.obj" \
- "$(INTDIR)\timefn.obj" \
- "$(INTDIR)\rdwrfn.obj" \
- "$(INTDIR)\consio.obj" \
- "$(INTDIR)\options.obj" \
- "$(INTDIR)\ulinks.obj" \
- "$(INTDIR)\errhnd.obj" \
- "$(INTDIR)\rarvm.obj" \
- "$(INTDIR)\rijndael.obj" \
- "$(INTDIR)\getbits.obj" \
- "$(INTDIR)\sha1.obj" \
- "$(INTDIR)\extinfo.obj" \
- "$(INTDIR)\extract.obj" \
- "$(INTDIR)\volume.obj" \
- "$(INTDIR)\list.obj" \
- "$(INTDIR)\find.obj" \
- "$(INTDIR)\unpack.obj" \
- "$(INTDIR)\cmddata.obj" \
- "$(INTDIR)\filestr.obj" \
- "$(INTDIR)\recvol.obj" \
- "$(INTDIR)\rs.obj" \
- "$(INTDIR)\scantree.obj"
-
-"$(OUTDIR)\unrar.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
- $(LINK32) @<<
- $(LINK32_FLAGS) $(LINK32_OBJS)
-<<
-
-!ENDIF
-
-.c{$(INTDIR)}.obj::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cpp{$(INTDIR)}.obj::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cxx{$(INTDIR)}.obj::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.c{$(INTDIR)}.sbr::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cpp{$(INTDIR)}.sbr::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cxx{$(INTDIR)}.sbr::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-
-!IF "$(NO_EXTERNAL_DEPS)" != "1"
-!IF EXISTS("msc.dep")
-!INCLUDE "msc.dep"
-!ELSE
-!MESSAGE Warning: cannot find "msc.dep"
-!ENDIF
-!ENDIF
-
-
-!IF "$(CFG)" == "unrar - Win32 Release" || "$(CFG)" == "unrar - Win32 Debug"
-SOURCE=.\archive.cpp
-
-"$(INTDIR)\archive.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\arcread.cpp
-
-"$(INTDIR)\arcread.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\cmddata.cpp
-
-"$(INTDIR)\cmddata.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\consio.cpp
-
-"$(INTDIR)\consio.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\crc.cpp
-
-"$(INTDIR)\crc.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\crypt.cpp
-
-"$(INTDIR)\crypt.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\encname.cpp
-
-"$(INTDIR)\encname.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\errhnd.cpp
-
-"$(INTDIR)\errhnd.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\extinfo.cpp
-
-"$(INTDIR)\extinfo.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\extract.cpp
-
-"$(INTDIR)\extract.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\filcreat.cpp
-
-"$(INTDIR)\filcreat.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\file.cpp
-
-"$(INTDIR)\file.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\filefn.cpp
-
-"$(INTDIR)\filefn.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\filestr.cpp
-
-"$(INTDIR)\filestr.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\find.cpp
-
-"$(INTDIR)\find.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\getbits.cpp
-
-"$(INTDIR)\getbits.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\global.cpp
-
-"$(INTDIR)\global.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\int64.cpp
-
-"$(INTDIR)\int64.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\isnt.cpp
-
-"$(INTDIR)\isnt.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\list.cpp
-
-"$(INTDIR)\list.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\match.cpp
-
-"$(INTDIR)\match.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\options.cpp
-
-"$(INTDIR)\options.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\pathfn.cpp
-
-"$(INTDIR)\pathfn.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\rar.cpp
-
-"$(INTDIR)\rar.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\rarvm.cpp
-
-"$(INTDIR)\rarvm.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\rawread.cpp
-
-"$(INTDIR)\rawread.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\rdwrfn.cpp
-
-"$(INTDIR)\rdwrfn.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\recvol.cpp
-
-"$(INTDIR)\recvol.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\resource.cpp
-
-"$(INTDIR)\resource.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\rijndael.cpp
-
-"$(INTDIR)\rijndael.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\rs.cpp
-
-"$(INTDIR)\rs.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\savepos.cpp
-
-"$(INTDIR)\savepos.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\scantree.cpp
-
-"$(INTDIR)\scantree.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\sha1.cpp
-
-"$(INTDIR)\sha1.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\strfn.cpp
-
-"$(INTDIR)\strfn.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\strlist.cpp
-
-"$(INTDIR)\strlist.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\system.cpp
-
-"$(INTDIR)\system.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\timefn.cpp
-
-"$(INTDIR)\timefn.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\ulinks.cpp
-
-"$(INTDIR)\ulinks.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\unicode.cpp
-
-"$(INTDIR)\unicode.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\unpack.cpp
-
-"$(INTDIR)\unpack.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\volume.cpp
-
-"$(INTDIR)\volume.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-
-!ENDIF
-
diff --git a/unrar/unrar/match.cpp b/unrar/unrar/match.cpp
index bd1c02f..ec88fa6 100644
--- a/unrar/unrar/match.cpp
+++ b/unrar/unrar/match.cpp
@@ -1,183 +1,82 @@
#include "rar.hpp"
-static bool match(char *pattern,char *string,bool ForceCase);
-static bool match(wchar *pattern,wchar *string,bool ForceCase);
-
-static int mstricompc(const char *Str1,const char *Str2,bool ForceCase);
-static int mstricompcw(const wchar *Str1,const wchar *Str2,bool ForceCase);
-static int mstrnicompc(const char *Str1,const char *Str2,size_t N,bool ForceCase);
-static int mstrnicompcw(const wchar *Str1,const wchar *Str2,size_t N,bool ForceCase);
-
-inline uint toupperc(byte ch,bool ForceCase)
-{
- if (ForceCase)
- return(ch);
-#ifdef _WIN_32
- return((uint)(LPARAM)CharUpper((LPTSTR)(ch)));
-#elif defined(_UNIX)
- return(ch);
-#else
- return(toupper(ch));
-#endif
-}
-
+static bool match(const wchar *pattern,const wchar *string,bool ForceCase);
+static int mwcsicompc(const wchar *Str1,const wchar *Str2,bool ForceCase);
+static int mwcsnicompc(const wchar *Str1,const wchar *Str2,size_t N,bool ForceCase);
inline uint touppercw(uint ch,bool ForceCase)
{
if (ForceCase)
- return(ch);
+ return ch;
#if defined(_UNIX)
- return(ch);
+ return ch;
#else
- return(toupperw(ch));
+ return toupperw(ch);
#endif
}
-bool CmpName(char *Wildcard,char *Name,int CmpPath)
+bool CmpName(const wchar *Wildcard,const wchar *Name,int CmpMode)
{
- bool ForceCase=(CmpPath&MATCH_FORCECASESENSITIVE)!=0;
+ bool ForceCase=(CmpMode&MATCH_FORCECASESENSITIVE)!=0;
- CmpPath&=MATCH_MODEMASK;
-
- if (CmpPath!=MATCH_NAMES)
- {
- size_t WildLength=strlen(Wildcard);
- if (CmpPath!=MATCH_EXACTPATH && mstrnicompc(Wildcard,Name,WildLength,ForceCase)==0)
- {
- char NextCh=Name[WildLength];
- if (NextCh=='\\' || NextCh=='/' || NextCh==0)
- return(true);
- }
- char Path1[NM],Path2[NM];
- GetFilePath(Wildcard,Path1,ASIZE(Path1));
- GetFilePath(Name,Path2,ASIZE(Path1));
- if (mstricompc(Wildcard,Path2,ForceCase)==0)
- return(true);
- if ((CmpPath==MATCH_PATH || CmpPath==MATCH_EXACTPATH) && mstricompc(Path1,Path2,ForceCase)!=0)
- return(false);
- if (CmpPath==MATCH_SUBPATH || CmpPath==MATCH_WILDSUBPATH)
- if (IsWildcard(Path1))
- return(match(Wildcard,Name,ForceCase));
- else
- if (CmpPath==MATCH_SUBPATH || IsWildcard(Wildcard))
- {
- if (*Path1 && mstrnicompc(Path1,Path2,strlen(Path1),ForceCase)!=0)
- return(false);
- }
- else
- if (mstricompc(Path1,Path2,ForceCase)!=0)
- return(false);
- }
- char *Name1=PointToName(Wildcard);
- char *Name2=PointToName(Name);
-
- // always return false for RAR temporary files to exclude them
- // from archiving operations
- if (mstrnicompc("__rar_",Name2,6,false)==0)
- return(false);
+ CmpMode&=MATCH_MODEMASK;
- return(match(Name1,Name2,ForceCase));
-}
-
-
-#ifndef SFX_MODULE
-bool CmpName(wchar *Wildcard,wchar *Name,int CmpPath)
-{
- bool ForceCase=(CmpPath&MATCH_FORCECASESENSITIVE)!=0;
-
- CmpPath&=MATCH_MODEMASK;
-
- if (CmpPath!=MATCH_NAMES)
+ if (CmpMode!=MATCH_NAMES)
{
- size_t WildLength=strlenw(Wildcard);
- if (CmpPath!=MATCH_EXACTPATH && mstrnicompcw(Wildcard,Name,WildLength,ForceCase)==0)
+ size_t WildLength=wcslen(Wildcard);
+ if (CmpMode!=MATCH_EXACT && CmpMode!=MATCH_EXACTPATH && CmpMode!=MATCH_ALLWILD &&
+ mwcsnicompc(Wildcard,Name,WildLength,ForceCase)==0)
{
+ // For all modes except MATCH_NAMES, MATCH_EXACT, MATCH_EXACTPATH, MATCH_ALLWILD,
+ // "path1" mask must match "path1\path2\filename.ext" and "path1" names.
wchar NextCh=Name[WildLength];
if (NextCh==L'\\' || NextCh==L'/' || NextCh==0)
return(true);
}
+
+ // Nothing more to compare for MATCH_SUBPATHONLY.
+ if (CmpMode==MATCH_SUBPATHONLY)
+ return(false);
+
wchar Path1[NM],Path2[NM];
GetFilePath(Wildcard,Path1,ASIZE(Path1));
GetFilePath(Name,Path2,ASIZE(Path2));
- if ((CmpPath==MATCH_PATH || CmpPath==MATCH_EXACTPATH) && mstricompcw(Path1,Path2,ForceCase)!=0)
+
+ if ((CmpMode==MATCH_EXACT || CmpMode==MATCH_EXACTPATH) &&
+ mwcsicompc(Path1,Path2,ForceCase)!=0)
return(false);
- if (CmpPath==MATCH_SUBPATH || CmpPath==MATCH_WILDSUBPATH)
- if (IsWildcard(NULL,Path1))
+ if (CmpMode==MATCH_ALLWILD)
+ return match(Wildcard,Name,ForceCase);
+ if (CmpMode==MATCH_SUBPATH || CmpMode==MATCH_WILDSUBPATH)
+ if (IsWildcard(Path1))
return(match(Wildcard,Name,ForceCase));
else
- if (CmpPath==MATCH_SUBPATH || IsWildcard(NULL,Wildcard))
+ if (CmpMode==MATCH_SUBPATH || IsWildcard(Wildcard))
{
- if (*Path1 && mstrnicompcw(Path1,Path2,strlenw(Path1),ForceCase)!=0)
+ if (*Path1 && mwcsnicompc(Path1,Path2,wcslen(Path1),ForceCase)!=0)
return(false);
}
else
- if (mstricompcw(Path1,Path2,ForceCase)!=0)
+ if (mwcsicompc(Path1,Path2,ForceCase)!=0)
return(false);
}
wchar *Name1=PointToName(Wildcard);
wchar *Name2=PointToName(Name);
- // always return false for RAR temporary files to exclude them
- // from archiving operations
- if (mstrnicompcw(L"__rar_",Name2,6,false)==0)
- return(false);
-
- return(match(Name1,Name2,ForceCase));
-}
-#endif
+ // Always return false for RAR temporary files to exclude them
+ // from archiving operations.
+// if (mwcsnicompc(L"__rar_",Name2,6,false)==0)
+// return(false);
+ if (CmpMode==MATCH_EXACT)
+ return(mwcsicompc(Name1,Name2,ForceCase)==0);
-bool match(char *pattern,char *string,bool ForceCase)
-{
- for (;; ++string)
- {
- char stringc=toupperc(*string,ForceCase);
- char patternc=toupperc(*pattern++,ForceCase);
- switch (patternc)
- {
- case 0:
- return(stringc==0);
- case '?':
- if (stringc == 0)
- return(false);
- break;
- case '*':
- if (*pattern==0)
- return(true);
- if (*pattern=='.')
- {
- if (pattern[1]=='*' && pattern[2]==0)
- return(true);
- char *dot=strchr(string,'.');
- if (pattern[1]==0)
- return (dot==NULL || dot[1]==0);
- if (dot!=NULL)
- {
- string=dot;
- if (strpbrk(pattern,"*?")==NULL && strchr(string+1,'.')==NULL)
- return(mstricompc(pattern+1,string+1,ForceCase)==0);
- }
- }
-
- while (*string)
- if (match(pattern,string++,ForceCase))
- return(true);
- return(false);
- default:
- if (patternc != stringc)
- if (patternc=='.' && stringc==0)
- return(match(pattern,string,ForceCase));
- else
- return(false);
- break;
- }
- }
+ return(match(Name1,Name2,ForceCase));
}
-#ifndef SFX_MODULE
-bool match(wchar *pattern,wchar *string,bool ForceCase)
+bool match(const wchar *pattern,const wchar *string,bool ForceCase)
{
for (;; ++string)
{
@@ -198,14 +97,14 @@ bool match(wchar *pattern,wchar *string,bool ForceCase)
{
if (pattern[1]=='*' && pattern[2]==0)
return(true);
- wchar *dot=strchrw(string,'.');
+ const wchar *dot=wcschr(string,'.');
if (pattern[1]==0)
return (dot==NULL || dot[1]==0);
if (dot!=NULL)
{
string=dot;
- if (strpbrkw(pattern,L"*?")==NULL && strchrw(string+1,'.')==NULL)
- return(mstricompcw(pattern+1,string+1,ForceCase)==0);
+ if (wcspbrk(pattern,L"*?")==NULL && wcschr(string+1,'.')==NULL)
+ return(mwcsicompc(pattern+1,string+1,ForceCase)==0);
}
}
@@ -215,56 +114,34 @@ bool match(wchar *pattern,wchar *string,bool ForceCase)
return(false);
default:
if (patternc != stringc)
- if (patternc=='.' && stringc==0)
+ {
+ // Allow "name." mask match "name" and "name.\" match "name\".
+ if (patternc=='.' && (stringc==0 || stringc=='\\' || stringc=='.'))
return(match(pattern,string,ForceCase));
else
return(false);
+ }
break;
}
}
}
-#endif
-
-
-int mstricompc(const char *Str1,const char *Str2,bool ForceCase)
-{
- if (ForceCase)
- return(strcmp(Str1,Str2));
- return(stricompc(Str1,Str2));
-}
-
-
-#ifndef SFX_MODULE
-int mstricompcw(const wchar *Str1,const wchar *Str2,bool ForceCase)
-{
- if (ForceCase)
- return(strcmpw(Str1,Str2));
- return(stricompcw(Str1,Str2));
-}
-#endif
-int mstrnicompc(const char *Str1,const char *Str2,size_t N,bool ForceCase)
+int mwcsicompc(const wchar *Str1,const wchar *Str2,bool ForceCase)
{
if (ForceCase)
- return(strncmp(Str1,Str2,N));
-#if defined(_UNIX)
- return(strncmp(Str1,Str2,N));
-#else
- return(strnicomp(Str1,Str2,N));
-#endif
+ return wcscmp(Str1,Str2);
+ return wcsicompc(Str1,Str2);
}
-#ifndef SFX_MODULE
-int mstrnicompcw(const wchar *Str1,const wchar *Str2,size_t N,bool ForceCase)
+int mwcsnicompc(const wchar *Str1,const wchar *Str2,size_t N,bool ForceCase)
{
if (ForceCase)
- return(strncmpw(Str1,Str2,N));
+ return wcsncmp(Str1,Str2,N);
#if defined(_UNIX)
- return(strncmpw(Str1,Str2,N));
+ return wcsncmp(Str1,Str2,N);
#else
- return(strnicmpw(Str1,Str2,N));
+ return wcsnicomp(Str1,Str2,N);
#endif
}
-#endif
diff --git a/unrar/unrar/match.hpp b/unrar/unrar/match.hpp
index 0e43514..1e65a3c 100644
--- a/unrar/unrar/match.hpp
+++ b/unrar/unrar/match.hpp
@@ -2,26 +2,37 @@
#define _RAR_MATCH_
enum {
- MATCH_NAMES, // Compare names only.
+ MATCH_NAMES, // Paths are ignored.
+ // Compares names only using wildcards.
- MATCH_PATH, // Compares names and paths. Both must match exactly.
- // Unlike MATCH_EXACTPATH, also matches names if
- // mask contains path only and this path is a part
- // of name path.
+ MATCH_SUBPATHONLY, // Paths must match either exactly or path in wildcard
+ // must be present in the beginning of file path.
+ // For example, "c:\path1\*" or "c:\path1" will match
+ // "c:\path1\path2\file".
+ // Names are not compared.
- MATCH_EXACTPATH, // Compares names and paths. Both must match exactly.
+ MATCH_EXACT, // Paths must match exactly.
+ // Names must match exactly.
+
+ MATCH_ALLWILD, // Paths and names are compared using wildcards.
+ // Unlike MATCH_SUBPATH, paths do not match subdirs
+ // unless a wildcard tells so.
+
+ MATCH_EXACTPATH, // Paths must match exactly.
+ // Names are compared using wildcards.
MATCH_SUBPATH, // Names must be the same, but path in mask is allowed
- // to be only a part of name path.
+ // to be only a part of name path. In other words,
+ // we match all files matching the file mask
+ // in current folder and subfolders.
- MATCH_WILDSUBPATH // Works as MATCH_SUBPATH if mask contains wildcards
- // and as MATCH_PATH otherwise.
+ MATCH_WILDSUBPATH // Works as MATCH_SUBPATH if file mask contains
+ // wildcards and as MATCH_EXACTPATH otherwise.
};
#define MATCH_MODEMASK 0x0000ffff
#define MATCH_FORCECASESENSITIVE 0x80000000
-bool CmpName(char *Wildcard,char *Name,int CmpPath);
-bool CmpName(wchar *Wildcard,wchar *Name,int CmpPath);
+bool CmpName(const wchar *Wildcard,const wchar *Name,int CmpMode);
#endif
diff --git a/unrar/unrar/model.cpp b/unrar/unrar/model.cpp
index abc73d2..e4f9e3c 100644
--- a/unrar/unrar/model.cpp
+++ b/unrar/unrar/model.cpp
@@ -5,10 +5,17 @@
* Contents: model description and encoding/decoding routines *
****************************************************************************/
-inline PPM_CONTEXT* PPM_CONTEXT::createChild(ModelPPM *Model,STATE* pStats,
- STATE& FirstState)
+static const int MAX_O=64; /* maximum allowed model order */
+const uint TOP=1 << 24, BOT=1 << 15;
+
+template <class T>
+inline void _PPMD_SWAP(T& t1,T& t2) { T tmp=t1; t1=t2; t2=tmp; }
+
+
+inline RARPPM_CONTEXT* RARPPM_CONTEXT::createChild(ModelPPM *Model,RARPPM_STATE* pStats,
+ RARPPM_STATE& FirstState)
{
- PPM_CONTEXT* pc = (PPM_CONTEXT*) Model->SubAlloc.AllocContext();
+ RARPPM_CONTEXT* pc = (RARPPM_CONTEXT*) Model->SubAlloc.AllocContext();
if ( pc )
{
pc->NumStats=1;
@@ -34,11 +41,15 @@ void ModelPPM::RestartModelRare()
memset(CharMask,0,sizeof(CharMask));
SubAlloc.InitSubAllocator();
InitRL=-(MaxOrder < 12 ? MaxOrder:12)-1;
- MinContext = MaxContext = (PPM_CONTEXT*) SubAlloc.AllocContext();
+ MinContext = MaxContext = (RARPPM_CONTEXT*) SubAlloc.AllocContext();
+ if (MinContext == NULL)
+ throw std::bad_alloc();
MinContext->Suffix=NULL;
OrderFall=MaxOrder;
MinContext->U.SummFreq=(MinContext->NumStats=256)+1;
- FoundState=MinContext->U.Stats=(STATE*)SubAlloc.AllocUnits(256/2);
+ FoundState=MinContext->U.Stats=(RARPPM_STATE*)SubAlloc.AllocUnits(256/2);
+ if (FoundState == NULL)
+ throw std::bad_alloc();
for (RunLength=InitRL, PrevSuccess=i=0;i < 256;i++)
{
MinContext->U.Stats[i].Symbol=i;
@@ -105,10 +116,10 @@ void ModelPPM::StartModelRare(int MaxOrder)
}
-void PPM_CONTEXT::rescale(ModelPPM *Model)
+void RARPPM_CONTEXT::rescale(ModelPPM *Model)
{
int OldNS=NumStats, i=NumStats-1, Adder, EscFreq;
- STATE* p1, * p;
+ RARPPM_STATE* p1, * p;
for (p=Model->FoundState;p != U.Stats;p--)
_PPMD_SWAP(p[0],p[-1]);
U.Stats->Freq += 4;
@@ -122,7 +133,7 @@ void PPM_CONTEXT::rescale(ModelPPM *Model)
U.SummFreq += (p->Freq=(p->Freq+Adder) >> 1);
if (p[0].Freq > p[-1].Freq)
{
- STATE tmp=*(p1=p);
+ RARPPM_STATE tmp=*(p1=p);
do
{
p1[0]=p1[-1];
@@ -139,7 +150,7 @@ void PPM_CONTEXT::rescale(ModelPPM *Model)
EscFreq += i;
if ((NumStats -= i) == 1)
{
- STATE tmp=*U.Stats;
+ RARPPM_STATE tmp=*U.Stats;
do
{
tmp.Freq-=(tmp.Freq >> 1);
@@ -152,19 +163,16 @@ void PPM_CONTEXT::rescale(ModelPPM *Model)
U.SummFreq += (EscFreq -= (EscFreq >> 1));
int n0=(OldNS+1) >> 1, n1=(NumStats+1) >> 1;
if (n0 != n1)
- U.Stats = (STATE*) Model->SubAlloc.ShrinkUnits(U.Stats,n0,n1);
+ U.Stats = (RARPPM_STATE*) Model->SubAlloc.ShrinkUnits(U.Stats,n0,n1);
Model->FoundState=U.Stats;
}
-inline PPM_CONTEXT* ModelPPM::CreateSuccessors(bool Skip,STATE* p1)
+inline RARPPM_CONTEXT* ModelPPM::CreateSuccessors(bool Skip,RARPPM_STATE* p1)
{
-#ifdef __ICL
- static
-#endif
- STATE UpState;
- PPM_CONTEXT* pc=MinContext, * UpBranch=FoundState->Successor;
- STATE * p, * ps[MAX_O], ** pps=ps;
+ RARPPM_STATE UpState;
+ RARPPM_CONTEXT* pc=MinContext, * UpBranch=FoundState->Successor;
+ RARPPM_STATE * p, * ps[MAX_O], ** pps=ps;
if ( !Skip )
{
*pps++ = FoundState;
@@ -195,14 +203,21 @@ LOOP_ENTRY:
{
pc=p->Successor;
break;
+
}
+ // We ensure that PPM order input parameter does not exceed MAX_O (64),
+ // so we do not really need this check and added it for extra safety.
+ // See CVE-2017-17969 for details.
+ if (pps>=ps+ASIZE(ps))
+ return NULL;
+
*pps++ = p;
} while ( pc->Suffix );
NO_LOOP:
if (pps == ps)
return pc;
UpState.Symbol=*(byte*) UpBranch;
- UpState.Successor=(PPM_CONTEXT*) (((byte*) UpBranch)+1);
+ UpState.Successor=(RARPPM_CONTEXT*) (((byte*) UpBranch)+1);
if (pc->NumStats != 1)
{
if ((byte*) pc <= SubAlloc.pText)
@@ -230,8 +245,8 @@ NO_LOOP:
inline void ModelPPM::UpdateModel()
{
- STATE fs = *FoundState, *p = NULL;
- PPM_CONTEXT *pc, *Successor;
+ RARPPM_STATE fs = *FoundState, *p = NULL;
+ RARPPM_CONTEXT *pc, *Successor;
uint ns1, ns, cf, sf, s0;
if (fs.Freq < MAX_FREQ/4 && (pc=MinContext->Suffix) != NULL)
{
@@ -269,7 +284,7 @@ inline void ModelPPM::UpdateModel()
return;
}
*SubAlloc.pText++ = fs.Symbol;
- Successor = (PPM_CONTEXT*) SubAlloc.pText;
+ Successor = (RARPPM_CONTEXT*) SubAlloc.pText;
if (SubAlloc.pText >= SubAlloc.FakeUnitsStart)
goto RESTART_MODEL;
if ( fs.Successor )
@@ -295,7 +310,7 @@ inline void ModelPPM::UpdateModel()
{
if ((ns1 & 1) == 0)
{
- pc->U.Stats=(STATE*) SubAlloc.ExpandUnits(pc->U.Stats,ns1 >> 1);
+ pc->U.Stats=(RARPPM_STATE*) SubAlloc.ExpandUnits(pc->U.Stats,ns1 >> 1);
if ( !pc->U.Stats )
goto RESTART_MODEL;
}
@@ -303,7 +318,7 @@ inline void ModelPPM::UpdateModel()
}
else
{
- p=(STATE*) SubAlloc.AllocUnits(1);
+ p=(RARPPM_STATE*) SubAlloc.AllocUnits(1);
if ( !p )
goto RESTART_MODEL;
*p=pc->OneState;
@@ -346,9 +361,9 @@ static const byte ExpEscape[16]={ 25,14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2,
-inline void PPM_CONTEXT::decodeBinSymbol(ModelPPM *Model)
+inline void RARPPM_CONTEXT::decodeBinSymbol(ModelPPM *Model)
{
- STATE& rs=OneState;
+ RARPPM_STATE& rs=OneState;
Model->HiBitsFlag=Model->HB2Flag[Model->FoundState->Symbol];
ushort& bs=Model->BinSumm[rs.Freq-1][Model->PrevSuccess+
Model->NS2BSIndx[Suffix->NumStats-1]+
@@ -360,14 +375,14 @@ inline void PPM_CONTEXT::decodeBinSymbol(ModelPPM *Model)
rs.Freq += (rs.Freq < 128);
Model->Coder.SubRange.LowCount=0;
Model->Coder.SubRange.HighCount=bs;
- bs = SHORT16(bs+INTERVAL-GET_MEAN(bs,PERIOD_BITS,2));
+ bs = GET_SHORT16(bs+INTERVAL-GET_MEAN(bs,PERIOD_BITS,2));
Model->PrevSuccess=1;
Model->RunLength++;
}
else
{
Model->Coder.SubRange.LowCount=bs;
- bs = SHORT16(bs-GET_MEAN(bs,PERIOD_BITS,2));
+ bs = GET_SHORT16(bs-GET_MEAN(bs,PERIOD_BITS,2));
Model->Coder.SubRange.HighCount=BIN_SCALE;
Model->InitEsc=ExpEscape[bs >> 10];
Model->NumMasked=1;
@@ -378,7 +393,7 @@ inline void PPM_CONTEXT::decodeBinSymbol(ModelPPM *Model)
}
-inline void PPM_CONTEXT::update1(ModelPPM *Model,STATE* p)
+inline void RARPPM_CONTEXT::update1(ModelPPM *Model,RARPPM_STATE* p)
{
(Model->FoundState=p)->Freq += 4;
U.SummFreq += 4;
@@ -394,13 +409,13 @@ inline void PPM_CONTEXT::update1(ModelPPM *Model,STATE* p)
-inline bool PPM_CONTEXT::decodeSymbol1(ModelPPM *Model)
+inline bool RARPPM_CONTEXT::decodeSymbol1(ModelPPM *Model)
{
Model->Coder.SubRange.scale=U.SummFreq;
- STATE* p=U.Stats;
+ RARPPM_STATE* p=U.Stats;
int i, HiCnt;
int count=Model->Coder.GetCurrentCount();
- if (count>=Model->Coder.SubRange.scale)
+ if (count>=(int)Model->Coder.SubRange.scale)
return(false);
if (count < (HiCnt=p->Freq))
{
@@ -439,7 +454,7 @@ inline bool PPM_CONTEXT::decodeSymbol1(ModelPPM *Model)
}
-inline void PPM_CONTEXT::update2(ModelPPM *Model,STATE* p)
+inline void RARPPM_CONTEXT::update2(ModelPPM *Model,RARPPM_STATE* p)
{
(Model->FoundState=p)->Freq += 4;
U.SummFreq += 4;
@@ -450,9 +465,9 @@ inline void PPM_CONTEXT::update2(ModelPPM *Model,STATE* p)
}
-inline SEE2_CONTEXT* PPM_CONTEXT::makeEscFreq2(ModelPPM *Model,int Diff)
+inline RARPPM_SEE2_CONTEXT* RARPPM_CONTEXT::makeEscFreq2(ModelPPM *Model,int Diff)
{
- SEE2_CONTEXT* psee2c;
+ RARPPM_SEE2_CONTEXT* psee2c;
if (NumStats != 256)
{
psee2c=Model->SEE2Cont[Model->NS2Indx[Diff-1]]+
@@ -472,11 +487,11 @@ inline SEE2_CONTEXT* PPM_CONTEXT::makeEscFreq2(ModelPPM *Model,int Diff)
-inline bool PPM_CONTEXT::decodeSymbol2(ModelPPM *Model)
+inline bool RARPPM_CONTEXT::decodeSymbol2(ModelPPM *Model)
{
int count, HiCnt, i=NumStats-Model->NumMasked;
- SEE2_CONTEXT* psee2c=makeEscFreq2(Model,i);
- STATE* ps[256], ** pps=ps, * p=U.Stats-1;
+ RARPPM_SEE2_CONTEXT* psee2c=makeEscFreq2(Model,i);
+ RARPPM_STATE* ps[256], ** pps=ps, * p=U.Stats-1;
HiCnt=0;
do
{
@@ -485,18 +500,29 @@ inline bool PPM_CONTEXT::decodeSymbol2(ModelPPM *Model)
p++;
} while (Model->CharMask[p->Symbol] == Model->EscCount);
HiCnt += p->Freq;
+
+ // We do not reuse PPMd coder in unstable state, so we do not really need
+ // this check and added it for extra safety. See CVE-2017-17969 for details.
+ if (pps>=ps+ASIZE(ps))
+ return false;
+
*pps++ = p;
} while ( --i );
Model->Coder.SubRange.scale += HiCnt;
count=Model->Coder.GetCurrentCount();
- if (count>=Model->Coder.SubRange.scale)
+ if (count>=(int)Model->Coder.SubRange.scale)
return(false);
p=*(pps=ps);
if (count < HiCnt)
{
HiCnt=0;
while ((HiCnt += p->Freq) <= count)
- p=*++pps;
+ {
+ pps++;
+ if (pps>=ps+ASIZE(ps)) // Extra safety check.
+ return false;
+ p=*pps;
+ }
Model->Coder.SubRange.LowCount = (Model->Coder.SubRange.HighCount=HiCnt)-p->Freq;
psee2c->update();
update2(Model,p);
@@ -506,15 +532,20 @@ inline bool PPM_CONTEXT::decodeSymbol2(ModelPPM *Model)
Model->Coder.SubRange.LowCount=HiCnt;
Model->Coder.SubRange.HighCount=Model->Coder.SubRange.scale;
i=NumStats-Model->NumMasked;
- pps--;
+
+ // 2022.12.02: we removed pps-- here and changed the code below to avoid
+ // "array subscript -1 is outside array bounds" warning in some compilers.
do
{
- Model->CharMask[(*++pps)->Symbol]=Model->EscCount;
+ if (pps>=ps+ASIZE(ps)) // Extra safety check.
+ return false;
+ Model->CharMask[(*pps)->Symbol]=Model->EscCount;
+ pps++;
} while ( --i );
psee2c->Summ += Model->Coder.SubRange.scale;
Model->NumMasked = NumStats;
}
- return(true);
+ return true;
}
@@ -540,7 +571,7 @@ void ModelPPM::CleanUp()
bool ModelPPM::DecodeInit(Unpack *UnpackRead,int &EscChar)
{
int MaxOrder=UnpackRead->GetChar();
- bool Reset=MaxOrder & 0x20;
+ bool Reset=(MaxOrder & 0x20)!=0;
int MaxMB;
if (Reset)
diff --git a/unrar/unrar/model.hpp b/unrar/unrar/model.hpp
index f39323b..52abc89 100644
--- a/unrar/unrar/model.hpp
+++ b/unrar/unrar/model.hpp
@@ -4,16 +4,17 @@
#include "coder.hpp"
#include "suballoc.hpp"
-const int MAX_O=64; /* maximum allowed model order */
-
-const int INT_BITS=7, PERIOD_BITS=7, TOT_BITS=INT_BITS+PERIOD_BITS,
- INTERVAL=1 << INT_BITS, BIN_SCALE=1 << TOT_BITS, MAX_FREQ=124;
-
-#ifndef STRICT_ALIGNMENT_REQUIRED
+#ifdef ALLOW_MISALIGNED
#pragma pack(1)
#endif
-struct SEE2_CONTEXT
+struct RARPPM_DEF
+{
+ static const int INT_BITS=7, PERIOD_BITS=7, TOT_BITS=INT_BITS+PERIOD_BITS,
+ INTERVAL=1 << INT_BITS, BIN_SCALE=1 << TOT_BITS, MAX_FREQ=124;
+};
+
+struct RARPPM_SEE2_CONTEXT : RARPPM_DEF
{ // SEE-contexts for PPM-contexts with masked symbols
ushort Summ;
byte Shift, Count;
@@ -24,7 +25,7 @@ struct SEE2_CONTEXT
}
uint getMean()
{
- uint RetVal=SHORT16(Summ) >> Shift;
+ uint RetVal=GET_SHORT16(Summ) >> Shift;
Summ -= RetVal;
return RetVal+(RetVal == 0);
}
@@ -40,45 +41,47 @@ struct SEE2_CONTEXT
class ModelPPM;
-struct PPM_CONTEXT;
+struct RARPPM_CONTEXT;
-struct STATE
+struct RARPPM_STATE
{
byte Symbol;
byte Freq;
- PPM_CONTEXT* Successor;
+ RARPPM_CONTEXT* Successor;
};
-struct FreqData
-{
- ushort SummFreq;
- STATE _PACK_ATTR * Stats;
-};
-struct PPM_CONTEXT
+struct RARPPM_CONTEXT : RARPPM_DEF
{
ushort NumStats;
+
+ struct FreqData
+ {
+ ushort SummFreq;
+ RARPPM_STATE RARPPM_PACK_ATTR * Stats;
+ };
+
union
{
FreqData U;
- STATE OneState;
+ RARPPM_STATE OneState;
};
- PPM_CONTEXT* Suffix;
+ RARPPM_CONTEXT* Suffix;
inline void encodeBinSymbol(ModelPPM *Model,int symbol); // MaxOrder:
inline void encodeSymbol1(ModelPPM *Model,int symbol); // ABCD context
inline void encodeSymbol2(ModelPPM *Model,int symbol); // BCD suffix
inline void decodeBinSymbol(ModelPPM *Model); // BCDE successor
inline bool decodeSymbol1(ModelPPM *Model); // other orders:
inline bool decodeSymbol2(ModelPPM *Model); // BCD context
- inline void update1(ModelPPM *Model,STATE* p); // CD suffix
- inline void update2(ModelPPM *Model,STATE* p); // BCDE successor
+ inline void update1(ModelPPM *Model,RARPPM_STATE* p); // CD suffix
+ inline void update2(ModelPPM *Model,RARPPM_STATE* p); // BCDE successor
void rescale(ModelPPM *Model);
- inline PPM_CONTEXT* createChild(ModelPPM *Model,STATE* pStats,STATE& FirstState);
- inline SEE2_CONTEXT* makeEscFreq2(ModelPPM *Model,int Diff);
+ inline RARPPM_CONTEXT* createChild(ModelPPM *Model,RARPPM_STATE* pStats,RARPPM_STATE& FirstState);
+ inline RARPPM_SEE2_CONTEXT* makeEscFreq2(ModelPPM *Model,int Diff);
};
-#ifndef STRICT_ALIGNMENT_REQUIRED
+#ifdef ALLOW_MISALIGNED
#ifdef _AIX
#pragma pack(pop)
#else
@@ -86,28 +89,15 @@ struct PPM_CONTEXT
#endif
#endif
-const uint UNIT_SIZE=Max(sizeof(PPM_CONTEXT),sizeof(RAR_MEM_BLK));
-const uint FIXED_UNIT_SIZE=12;
-
-/*
-inline PPM_CONTEXT::PPM_CONTEXT(STATE* pStats,PPM_CONTEXT* ShorterContext):
- NumStats(1), Suffix(ShorterContext) { pStats->Successor=this; }
-inline PPM_CONTEXT::PPM_CONTEXT(): NumStats(0) {}
-*/
-
-template <class T>
-inline void _PPMD_SWAP(T& t1,T& t2) { T tmp=t1; t1=t2; t2=tmp; }
-
-
-class ModelPPM
+class ModelPPM : RARPPM_DEF
{
private:
- friend struct PPM_CONTEXT;
+ friend struct RARPPM_CONTEXT;
- _PACK_ATTR SEE2_CONTEXT SEE2Cont[25][16], DummySEE2Cont;
+ RARPPM_SEE2_CONTEXT SEE2Cont[25][16], DummySEE2Cont;
- struct PPM_CONTEXT *MinContext, *MedContext, *MaxContext;
- STATE* FoundState; // found next state transition
+ struct RARPPM_CONTEXT *MinContext, *MedContext, *MaxContext;
+ RARPPM_STATE* FoundState; // found next state transition
int NumMasked, InitEsc, OrderFall, MaxOrder, RunLength, InitRL;
byte CharMask[256], NS2Indx[256], NS2BSIndx[256], HB2Flag[256];
byte EscCount, PrevSuccess, HiBitsFlag;
@@ -118,7 +108,7 @@ class ModelPPM
void RestartModelRare();
void StartModelRare(int MaxOrder);
- inline PPM_CONTEXT* CreateSuccessors(bool Skip,STATE* p1);
+ inline RARPPM_CONTEXT* CreateSuccessors(bool Skip,RARPPM_STATE* p1);
inline void UpdateModel();
inline void ClearMask();
diff --git a/unrar/unrar/msc.dep b/unrar/unrar/msc.dep
deleted file mode 100644
index 1205afa..0000000
--- a/unrar/unrar/msc.dep
+++ /dev/null
@@ -1,2532 +0,0 @@
-# Microsoft Developer Studio Generated Dependency File, included by unrar.mak
-
-.\archive.cpp : \
- ".\arccmt.cpp"\
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\arcread.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\cmddata.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\consio.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.cpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\crc.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\crypt.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\encname.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\errhnd.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\extinfo.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
- ".\win32acl.cpp"\
- ".\win32stm.cpp"\
-
-
-.\extract.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\filcreat.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\file.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\filefn.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\filestr.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\find.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\getbits.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\global.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\int64.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\isnt.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\list.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\match.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\options.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\pathfn.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\rar.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.cpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\rarvm.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rarvmtbl.cpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\rawread.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\rdwrfn.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\recvol.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\resource.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\rijndael.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\rs.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\savepos.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\scantree.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\sha1.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\strfn.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\strlist.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\system.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\timefn.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\ulinks.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\unicode.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\unpack.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.cpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.cpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.cpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\unpack15.cpp"\
- ".\unpack20.cpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\volume.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
diff --git a/unrar/unrar/options.cpp b/unrar/unrar/options.cpp
index 57ca92f..22ae27c 100644
--- a/unrar/unrar/options.cpp
+++ b/unrar/unrar/options.cpp
@@ -6,23 +6,22 @@ RAROptions::RAROptions()
}
-RAROptions::~RAROptions()
-{
- memset(this,0,sizeof(RAROptions));
-}
-
-
void RAROptions::Init()
{
memset(this,0,sizeof(RAROptions));
- WinSize=0x400000;
+ WinSize=0x2000000;
Overwrite=OVERWRITE_DEFAULT;
Method=3;
MsgStream=MSG_STDOUT;
ConvertNames=NAMES_ORIGINALCASE;
- ProcessEA=true;
- xmtime=EXTTIME_HIGH3;
- CurVolNum=0;
- FileSizeLess=INT64ERR;
- FileSizeMore=INT64ERR;
+ xmtime=EXTTIME_MAX;
+ FileSizeLess=INT64NDF;
+ FileSizeMore=INT64NDF;
+ HashType=HASH_CRC32;
+#ifdef RAR_SMP
+ Threads=GetNumberOfThreads();
+#endif
+#ifdef USE_QOPEN
+ QOpenMode=QOPEN_AUTO;
+#endif
}
diff --git a/unrar/unrar/options.hpp b/unrar/unrar/options.hpp
index 8af0e8b..e249eb5 100644
--- a/unrar/unrar/options.hpp
+++ b/unrar/unrar/options.hpp
@@ -1,37 +1,85 @@
#ifndef _RAR_OPTIONS_
#define _RAR_OPTIONS_
-#define DEFAULT_RECOVERY -1
+#define DEFAULT_RECOVERY -3
#define DEFAULT_RECVOLUMES -10
-enum PathExclMode {
- EXCL_NONE,EXCL_BASEPATH,EXCL_SKIPWHOLEPATH,EXCL_SAVEFULLPATH,
- EXCL_SKIPABSPATH,EXCL_ABSPATH
+#define VOLSIZE_AUTO INT64NDF // Automatically detect the volume size.
+
+enum PATH_EXCL_MODE {
+ EXCL_UNCHANGED=0, // Process paths as is (default).
+ EXCL_SKIPWHOLEPATH, // -ep (exclude the path completely)
+ EXCL_BASEPATH, // -ep1 (exclude the base part of path)
+ EXCL_SAVEFULLPATH, // -ep2 (the full path without the disk letter)
+ EXCL_ABSPATH // -ep3 (the full path with the disk letter)
};
+
enum {SOLID_NONE=0,SOLID_NORMAL=1,SOLID_COUNT=2,SOLID_FILEEXT=4,
SOLID_VOLUME_DEPENDENT=8,SOLID_VOLUME_INDEPENDENT=16};
-enum {ARCTIME_NONE,ARCTIME_KEEP,ARCTIME_LATEST};
+
+enum {ARCTIME_NONE=0,ARCTIME_KEEP,ARCTIME_LATEST};
+
enum EXTTIME_MODE {
- EXTTIME_NONE,EXTTIME_1S,EXTTIME_HIGH1,EXTTIME_HIGH2,EXTTIME_HIGH3
+ EXTTIME_NONE=0,EXTTIME_1S,EXTTIME_MAX
};
-enum {NAMES_ORIGINALCASE,NAMES_UPPERCASE,NAMES_LOWERCASE};
-enum MESSAGE_TYPE {MSG_STDOUT,MSG_STDERR,MSG_ERRONLY,MSG_NULL};
-enum OVERWRITE_MODE {
- OVERWRITE_DEFAULT, // ask for extraction, silently overwrite for archiving
+enum {NAMES_ORIGINALCASE=0,NAMES_UPPERCASE,NAMES_LOWERCASE};
+
+enum MESSAGE_TYPE {MSG_STDOUT=0,MSG_STDERR,MSG_ERRONLY,MSG_NULL};
+
+enum RECURSE_MODE
+{
+ RECURSE_NONE=0, // no recurse switches
+ RECURSE_DISABLE, // switch -r-
+ RECURSE_ALWAYS, // switch -r
+ RECURSE_WILDCARDS, // switch -r0
+};
+
+enum OVERWRITE_MODE
+{
+ OVERWRITE_DEFAULT=0, // Ask when extracting, silently overwrite when archiving.
OVERWRITE_ALL,
OVERWRITE_NONE,
OVERWRITE_AUTORENAME,
OVERWRITE_FORCE_ASK
};
-enum RAR_CHARSET { RCH_DEFAULT=0,RCH_ANSI,RCH_OEM,RCH_UNICODE };
+enum ARC_METADATA
+{
+ ARCMETA_NONE=0,
+ ARCMETA_SAVE, // -ams
+ ARCMETA_RESTORE // -amr
+};
+
+enum QOPEN_MODE { QOPEN_NONE, QOPEN_AUTO, QOPEN_ALWAYS };
+
+enum RAR_CHARSET { RCH_DEFAULT=0,RCH_ANSI,RCH_OEM,RCH_UNICODE,RCH_UTF8 };
-#define MAX_FILTERS 16
+#define MAX_FILTER_TYPES 16
enum FilterState {FILTER_DEFAULT=0,FILTER_AUTO,FILTER_FORCE,FILTER_DISABLE};
+enum SAVECOPY_MODE {
+ SAVECOPY_NONE=0, SAVECOPY_SILENT, SAVECOPY_LIST, SAVECOPY_LISTEXIT,
+ SAVECOPY_DUPLISTEXIT
+};
+
+enum APPENDARCNAME_MODE
+{
+ APPENDARCNAME_NONE=0,APPENDARCNAME_DESTPATH,APPENDARCNAME_OWNSUBDIR,
+ APPENDARCNAME_OWNDIR
+};
+
+enum POWER_MODE {
+ POWERMODE_KEEP=0,POWERMODE_OFF,POWERMODE_HIBERNATE,POWERMODE_SLEEP,
+ POWERMODE_RESTART
+};
+
+
+// Need "forced off" state to turn off sound in GUI command line.
+enum SOUND_NOTIFY_MODE {SOUND_NOTIFY_DEFAULT=0,SOUND_NOTIFY_ON,SOUND_NOTIFY_OFF};
+
struct FilterMode
{
FilterState State;
@@ -39,104 +87,136 @@ struct FilterMode
int Param2;
};
+#define MAX_GENERATE_MASK 128
+
+// Here we store simple data types, which we can clear and move all together
+// quickly. Rest of data types goes to CommandData.
class RAROptions
{
public:
RAROptions();
- ~RAROptions();
void Init();
uint ExclFileAttr;
uint InclFileAttr;
+
+ // We handle -ed and -e+d with special flags instead of attribute mask,
+ // so it works with both Windows and Unix archives.
+ bool ExclDir;
+ bool InclDir;
+
bool InclAttrSet;
- uint WinSize;
- char TempPath[NM];
- char SFXModule[NM];
- char ExtrPath[NM];
- wchar ExtrPathW[NM];
- char CommentFile[NM];
+ size_t WinSize;
+ wchar TempPath[NM];
+ wchar SFXModule[NM];
+
+#ifdef USE_QOPEN
+ QOPEN_MODE QOpenMode;
+#endif
+
+ bool ConfigDisabled; // Switch -cfg-.
+ wchar ExtrPath[NM];
+ wchar CommentFile[NM];
RAR_CHARSET CommentCharset;
RAR_CHARSET FilelistCharset;
- char ArcPath[NM];
- wchar ArcPathW[NM];
- char Password[MAXPASSWORD];
+ RAR_CHARSET ErrlogCharset;
+ RAR_CHARSET RedirectCharset;
+
+ wchar ArcPath[NM]; // For -ap<path>.
+ wchar ExclArcPath[NM]; // For -ep4<path> switch.
bool EncryptHeaders;
- char LogName[NM];
+ bool SkipEncrypted;
+
+ bool ManualPassword; // Password entered manually during operation, might need to clean for next archive.
+
+ wchar LogName[NM];
MESSAGE_TYPE MsgStream;
- bool Sound;
+ SOUND_NOTIFY_MODE Sound;
OVERWRITE_MODE Overwrite;
int Method;
+ HASH_TYPE HashType;
int Recovery;
int RecVolNumber;
+ ARC_METADATA ArcMetadata;
bool DisablePercentage;
bool DisableCopyright;
bool DisableDone;
+ bool DisableNames;
+ bool PrintVersion;
int Solid;
int SolidCount;
bool ClearArc;
bool AddArcOnly;
- bool AV;
bool DisableComment;
bool FreshFiles;
bool UpdateFiles;
- PathExclMode ExclPath;
- int Recurse;
- Int64 VolSize;
- Array<Int64> NextVolSizes;
- int CurVolNum;
+ PATH_EXCL_MODE ExclPath;
+ RECURSE_MODE Recurse;
+ int64 VolSize;
+ uint CurVolNum;
bool AllYes;
- bool DisableViewAV;
+ bool VerboseOutput; // -iv, display verbose output, used only in "WinRAR t" now.
bool DisableSortSolid;
int ArcTime;
int ConvertNames;
bool ProcessOwners;
- bool SaveLinks;
+ bool SaveSymLinks;
+ bool SaveHardLinks;
+ bool AbsoluteLinks;
int Priority;
int SleepTime;
bool KeepBroken;
- bool EraseDisk;
bool OpenShared;
- bool ExclEmptyDir;
bool DeleteFiles;
- bool SyncFiles;
+
+#ifdef _WIN_ALL
+ bool AllowIncompatNames; // Allow names with trailing dots and spaces.
+#endif
+
+
+#ifndef SFX_MODULE
bool GenerateArcName;
- char GenerateMask[80];
+ wchar GenerateMask[MAX_GENERATE_MASK];
+ wchar DefGenerateMask[MAX_GENERATE_MASK];
+#endif
+ bool SyncFiles;
bool ProcessEA;
bool SaveStreams;
bool SetCompressedAttr;
- uint FileTimeOlder;
- uint FileTimeNewer;
- RarTime FileTimeBefore;
- RarTime FileTimeAfter;
- Int64 FileSizeLess;
- Int64 FileSizeMore;
- bool OldNumbering;
+ bool IgnoreGeneralAttr;
+ RarTime FileMtimeBefore,FileCtimeBefore,FileAtimeBefore;
+ bool FileMtimeBeforeOR,FileCtimeBeforeOR,FileAtimeBeforeOR;
+ RarTime FileMtimeAfter,FileCtimeAfter,FileAtimeAfter;
+ bool FileMtimeAfterOR,FileCtimeAfterOR,FileAtimeAfterOR;
+ int64 FileSizeLess;
+ int64 FileSizeMore;
bool Lock;
bool Test;
bool VolumePause;
- FilterMode FilterModes[MAX_FILTERS];
- char EmailTo[NM];
- int VersionControl;
- bool NoEndBlock;
- bool AppendArcNameToPath;
- bool Shutdown;
- EXTTIME_MODE xmtime;
+ FilterMode FilterModes[MAX_FILTER_TYPES];
+ wchar EmailTo[NM];
+ uint VersionControl;
+ APPENDARCNAME_MODE AppendArcNameToPath;
+ POWER_MODE Shutdown;
+ EXTTIME_MODE xmtime; // Extended time modes (time precision to store).
EXTTIME_MODE xctime;
EXTTIME_MODE xatime;
- EXTTIME_MODE xarctime;
- char CompressStdin[NM];
+ bool PreserveAtime;
+
+ // Read data from stdin and store in archive under a name specified here
+ // when archiving. Read an archive from stdin if any non-empty string
+ // is specified here when extracting.
+ wchar UseStdin[NM];
+
+ uint Threads; // We use it to init hash even if RAR_SMP is not defined.
-#ifdef PACK_SMP
- uint Threads;
-#endif
#ifdef RARDLL
- char DllDestName[NM];
- wchar DllDestNameW[NM];
+ wchar DllDestName[NM];
int DllOpMode;
int DllError;
LPARAM UserData;
diff --git a/unrar/unrar/os.hpp b/unrar/unrar/os.hpp
index fbcf3a0..4b21e49 100644
--- a/unrar/unrar/os.hpp
+++ b/unrar/unrar/os.hpp
@@ -8,141 +8,139 @@
#define INCL_BASE
#endif
-#if defined(_WIN_32) || defined(_EMX)
-#define ENABLE_BAD_ALLOC
+#if defined(RARDLL) && !defined(SILENT)
+#define SILENT
#endif
+#include <new>
+#include <string>
+#include <vector>
-#if defined(_WIN_32) || defined(_EMX)
+
+#if defined(_WIN_ALL) || defined(_EMX)
#define LITTLE_ENDIAN
-#define NM 1024
+#define NM 2048
+
+#ifdef _WIN_ALL
-#ifdef _WIN_32
- #define STRICT
- #undef WINVER
- #undef _WIN32_WINNT
- #define WINVER 0x0400
- #define _WIN32_WINNT 0x0300
+// We got a report that just "#define STRICT" is incompatible with
+// "#define STRICT 1" in Windows 10 SDK minwindef.h and depending on the order
+// in which these statements are reached this may cause a compiler warning
+// and build break for other projects incorporating this source.
+// So we changed it to "#define STRICT 1".
+#ifndef STRICT
+#define STRICT 1
+#endif
+
+// 'ifndef' check here is needed for unrar.dll header to avoid macro
+// re-definition warnings in third party projects.
+#ifndef UNICODE
+#define UNICODE
+#define _UNICODE // Set _T() macro to convert from narrow to wide strings.
+#endif
+#if 0
+// 2021.09.05: Allow newer Vista+ APIs like IFileOpenDialog for WinRAR,
+// but still keep SFX modules XP compatible.
+#define WINVER _WIN32_WINNT_VISTA
+#define _WIN32_WINNT _WIN32_WINNT_VISTA
+#else
+#define WINVER _WIN32_WINNT_WINXP
+#define _WIN32_WINNT _WIN32_WINNT_WINXP
+#endif
+
+#if !defined(ZIPSFX)
+#define RAR_SMP
+#endif
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <prsht.h>
+#include <shlwapi.h>
+#pragma comment(lib, "Shlwapi.lib")
+#include <PowrProf.h>
+#pragma comment(lib, "PowrProf.lib")
+#include <shellapi.h>
+#include <shlobj.h>
+#include <winioctl.h>
+#include <wincrypt.h>
+#include <wchar.h>
+#include <wctype.h>
-#ifndef _WIN_CE
- #include <shellapi.h>
- #include <shlobj.h>
- #include <winioctl.h>
-#endif
-#endif
+#endif // _WIN_ALL
-#ifndef _WIN_CE
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <dos.h>
-#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dos.h>
-#if !defined(_EMX) && !defined(_MSC_VER) && !defined(_WIN_CE)
+#if !defined(_EMX) && !defined(_MSC_VER)
#include <dir.h>
#endif
#ifdef _MSC_VER
- #define for if (0) ; else for
-#ifndef _WIN_CE
#include <direct.h>
-#endif
-#else
- #include <dirent.h>
-#endif
-
-#ifndef _WIN_CE
- #include <share.h>
-#endif
-
-#if defined(ENABLE_BAD_ALLOC) && !defined(_WIN_CE)
- #include <new.h>
-#endif
+ #include <intrin.h>
-#ifdef _EMX
- #include <unistd.h>
- #include <pwd.h>
- #include <grp.h>
- #include <errno.h>
- #ifdef _DJGPP
- #include <utime.h>
- #else
- #include <os2.h>
- #include <sys/utime.h>
- #include <emx/syscalls.h>
+ // Use SSE only for x86/x64, not ARM Windows.
+ #if defined(_M_IX86) || defined(_M_X64)
+ #define USE_SSE
+ #define SSE_ALIGNMENT 16
#endif
#else
- #if defined(_MSC_VER) || defined(__MINGW32__)
- #include <exception>
- #else
- #include <except.h>
- #endif
-#endif
+ #include <dirent.h>
+#endif // _MSC_VER
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
-#ifndef _WIN_CE
- #include <fcntl.h>
- #include <dos.h>
- #include <io.h>
- #include <time.h>
- #include <signal.h>
-#endif
+#include <fcntl.h>
+#include <dos.h>
+#include <io.h>
+#include <time.h>
+#include <signal.h>
-/*
-#ifdef _WIN_32
-#pragma hdrstop
-#endif
-*/
+#define SAVE_LINKS
#define ENABLE_ACCESS
-#define DefConfigName "rar.ini"
-#define DefLogName "rar.log"
+#define DefConfigName L"rar.ini"
+#define DefLogName L"rar.log"
-#define PATHDIVIDER "\\"
-#define PATHDIVIDERW L"\\"
+#define SPATHDIVIDER L"\\"
#define CPATHDIVIDER '\\'
-#define MASKALL "*"
-#define MASKALLW L"*"
+#define MASKALL L"*"
#define READBINARY "rb"
#define READTEXT "rt"
#define UPDATEBINARY "r+b"
#define CREATEBINARY "w+b"
+#define WRITEBINARY "wb"
#define APPENDTEXT "at"
-#if defined(_WIN_32)
+#if defined(_WIN_ALL)
#ifdef _MSC_VER
#define _stdfunction __cdecl
+ #define _forceinline __forceinline
#else
#define _stdfunction _USERENTRY
+ #define _forceinline inline
#endif
#else
#define _stdfunction
+ #define _forceinline inline
#endif
-#endif
+#endif // defined(_WIN_ALL) || defined(_EMX)
#ifdef _UNIX
-#define NM 1024
-
-#ifdef _BEOS
-#include <be/kernel/fs_info.h>
-#include <be/kernel/fs_attr.h>
-#endif
+#define NM 2048
#include <unistd.h>
#include <sys/types.h>
@@ -151,13 +149,16 @@
#if defined(__QNXNTO__)
#include <sys/param.h>
#endif
-#if defined(__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__) || defined(__APPLE__)
- #include <sys/param.h>
- #include <sys/mount.h>
-#else
+#if defined(RAR_SMP) && defined(__APPLE__)
+ #include <sys/sysctl.h>
+#endif
+#ifndef SFX_MODULE
+ #include <sys/statvfs.h>
#endif
#include <pwd.h>
#include <grp.h>
+#include <wchar.h>
+#include <wctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
@@ -171,29 +172,35 @@
#include <utime.h>
#include <locale.h>
+
#ifdef S_IFLNK
#define SAVE_LINKS
#endif
+#if defined(__linux) || defined(__FreeBSD__)
+#include <sys/time.h>
+#define USE_LUTIMES
+#endif
+
#define ENABLE_ACCESS
-#define DefConfigName ".rarrc"
-#define DefLogName ".rarlog"
+#define DefConfigName L".rarrc"
+#define DefLogName L".rarlog"
-#define PATHDIVIDER "/"
-#define PATHDIVIDERW L"/"
+#define SPATHDIVIDER L"/"
#define CPATHDIVIDER '/'
-#define MASKALL "*"
-#define MASKALLW L"*"
+#define MASKALL L"*"
#define READBINARY "r"
#define READTEXT "r"
#define UPDATEBINARY "r+"
#define CREATEBINARY "w+"
+#define WRITEBINARY "w"
#define APPENDTEXT "a"
#define _stdfunction
+#define _forceinline inline
#ifdef _APPLE
#if defined(__BIG_ENDIAN__) && !defined(BIG_ENDIAN)
@@ -212,31 +219,59 @@
#endif
#endif
+// Unlike Apple x64, utimensat shall be available in all Apple M1 systems.
+#if _POSIX_C_SOURCE >= 200809L || defined(__APPLE__) && defined(__arm64__)
+ #define UNIX_TIME_NS // Nanosecond time precision in Unix.
#endif
- typedef const char* MSGID;
+#endif // _UNIX
+
+#if 0
+ #define MSGID_INT
+ typedef int MSGID;
+#else
+ typedef const wchar* MSGID;
+#endif
+
+#ifndef SSE_ALIGNMENT // No SSE use and no special data alignment is required.
+ #define SSE_ALIGNMENT 1
+#endif
#define safebuf static
+// Solaris defines _LITTLE_ENDIAN or _BIG_ENDIAN.
+#if defined(_LITTLE_ENDIAN) && !defined(LITTLE_ENDIAN)
+ #define LITTLE_ENDIAN
+#endif
+#if defined(_BIG_ENDIAN) && !defined(BIG_ENDIAN)
+ #define BIG_ENDIAN
+#endif
+
+#if !defined(LITTLE_ENDIAN) && !defined(BIG_ENDIAN)
+ #if defined(__i386) || defined(i386) || defined(__i386__) || defined(__x86_64)
+ #define LITTLE_ENDIAN
+ #elif defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN || defined(__LITTLE_ENDIAN__)
+ #define LITTLE_ENDIAN
+ #elif defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN || defined(__BIG_ENDIAN__)
+ #define BIG_ENDIAN
+ #else
+ #error "Neither LITTLE_ENDIAN nor BIG_ENDIAN are defined. Define one of them."
+ #endif
+#endif
+
#if defined(LITTLE_ENDIAN) && defined(BIG_ENDIAN)
#if defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN
#undef LITTLE_ENDIAN
#elif defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN
#undef BIG_ENDIAN
#else
- #error "Both LITTLE_ENDIAN and BIG_ENDIAN are defined. Undef something one"
+ #error "Both LITTLE_ENDIAN and BIG_ENDIAN are defined. Undef one of them."
#endif
#endif
-#if !defined(BIG_ENDIAN) && !defined(_WIN_CE) && defined(_WIN_32)
-/* allow not aligned integer access, increases speed in some operations */
-#define ALLOW_NOT_ALIGNED_INT
-#endif
-
-#if defined(__sparc) || defined(sparc) || defined(__sparcv9)
-/* prohibit not aligned access to data structures in text comression
- algorithm, increases memory requirements */
-#define STRICT_ALIGNMENT_REQUIRED
+#if !defined(BIG_ENDIAN) && defined(_WIN_ALL) || defined(__i386__) || defined(__x86_64__)
+// Allow not aligned integer access, increases speed in some operations.
+#define ALLOW_MISALIGNED
#endif
#endif // _RAR_OS_
diff --git a/unrar/unrar/os2ea.cpp b/unrar/unrar/os2ea.cpp
deleted file mode 100644
index f418615..0000000
--- a/unrar/unrar/os2ea.cpp
+++ /dev/null
@@ -1,94 +0,0 @@
-#include <os2.h>
-
-
-
-void ExtractOS2EA(Archive &Arc,char *FileName)
-{
- if (_osmode != OS2_MODE)
- {
- mprintf(St(MSkipEA));
- return;
- }
-
- if (Arc.HeaderCRC!=Arc.EAHead.HeadCRC)
- {
- Log(Arc.FileName,St(MEABroken),FileName);
- ErrHandler.SetErrorCode(CRC_ERROR);
- return;
- }
-
- if (Arc.EAHead.Method<0x31 || Arc.EAHead.Method>0x35 || Arc.EAHead.UnpVer>PACK_VER)
- {
- Log(Arc.FileName,St(MEAUnknHeader),FileName);
- ErrHandler.SetErrorCode(WARNING);
- return;
- }
-
- struct StructEAOP2
- {
- char *GEAPtr;
- char *FEAPtr;
- unsigned long Error;
- } EAOP2;
-
- ComprDataIO DataIO;
- Unpack Unpack(&DataIO);
- Unpack.Init();
-
- Array<unsigned char> UnpData(Arc.EAHead.UnpSize);
- DataIO.SetUnpackToMemory(&UnpData[0],Arc.EAHead.UnpSize);
- DataIO.SetPackedSizeToRead(Arc.EAHead.DataSize);
- DataIO.EnableShowProgress(false);
- DataIO.SetFiles(&Arc,NULL);
- Unpack.SetDestSize(Arc.EAHead.UnpSize);
- Unpack.DoUnpack(Arc.EAHead.UnpVer,false);
-
- if (Arc.EAHead.EACRC!=~DataIO.UnpFileCRC)
- {
- Log(Arc.FileName,St(MEABroken),FileName);
- ErrHandler.SetErrorCode(CRC_ERROR);
- return;
- }
-
- EAOP2.FEAPtr=(char *)&UnpData[0];
- EAOP2.GEAPtr=NULL;
- if (DosSetPathInfo((unsigned char *)FileName,2,&EAOP2,sizeof(EAOP2),0x10)!=0)
- {
- Log(Arc.FileName,St(MCannotSetEA),FileName);
- ErrHandler.SetErrorCode(WARNING);
- }
- File::SetCloseFileTimeByName(FileName,&Arc.NewLhd.mtime,&Arc.NewLhd.atime);
- mprintf(St(MShowEA));
-}
-
-
-void ExtractOS2EANew(Archive &Arc,char *FileName)
-{
- if (_osmode != OS2_MODE)
- {
- mprintf(St(MSkipEA));
- return;
- }
-
- Array<byte> SubData;
- if (!Arc.ReadSubData(&SubData,NULL))
- return;
-
- struct StructEAOP2
- {
- char *GEAPtr;
- char *FEAPtr;
- unsigned long Error;
- } EAOP2;
-
- EAOP2.FEAPtr=(char *)&SubData[0];
- EAOP2.GEAPtr=NULL;
- if (DosSetPathInfo((unsigned char *)FileName,2,&EAOP2,sizeof(EAOP2),0x10)!=0)
- {
- Log(Arc.FileName,St(MCannotSetEA),FileName);
- ErrHandler.SetErrorCode(WARNING);
- }
- File::SetCloseFileTimeByName(FileName,&Arc.NewLhd.mtime,&Arc.NewLhd.atime);
- mprintf(St(MShowEA));
-}
-
diff --git a/unrar/unrar/pathfn.cpp b/unrar/unrar/pathfn.cpp
index 3621916..e959e9d 100644
--- a/unrar/unrar/pathfn.cpp
+++ b/unrar/unrar/pathfn.cpp
@@ -1,99 +1,47 @@
#include "rar.hpp"
-char* PointToName(const char *Path)
-{
- const char *Found=NULL;
- for (const char *s=Path;*s!=0;s=charnext(s))
- if (IsPathDiv(*s))
- Found=(char*)(s+1);
- if (Found!=NULL)
- return((char*)Found);
- return (char*)((*Path && IsDriveDiv(Path[1]) && charnext(Path)==Path+1) ? Path+2:Path);
-}
-
-
wchar* PointToName(const wchar *Path)
{
- for (int I=strlenw(Path)-1;I>=0;I--)
+ for (int I=(int)wcslen(Path)-1;I>=0;I--)
if (IsPathDiv(Path[I]))
return (wchar*)&Path[I+1];
- return (wchar*)((*Path && IsDriveDiv(Path[1])) ? Path+2:Path);
+ return (wchar*)((*Path!=0 && IsDriveDiv(Path[1])) ? Path+2:Path);
}
-char* PointToLastChar(const char *Path)
-{
- for (const char *s=Path,*p=Path;;p=s,s=charnext(s))
- if (*s==0)
- return((char *)p);
-}
-
-
-
-
-char* ConvertPath(const char *SrcPath,char *DestPath)
+wchar* PointToLastChar(const wchar *Path)
{
- const char *DestPtr=SrcPath;
-
- /* prevents \..\ in any part of path string */
- for (const char *s=DestPtr;*s!=0;s++)
- if (IsPathDiv(s[0]) && s[1]=='.' && s[2]=='.' && IsPathDiv(s[3]))
- DestPtr=s+4;
-
- /* removes any sequence of . and \ in the beginning of path string */
- while (*DestPtr)
- {
- const char *s=DestPtr;
- if (s[0] && IsDriveDiv(s[1]))
- s+=2;
- else
- if (s[0]=='\\' && s[1]=='\\')
- {
- const char *Slash=strchr(s+2,'\\');
- if (Slash!=NULL && (Slash=strchr(Slash+1,'\\'))!=NULL)
- s=Slash+1;
- }
- for (const char *t=s;*t!=0;t++)
- if (IsPathDiv(*t))
- s=t+1;
- else
- if (*t!='.')
- break;
- if (s==DestPtr)
- break;
- DestPtr=s;
- }
-
- /* code above does not remove last "..", doing here */
- if (DestPtr[0]=='.' && DestPtr[1]=='.' && DestPtr[2]==0)
- DestPtr+=2;
-
- if (DestPath!=NULL)
- {
- char TmpStr[NM];
- strncpyz(TmpStr,DestPtr,ASIZE(TmpStr));
- strcpy(DestPath,TmpStr);
- }
- return((char *)DestPtr);
+ size_t Length=wcslen(Path);
+ return (wchar*)(Length>0 ? Path+Length-1:Path);
}
-wchar* ConvertPath(const wchar *SrcPath,wchar *DestPath)
+wchar* ConvertPath(const wchar *SrcPath,wchar *DestPath,size_t DestSize)
{
const wchar *DestPtr=SrcPath;
+
+ // Prevent \..\ in any part of path string.
for (const wchar *s=DestPtr;*s!=0;s++)
if (IsPathDiv(s[0]) && s[1]=='.' && s[2]=='.' && IsPathDiv(s[3]))
DestPtr=s+4;
- while (*DestPtr)
+
+ // Remove any amount of <d>:\ and any sequence of . and \ in the beginning of path string.
+ while (*DestPtr!=0)
{
const wchar *s=DestPtr;
- if (s[0] && IsDriveDiv(s[1]))
+ if (s[0]!=0 && IsDriveDiv(s[1]))
s+=2;
- if (s[0]=='\\' && s[1]=='\\')
+
+ // Skip UNC Windows \\server\share\ or Unix //server/share/
+ if (IsPathDiv(s[0]) && IsPathDiv(s[1]))
{
- const wchar *Slash=strchrw(s+2,'\\');
- if (Slash!=NULL && (Slash=strchrw(Slash+1,'\\'))!=NULL)
- s=Slash+1;
+ uint SlashCount=0;
+ for (const wchar *t=s+2;*t!=0;t++)
+ if (IsPathDiv(*t) && ++SlashCount==2)
+ {
+ s=t+1; // Found two more path separators after leading two.
+ break;
+ }
}
for (const wchar *t=s;*t!=0;t++)
if (IsPathDiv(*t))
@@ -105,122 +53,96 @@ wchar* ConvertPath(const wchar *SrcPath,wchar *DestPath)
break;
DestPtr=s;
}
+
+ // Code above does not remove last "..", doing here.
+ if (DestPtr[0]=='.' && DestPtr[1]=='.' && DestPtr[2]==0)
+ DestPtr+=2;
+
if (DestPath!=NULL)
{
+ // SrcPath and DestPath can point to same memory area,
+ // so we use the temporary buffer for copying.
wchar TmpStr[NM];
- strncpyw(TmpStr,DestPtr,sizeof(TmpStr)/sizeof(TmpStr[0])-1);
- strcpyw(DestPath,TmpStr);
+ wcsncpyz(TmpStr,DestPtr,ASIZE(TmpStr));
+ wcsncpyz(DestPath,TmpStr,DestSize);
}
- return((wchar *)DestPtr);
+ return (wchar *)DestPtr;
}
-void SetExt(char *Name,const char *NewExt)
+void SetName(wchar *FullName,const wchar *Name,size_t MaxSize)
{
- char *Dot=GetExt(Name);
- if (NewExt==NULL)
- {
- if (Dot!=NULL)
- *Dot=0;
- }
- else
- if (Dot==NULL)
- {
- strcat(Name,".");
- strcat(Name,NewExt);
- }
- else
- strcpy(Dot+1,NewExt);
+ wchar *NamePtr=PointToName(FullName);
+ wcsncpyz(NamePtr,Name,MaxSize-(NamePtr-FullName));
}
-#ifndef SFX_MODULE
-void SetExt(wchar *Name,const wchar *NewExt)
+void SetExt(wchar *Name,const wchar *NewExt,size_t MaxSize)
{
if (Name==NULL || *Name==0)
return;
wchar *Dot=GetExt(Name);
- if (NewExt==NULL)
+ if (Dot!=NULL)
+ *Dot=0;
+ if (NewExt!=NULL)
{
- if (Dot!=NULL)
- *Dot=0;
+ wcsncatz(Name,L".",MaxSize);
+ wcsncatz(Name,NewExt,MaxSize);
}
- else
- if (Dot==NULL)
- {
- strcatw(Name,L".");
- strcatw(Name,NewExt);
- }
- else
- strcpyw(Dot+1,NewExt);
}
-#endif
#ifndef SFX_MODULE
-void SetSFXExt(char *SFXName)
-{
-#ifdef _UNIX
- SetExt(SFXName,"sfx");
-#endif
-
-#if defined(_WIN_32) || defined(_EMX)
- SetExt(SFXName,"exe");
-#endif
-}
-#endif
-
-
-#ifndef SFX_MODULE
-void SetSFXExt(wchar *SFXName)
+void SetSFXExt(wchar *SFXName,size_t MaxSize)
{
if (SFXName==NULL || *SFXName==0)
return;
#ifdef _UNIX
- SetExt(SFXName,L"sfx");
+ SetExt(SFXName,L"sfx",MaxSize);
#endif
-#if defined(_WIN_32) || defined(_EMX)
- SetExt(SFXName,L"exe");
+#if defined(_WIN_ALL) || defined(_EMX)
+ SetExt(SFXName,L"exe",MaxSize);
#endif
}
#endif
-char *GetExt(const char *Name)
-{
- return(strrchrd(PointToName(Name),'.'));
-}
-
-
+// 'Ext' is an extension with the leading dot, like L".rar".
wchar *GetExt(const wchar *Name)
{
- return(Name==NULL ? (wchar *)L"":strrchrw(PointToName(Name),'.'));
+ return Name==NULL ? NULL:wcsrchr(PointToName(Name),'.');
}
-bool CmpExt(const char *Name,const char *Ext)
+// 'Ext' is an extension without the leading dot, like L"rar".
+bool CmpExt(const wchar *Name,const wchar *Ext)
{
- char *NameExt=GetExt(Name);
- return(NameExt!=NULL && stricomp(NameExt+1,Ext)==0);
+ wchar *NameExt=GetExt(Name);
+ return NameExt!=NULL && wcsicomp(NameExt+1,Ext)==0;
}
-bool IsWildcard(const char *Str,const wchar *StrW)
+bool IsWildcard(const wchar *Str)
{
- if (StrW!=NULL && *StrW!=0)
- return(strpbrkw(StrW,L"*?")!=NULL);
- return(Str==NULL ? false:strpbrk(Str,"*?")!=NULL);
+ if (Str==NULL)
+ return false;
+#ifdef _WIN_ALL
+ // Not treat the special NTFS \\?\d: path prefix as a wildcard.
+ if (Str[0]=='\\' && Str[1]=='\\' && Str[2]=='?' && Str[3]=='\\')
+ Str+=4;
+#endif
+ return wcspbrk(Str,L"*?")!=NULL;
}
bool IsPathDiv(int Ch)
{
-#if defined(_WIN_32) || defined(_EMX)
- return(Ch=='\\' || Ch=='/');
+#ifdef _WIN_ALL
+ return Ch=='\\' || Ch=='/';
#else
- return(Ch==CPATHDIVIDER);
+ return Ch==CPATHDIVIDER;
#endif
}
@@ -228,70 +150,67 @@ bool IsPathDiv(int Ch)
bool IsDriveDiv(int Ch)
{
#ifdef _UNIX
- return(false);
+ return false;
#else
- return(Ch==':');
+ return Ch==':';
#endif
}
-int GetPathDisk(const char *Path)
+bool IsDriveLetter(const wchar *Path)
{
- if (IsDiskLetter(Path))
- return(etoupper(*Path)-'A');
- else
- return(-1);
+ wchar Letter=etoupperw(Path[0]);
+ return Letter>='A' && Letter<='Z' && IsDriveDiv(Path[1]);
}
-void AddEndSlash(char *Path)
+int GetPathDisk(const wchar *Path)
{
- char *LastChar=PointToLastChar(Path);
- if (*LastChar!=0 && *LastChar!=CPATHDIVIDER)
- strcat(LastChar,PATHDIVIDER);
+ if (IsDriveLetter(Path))
+ return etoupperw(*Path)-'A';
+ else
+ return -1;
}
-void AddEndSlash(wchar *Path)
+void AddEndSlash(wchar *Path,size_t MaxLength)
{
- int Length=strlenw(Path);
- if (Length>0 && Path[Length-1]!=CPATHDIVIDER)
- strcatw(Path,PATHDIVIDERW);
+ size_t Length=wcslen(Path);
+ if (Length>0 && Path[Length-1]!=CPATHDIVIDER && Length+1<MaxLength)
+ {
+ Path[Length]=CPATHDIVIDER;
+ Path[Length+1]=0;
+ }
}
-// returns file path including the trailing path separator symbol
-void GetFilePath(const char *FullName,char *Path,int MaxLength)
+void MakeName(const wchar *Path,const wchar *Name,wchar *Pathname,size_t MaxSize)
{
- int PathLength=Min(MaxLength-1,PointToName(FullName)-FullName);
- strncpy(Path,FullName,PathLength);
- Path[PathLength]=0;
+ // 'Path', 'Name' and 'Pathname' can point to same memory area. So we use
+ // the temporary buffer instead of constructing the name in 'Pathname'.
+ wchar OutName[NM];
+ wcsncpyz(OutName,Path,ASIZE(OutName));
+ // Do not add slash to d:, we want to allow relative paths like d:filename.
+ if (!IsDriveLetter(Path) || Path[2]!=0)
+ AddEndSlash(OutName,ASIZE(OutName));
+ wcsncatz(OutName,Name,ASIZE(OutName));
+ wcsncpyz(Pathname,OutName,MaxSize);
}
-// returns file path including the trailing path separator symbol
-void GetFilePath(const wchar *FullName,wchar *Path,int MaxLength)
+// Returns file path including the trailing path separator symbol.
+void GetFilePath(const wchar *FullName,wchar *Path,size_t MaxLength)
{
- int PathLength=Min(MaxLength-1,PointToName(FullName)-FullName);
- strncpyw(Path,FullName,PathLength);
+ if (MaxLength==0)
+ return;
+ size_t PathLength=Min(MaxLength-1,size_t(PointToName(FullName)-FullName));
+ wcsncpy(Path,FullName,PathLength);
Path[PathLength]=0;
}
-// removes name and returns file path without the trailing
-// path separator symbol
-void RemoveNameFromPath(char *Path)
-{
- char *Name=PointToName(Path);
- if (Name>=Path+2 && (!IsDriveDiv(Path[1]) || Name>=Path+4))
- Name--;
- *Name=0;
-}
-
-
-#ifndef SFX_MODULE
-// removes name and returns file path without the trailing
-// path separator symbol
+// Removes name and returns file path without the trailing
+// path separator symbol.
void RemoveNameFromPath(wchar *Path)
{
wchar *Name=PointToName(Path);
@@ -299,11 +218,10 @@ void RemoveNameFromPath(wchar *Path)
Name--;
*Name=0;
}
-#endif
-#if defined(_WIN_32) && !defined(_WIN_CE) && !defined(SFX_MODULE)
-void GetAppDataPath(char *Path)
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
+bool GetAppDataPath(wchar *Path,size_t MaxSize,bool Create)
{
LPMALLOC g_pMalloc;
SHGetMalloc(&g_pMalloc);
@@ -313,106 +231,89 @@ void GetAppDataPath(char *Path)
if (SHGetSpecialFolderLocation(NULL,CSIDL_APPDATA,&ppidl)==NOERROR &&
SHGetPathFromIDList(ppidl,Path) && *Path!=0)
{
- AddEndSlash(Path);
- strcat(Path,"WinRAR");
- Success=FileExist(Path) || MakeDir(Path,NULL,0)==MKDIR_SUCCESS;
- }
- if (!Success)
- {
- GetModuleFileName(NULL,Path,NM);
- RemoveNameFromPath(Path);
+ AddEndSlash(Path,MaxSize);
+ wcsncatz(Path,L"WinRAR",MaxSize);
+ Success=FileExist(Path);
+ if (!Success && Create)
+ Success=MakeDir(Path,false,0)==MKDIR_SUCCESS;
}
g_pMalloc->Free(ppidl);
+ return Success;
}
#endif
-#if defined(_WIN_32) && !defined(_WIN_CE) && !defined(SFX_MODULE)
-void GetRarDataPath(char *Path)
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
+void GetRarDataPath(wchar *Path,size_t MaxSize,bool Create)
{
*Path=0;
HKEY hKey;
- if (RegOpenKeyEx(HKEY_CURRENT_USER,"Software\\WinRAR\\Paths",0,
+ if (RegOpenKeyEx(HKEY_CURRENT_USER,L"Software\\WinRAR\\Paths",0,
KEY_QUERY_VALUE,&hKey)==ERROR_SUCCESS)
{
- DWORD DataSize=NM,Type;
- RegQueryValueEx(hKey,"AppData",0,&Type,(BYTE *)Path,&DataSize);
+ DWORD DataSize=(DWORD)MaxSize,Type;
+ RegQueryValueEx(hKey,L"AppData",0,&Type,(BYTE *)Path,&DataSize);
RegCloseKey(hKey);
}
if (*Path==0 || !FileExist(Path))
- GetAppDataPath(Path);
+ if (!GetAppDataPath(Path,MaxSize,Create))
+ {
+ GetModuleFileName(NULL,Path,(DWORD)MaxSize);
+ RemoveNameFromPath(Path);
+ }
}
#endif
#ifndef SFX_MODULE
-bool EnumConfigPaths(char *Path,int Number)
-{
-#ifdef _EMX
- static char RARFileName[NM];
- if (Number==-1)
- strcpy(RARFileName,Path);
- if (Number!=0)
- return(false);
-#ifndef _DJGPP
- if (_osmode==OS2_MODE)
- {
- PTIB ptib;
- PPIB ppib;
- DosGetInfoBlocks(&ptib, &ppib);
- DosQueryModuleName(ppib->pib_hmte,NM,Path);
- }
- else
-#endif
- strcpy(Path,RARFileName);
- RemoveNameFromPath(Path);
- return(true);
-#elif defined(_UNIX)
- static const char *AltPath[]={
- "/etc","/etc/rar","/usr/lib","/usr/local/lib","/usr/local/etc"
+bool EnumConfigPaths(uint Number,wchar *Path,size_t MaxSize,bool Create)
+{
+#ifdef _UNIX
+ static const wchar *ConfPath[]={
+ L"/etc", L"/etc/rar", L"/usr/lib", L"/usr/local/lib", L"/usr/local/etc"
};
if (Number==0)
{
char *EnvStr=getenv("HOME");
- strncpy(Path, (EnvStr==NULL) ? AltPath[0] : EnvStr, NM-1);
- Path[NM-1]=0;
- return(true);
+ if (EnvStr!=NULL)
+ CharToWide(EnvStr,Path,MaxSize);
+ else
+ wcsncpyz(Path,ConfPath[0],MaxSize);
+ return true;
}
Number--;
- if (Number<0 || Number>=sizeof(AltPath)/sizeof(AltPath[0]))
- return(false);
- strcpy(Path,AltPath[Number]);
- return(true);
-#elif defined(_WIN_32)
-
- if (Number<0 || Number>1)
- return(false);
+ if (Number>=ASIZE(ConfPath))
+ return false;
+ wcsncpyz(Path,ConfPath[Number], MaxSize);
+ return true;
+#elif defined(_WIN_ALL)
+ if (Number>1)
+ return false;
if (Number==0)
- GetRarDataPath(Path);
+ GetRarDataPath(Path,MaxSize,Create);
else
{
- GetModuleFileName(NULL,Path,NM);
+ GetModuleFileName(NULL,Path,(DWORD)MaxSize);
RemoveNameFromPath(Path);
}
- return(true);
-
+ return true;
#else
- return(false);
+ return false;
#endif
}
#endif
#ifndef SFX_MODULE
-void GetConfigName(const char *Name,char *FullName,bool CheckExist)
+void GetConfigName(const wchar *Name,wchar *FullName,size_t MaxSize,bool CheckExist,bool Create)
{
*FullName=0;
- for (int I=0;EnumConfigPaths(FullName,I);I++)
+ for (uint I=0;EnumConfigPaths(I,FullName,MaxSize,Create);I++)
{
- AddEndSlash(FullName);
- strcat(FullName,Name);
+ AddEndSlash(FullName,MaxSize);
+ wcsncatz(FullName,Name,MaxSize);
if (!CheckExist || WildFileExist(FullName))
break;
}
@@ -420,52 +321,84 @@ void GetConfigName(const char *Name,char *FullName,bool CheckExist)
#endif
-// returns a pointer to rightmost digit of volume number
-char* GetVolNumPart(char *ArcName)
+// Returns a pointer to rightmost digit of volume number or to beginning
+// of file name if numeric part is missing.
+wchar* GetVolNumPart(const wchar *ArcName)
{
- char *ChPtr=ArcName+strlen(ArcName)-1;
- while (!isdigit(*ChPtr) && ChPtr>ArcName)
+ // We do not want to increment any characters in path component.
+ ArcName=PointToName(ArcName);
+
+ if (*ArcName==0)
+ return (wchar *)ArcName;
+
+ // Pointing to last name character.
+ const wchar *ChPtr=ArcName+wcslen(ArcName)-1;
+
+ // Skipping the archive extension.
+ while (!IsDigit(*ChPtr) && ChPtr>ArcName)
ChPtr--;
- char *NumPtr=ChPtr;
- while (isdigit(*NumPtr) && NumPtr>ArcName)
+
+ // Skipping the numeric part of name.
+ const wchar *NumPtr=ChPtr;
+ while (IsDigit(*NumPtr) && NumPtr>ArcName)
NumPtr--;
+
+ // Searching for first numeric part in names like name.part##of##.rar.
+ // Stop search on the first dot.
while (NumPtr>ArcName && *NumPtr!='.')
{
- if (isdigit(*NumPtr))
+ if (IsDigit(*NumPtr))
{
- char *Dot=strchrd(PointToName(ArcName),'.');
+ // Validate the first numeric part only if it has a dot somewhere
+ // before it.
+ const wchar *Dot=wcschr(ArcName,'.');
if (Dot!=NULL && Dot<NumPtr)
ChPtr=NumPtr;
break;
}
NumPtr--;
}
- return(ChPtr);
+ return (wchar *)ChPtr;
}
-void NextVolumeName(char *ArcName,wchar *ArcNameW,uint MaxLength,bool OldNumbering)
+void NextVolumeName(wchar *ArcName,uint MaxLength,bool OldNumbering)
{
- char *ChPtr;
+ wchar *ChPtr;
if ((ChPtr=GetExt(ArcName))==NULL)
{
- strcat(ArcName,".rar");
+ wcsncatz(ArcName,L".rar",MaxLength);
ChPtr=GetExt(ArcName);
}
else
- if (ChPtr[1]==0 || stricomp(ChPtr+1,"exe")==0 || stricomp(ChPtr+1,"sfx")==0)
- strcpy(ChPtr+1,"rar");
+ if (ChPtr[1]==0 || wcsicomp(ChPtr,L".exe")==0 || wcsicomp(ChPtr,L".sfx")==0)
+ wcsncpyz(ChPtr,L".rar",MaxLength-(ChPtr-ArcName));
+
+ if (ChPtr==NULL || *ChPtr!='.' || ChPtr[1]==0)
+ {
+ // Normally we shall have some extension here. If we don't, it means
+ // the name has no extension and buffer has no free space to append one.
+ // Let's clear the name to prevent a new call with same name and return.
+ *ArcName=0;
+ return;
+ }
+
if (!OldNumbering)
{
ChPtr=GetVolNumPart(ArcName);
+ // We should not check for IsDigit(*ChPtr) here and should increment
+ // even non-digits. If we got a corrupt archive with volume flag,
+ // but without numeric part, we still need to modify its name somehow,
+ // so while (exist(name)) {NextVolumeName()} loops do not run infinitely.
while ((++(*ChPtr))=='9'+1)
{
*ChPtr='0';
ChPtr--;
- if (ChPtr<ArcName || !isdigit(*ChPtr))
+ if (ChPtr<ArcName || !IsDigit(*ChPtr))
{
- for (char *EndPtr=ArcName+strlen(ArcName);EndPtr!=ChPtr;EndPtr--)
+ // Convert .part:.rar (.part9.rar after increment) to part10.rar.
+ for (wchar *EndPtr=ArcName+wcslen(ArcName);EndPtr!=ChPtr;EndPtr--)
*(EndPtr+1)=*EndPtr;
*(ChPtr+1)='1';
break;
@@ -473,15 +406,15 @@ void NextVolumeName(char *ArcName,wchar *ArcNameW,uint MaxLength,bool OldNumberi
}
}
else
- if (!isdigit(*(ChPtr+2)) || !isdigit(*(ChPtr+3)))
- strcpy(ChPtr+2,"00");
+ if (!IsDigit(ChPtr[2]) || !IsDigit(ChPtr[3]))
+ wcsncpyz(ChPtr+2,L"00",MaxLength-(ChPtr-ArcName)-2); // From .rar to .r00.
else
{
- ChPtr+=3;
- while ((++(*ChPtr))=='9'+1)
- if (*(ChPtr-1)=='.')
+ ChPtr+=wcslen(ChPtr)-1; // Set to last character.
+ while (++(*ChPtr)=='9'+1)
+ if (ChPtr<=ArcName || *(ChPtr-1)=='.')
{
- *ChPtr='A';
+ *ChPtr='a'; // From .999 to .a00 if started from .001 or for too short names.
break;
}
else
@@ -490,291 +423,664 @@ void NextVolumeName(char *ArcName,wchar *ArcNameW,uint MaxLength,bool OldNumberi
ChPtr--;
}
}
- if (ArcNameW!=NULL && *ArcNameW!=0)
- {
- // Copy incremented trailing low ASCII volume name part to Unicode name.
- // It is simpler than implementing Unicode version of entire function.
- char *NumPtr=GetVolNumPart(ArcName);
-
- // moving to first digit in volume number
- while (NumPtr>ArcName && isdigit(*NumPtr) && isdigit(*(NumPtr-1)))
- NumPtr--;
-
- // also copy the first character before volume number,
- // because it can be changed when going from .r99 to .s00
- if (NumPtr>ArcName)
- NumPtr--;
-
- int CharsToCopy=strlen(ArcName)-(NumPtr-ArcName);
- int DestPos=strlenw(ArcNameW)-CharsToCopy;
- if (DestPos>0)
- {
- CharToWide(NumPtr,ArcNameW+DestPos,MaxLength-DestPos-1);
- ArcNameW[MaxLength-1]=0;
- }
- }
}
-bool IsNameUsable(const char *Name)
+bool IsNameUsable(const wchar *Name)
{
-#ifndef _UNIX
- if (Name[0] && Name[1] && strchr(Name+2,':')!=NULL)
- return(false);
- for (const char *s=Name;*s!=0;s=charnext(s))
+ // We were asked to apply Windows-like conversion in Linux in case
+ // files are unpacked to Windows share. This code is invoked only
+ // if file failed to be created, so it doesn't affect extraction
+ // of Unix compatible names to native Unix drives.
+#ifdef _UNIX
+ // Windows shares in Unix do not allow the drive letter,
+ // so unlike Windows version, we check all characters here.
+ if (wcschr(Name,':')!=NULL)
+ return false;
+#else
+ if (Name[0]!=0 && Name[1]!=0 && wcschr(Name+2,':')!=NULL)
+ return false;
+#endif
+ for (const wchar *s=Name;*s!=0;s++)
{
- if (*s<32)
- return(false);
- if (*s==' ' && IsPathDiv(s[1]))
- return(false);
- }
+ if ((uint)*s<32)
+ return false;
+
+ // It is for Windows shares in Unix. We can create such names in Windows.
+#ifdef _UNIX
+ // No spaces or dots before the path separator are allowed in Windows
+ // shares. But they are allowed and automtically removed at the end of
+ // file or folder name, so it is useless to replace them here.
+ // Since such files or folders are created successfully, a supposed
+ // conversion here would never be invoked.
+ if ((*s==' ' || *s=='.') && IsPathDiv(s[1]))
+ return false;
#endif
- return(*Name!=0 && strpbrk(Name,"?*<>|\"")==NULL);
+ }
+ return *Name!=0 && wcspbrk(Name,L"?*<>|\"")==NULL;
}
-void MakeNameUsable(char *Name,bool Extended)
+
+
+void MakeNameUsable(wchar *Name,bool Extended)
{
- for (char *s=Name;*s!=0;s=charnext(s))
+ for (wchar *s=Name;*s!=0;s++)
{
- if (strchr(Extended ? "?*<>|\"":"?*",*s)!=NULL || Extended && *s<32)
- *s='_';
-#ifdef _EMX
- if (*s=='=')
+ if (wcschr(Extended ? L"?*<>|\"":L"?*",*s)!=NULL || Extended && (uint)*s<32)
*s='_';
-#endif
-#ifndef _UNIX
+#ifdef _UNIX
+ // We were asked to apply Windows-like conversion in Linux in case
+ // files are unpacked to Windows share. This code is invoked only
+ // if file failed to be created, so it doesn't affect extraction
+ // of Unix compatible names to native Unix drives.
+ if (Extended)
+ {
+ // Windows shares in Unix do not allow the drive letter,
+ // so unlike Windows version, we check all characters here.
+ if (*s==':')
+ *s='_';
+
+ // No spaces or dots before the path separator are allowed on Windows
+ // shares. But they are allowed and automatically removed at the end of
+ // file or folder name, so it is useless to replace them here.
+ // Since such files or folders are created successfully, a supposed
+ // conversion here would never be invoked.
+ if ((*s==' ' || *s=='.') && IsPathDiv(s[1]))
+ *s='_';
+ }
+#else
if (s-Name>1 && *s==':')
*s='_';
- if (*s==' ' && IsPathDiv(s[1]))
+#if 0 // We already can create such files.
+ // Remove ' ' and '.' before path separator, but allow .\ and ..\.
+ if (IsPathDiv(s[1]) && (*s==' ' || *s=='.' && s>Name &&
+ !IsPathDiv(s[-1]) && (s[-1]!='.' || s>Name+1 && !IsPathDiv(s[-2]))))
*s='_';
#endif
+#endif
}
}
-char* UnixSlashToDos(char *SrcName,char *DestName,uint MaxLength)
+void UnixSlashToDos(const char *SrcName,char *DestName,size_t MaxLength)
{
- if (DestName!=NULL && DestName!=SrcName)
- if (strlen(SrcName)>=MaxLength)
- {
- *DestName=0;
- return(DestName);
- }
- else
- strcpy(DestName,SrcName);
- for (char *s=SrcName;*s!=0;s=charnext(s))
- {
- if (*s=='/')
- if (DestName==NULL)
- *s='\\';
- else
- DestName[s-SrcName]='\\';
- }
- return(DestName==NULL ? SrcName:DestName);
+ size_t Copied=0;
+ for (;Copied<MaxLength-1 && SrcName[Copied]!=0;Copied++)
+ DestName[Copied]=SrcName[Copied]=='/' ? '\\':SrcName[Copied];
+ DestName[Copied]=0;
}
-char* DosSlashToUnix(char *SrcName,char *DestName,uint MaxLength)
+void DosSlashToUnix(const char *SrcName,char *DestName,size_t MaxLength)
{
- if (DestName!=NULL && DestName!=SrcName)
- if (strlen(SrcName)>=MaxLength)
- {
- *DestName=0;
- return(DestName);
- }
- else
- strcpy(DestName,SrcName);
- for (char *s=SrcName;*s!=0;s=charnext(s))
- {
- if (*s=='\\')
- if (DestName==NULL)
- *s='/';
- else
- DestName[s-SrcName]='/';
- }
- return(DestName==NULL ? SrcName:DestName);
+ size_t Copied=0;
+ for (;Copied<MaxLength-1 && SrcName[Copied]!=0;Copied++)
+ DestName[Copied]=SrcName[Copied]=='\\' ? '/':SrcName[Copied];
+ DestName[Copied]=0;
+}
+
+
+void UnixSlashToDos(const wchar *SrcName,wchar *DestName,size_t MaxLength)
+{
+ size_t Copied=0;
+ for (;Copied<MaxLength-1 && SrcName[Copied]!=0;Copied++)
+ DestName[Copied]=SrcName[Copied]=='/' ? '\\':SrcName[Copied];
+ DestName[Copied]=0;
+}
+
+
+void DosSlashToUnix(const wchar *SrcName,wchar *DestName,size_t MaxLength)
+{
+ size_t Copied=0;
+ for (;Copied<MaxLength-1 && SrcName[Copied]!=0;Copied++)
+ DestName[Copied]=SrcName[Copied]=='\\' ? '/':SrcName[Copied];
+ DestName[Copied]=0;
}
-wchar* UnixSlashToDos(wchar *SrcName,wchar *DestName,uint MaxLength)
+void ConvertNameToFull(const wchar *Src,wchar *Dest,size_t MaxSize)
{
- if (DestName!=NULL && DestName!=SrcName)
- if (strlenw(SrcName)>=MaxLength)
+ if (Src==NULL || *Src==0)
+ {
+ if (MaxSize>0)
+ *Dest=0;
+ return;
+ }
+#ifdef _WIN_ALL
+ {
+ wchar FullName[NM],*NamePtr;
+ DWORD Code=GetFullPathName(Src,ASIZE(FullName),FullName,&NamePtr);
+ if (Code==0 || Code>ASIZE(FullName))
{
- *DestName=0;
- return(DestName);
+ wchar LongName[NM];
+ if (GetWinLongPath(Src,LongName,ASIZE(LongName)))
+ Code=GetFullPathName(LongName,ASIZE(FullName),FullName,&NamePtr);
}
+ if (Code!=0 && Code<ASIZE(FullName))
+ wcsncpyz(Dest,FullName,MaxSize);
else
- strcpyw(DestName,SrcName);
- for (wchar *s=SrcName;*s!=0;s++)
+ if (Src!=Dest)
+ wcsncpyz(Dest,Src,MaxSize);
+ }
+#elif defined(_UNIX)
+ if (IsFullPath(Src))
+ *Dest=0;
+ else
{
- if (*s=='/')
- if (DestName==NULL)
- *s='\\';
- else
- DestName[s-SrcName]='\\';
+ char CurDirA[NM];
+ if (getcwd(CurDirA,ASIZE(CurDirA))==NULL)
+ *CurDirA=0;
+ CharToWide(CurDirA,Dest,MaxSize);
+ AddEndSlash(Dest,MaxSize);
}
- return(DestName==NULL ? SrcName:DestName);
-}
-
-
-bool IsFullPath(const char *Path)
-{
- char PathOnly[NM];
- GetFilePath(Path,PathOnly,ASIZE(PathOnly));
- if (IsWildcard(PathOnly,NULL))
- return(true);
-#if defined(_WIN_32) || defined(_EMX)
- return(Path[0]=='\\' && Path[1]=='\\' ||
- IsDiskLetter(Path) && IsPathDiv(Path[2]));
+ wcsncatz(Dest,Src,MaxSize);
#else
- return(IsPathDiv(Path[0]));
+ wcsncpyz(Dest,Src,MaxSize);
#endif
}
bool IsFullPath(const wchar *Path)
{
+/*
wchar PathOnly[NM];
GetFilePath(Path,PathOnly,ASIZE(PathOnly));
- if (IsWildcard(NULL,PathOnly))
- return(true);
-#if defined(_WIN_32) || defined(_EMX)
- return(Path[0]=='\\' && Path[1]=='\\' ||
- IsDiskLetter(Path) && IsPathDiv(Path[2]));
+ if (IsWildcard(PathOnly))
+ return true;
+*/
+#if defined(_WIN_ALL) || defined(_EMX)
+ return Path[0]=='\\' && Path[1]=='\\' || IsDriveLetter(Path) && IsPathDiv(Path[2]);
#else
- return(IsPathDiv(Path[0]));
+ return IsPathDiv(Path[0]);
#endif
}
-bool IsDiskLetter(const char *Path)
+bool IsFullRootPath(const wchar *Path)
{
- char Letter=etoupper(Path[0]);
- return(Letter>='A' && Letter<='Z' && IsDriveDiv(Path[1]));
+ return IsFullPath(Path) || IsPathDiv(Path[0]);
}
-bool IsDiskLetter(const wchar *Path)
-{
- int Letter=etoupper(Path[0]);
- return(Letter>='A' && Letter<='Z' && IsDriveDiv(Path[1]));
-}
-
-
-void GetPathRoot(const char *Path,char *Root)
+void GetPathRoot(const wchar *Path,wchar *Root,size_t MaxSize)
{
*Root=0;
- if (IsDiskLetter(Path))
- sprintf(Root,"%c:\\",*Path);
+ if (IsDriveLetter(Path))
+ swprintf(Root,MaxSize,L"%c:\\",*Path);
else
if (Path[0]=='\\' && Path[1]=='\\')
{
- const char *Slash=strchr(Path+2,'\\');
+ const wchar *Slash=wcschr(Path+2,'\\');
if (Slash!=NULL)
{
size_t Length;
- if ((Slash=strchr(Slash+1,'\\'))!=NULL)
+ if ((Slash=wcschr(Slash+1,'\\'))!=NULL)
Length=Slash-Path+1;
else
- Length=strlen(Path);
- strncpy(Root,Path,Length);
+ Length=wcslen(Path);
+ if (Length>=MaxSize)
+ Length=0;
+ wcsncpy(Root,Path,Length);
Root[Length]=0;
}
}
}
-int ParseVersionFileName(char *Name,wchar *NameW,bool Truncate)
+int ParseVersionFileName(wchar *Name,bool Truncate)
{
int Version=0;
- char *VerText=strrchrd(Name,';');
+ wchar *VerText=wcsrchr(Name,';');
if (VerText!=NULL)
{
- Version=atoi(VerText+1);
+ Version=atoiw(VerText+1);
if (Truncate)
*VerText=0;
}
- if (NameW!=NULL)
- {
- wchar *VerTextW=strrchrw(NameW,';');
- if (VerTextW!=NULL)
- {
- if (Version==0)
- Version=atoiw(VerTextW+1);
- if (Truncate)
- *VerTextW=0;
- }
- }
- return(Version);
+ return Version;
}
-#ifndef SFX_MODULE
-char* VolNameToFirstName(const char *VolName,char *FirstName,bool NewNumbering)
+#if !defined(SFX_MODULE)
+// Get the name of first volume. Return the leftmost digit of volume number.
+wchar* VolNameToFirstName(const wchar *VolName,wchar *FirstName,size_t MaxSize,bool NewNumbering)
{
if (FirstName!=VolName)
- strcpy(FirstName,VolName);
- char *VolNumStart=FirstName;
+ wcsncpyz(FirstName,VolName,MaxSize);
+ wchar *VolNumStart=FirstName;
if (NewNumbering)
{
- int N='1';
- for (char *ChPtr=GetVolNumPart(FirstName);ChPtr>FirstName;ChPtr--)
- if (isdigit(*ChPtr))
+ wchar N='1';
+
+ // From the rightmost digit of volume number to the left.
+ for (wchar *ChPtr=GetVolNumPart(FirstName);ChPtr>FirstName;ChPtr--)
+ if (IsDigit(*ChPtr))
{
- *ChPtr=N;
+ *ChPtr=N; // Set the rightmost digit to '1' and others to '0'.
N='0';
}
else
if (N=='0')
{
- VolNumStart=ChPtr+1;
+ VolNumStart=ChPtr+1; // Store the position of leftmost digit in volume number.
break;
}
}
else
{
- SetExt(FirstName,"rar");
+ // Old volume numbering scheme. Just set the extension to ".rar".
+ SetExt(FirstName,L"rar",MaxSize);
VolNumStart=GetExt(FirstName);
}
if (!FileExist(FirstName))
{
- char Mask[NM];
- strcpy(Mask,FirstName);
- SetExt(Mask,"*");
+ // If the first volume, which name we just generated, does not exist,
+ // check if volume with same name and any other extension is available.
+ // It can help in case of *.exe or *.sfx first volume.
+ wchar Mask[NM];
+ wcsncpyz(Mask,FirstName,ASIZE(Mask));
+ SetExt(Mask,L"*",ASIZE(Mask));
FindFile Find;
Find.SetMask(Mask);
- struct FindData FD;
+ FindData FD;
while (Find.Next(&FD))
{
Archive Arc;
- if (Arc.Open(FD.Name,FD.NameW) && Arc.IsArchive(true) && !Arc.NotFirstVolume)
+ if (Arc.Open(FD.Name,0) && Arc.IsArchive(true) && Arc.FirstVolume)
{
- strcpy(FirstName,FD.Name);
+ wcsncpyz(FirstName,FD.Name,MaxSize);
break;
}
}
}
- return(VolNumStart);
+ return VolNumStart;
}
#endif
+#ifndef SFX_MODULE
+static void GenArcName(wchar *ArcName,size_t MaxSize,const wchar *GenerateMask,uint ArcNumber,bool &ArcNumPresent)
+{
+ bool Prefix=false;
+ if (*GenerateMask=='+')
+ {
+ Prefix=true; // Add the time string before the archive name.
+ GenerateMask++; // Skip '+' in the beginning of time mask.
+ }
+
+ wchar Mask[MAX_GENERATE_MASK];
+ wcsncpyz(Mask,*GenerateMask!=0 ? GenerateMask:L"yyyymmddhhmmss",ASIZE(Mask));
+
+ bool QuoteMode=false;
+ uint MAsMinutes=0; // By default we treat 'M' as months.
+ for (uint I=0;Mask[I]!=0;I++)
+ {
+ if (Mask[I]=='{' || Mask[I]=='}')
+ {
+ QuoteMode=(Mask[I]=='{');
+ continue;
+ }
+ if (QuoteMode)
+ continue;
+ int CurChar=toupperw(Mask[I]);
+ if (CurChar=='H')
+ MAsMinutes=2; // Treat next two 'M' after 'H' as minutes.
+ if (CurChar=='D' || CurChar=='Y')
+ MAsMinutes=0; // Treat 'M' in HHDDMMYY and HHYYMMDD as month.
+
+ if (MAsMinutes>0 && CurChar=='M')
+ {
+ // Replace minutes with 'I'. We use 'M' both for months and minutes,
+ // so we treat as minutes only those 'M', which are found after hours.
+ Mask[I]='I';
+ MAsMinutes--;
+ }
+ if (CurChar=='N')
+ {
+ uint Digits=GetDigits(ArcNumber);
+ uint NCount=0;
+ while (toupperw(Mask[I+NCount])=='N')
+ NCount++;
+
+ // Here we ensure that we have enough 'N' characters to fit all digits
+ // of archive number. We'll replace them by actual number later
+ // in this function.
+ if (NCount<Digits && wcslen(Mask)+Digits-NCount<ASIZE(Mask))
+ {
+ wmemmove(Mask+I+Digits,Mask+I+NCount,wcslen(Mask+I+NCount)+1);
+ wmemset(Mask+I,'N',Digits);
+ }
+ I+=Max(Digits,NCount)-1;
+ ArcNumPresent=true;
+ continue;
+ }
+ }
+
+ RarTime CurTime;
+ CurTime.SetCurrentTime();
+ RarLocalTime rlt;
+ CurTime.GetLocal(&rlt);
+
+ wchar Ext[NM],*Dot=GetExt(ArcName);
+ *Ext=0;
+ if (Dot==NULL)
+ wcsncpyz(Ext,*PointToName(ArcName)==0 ? L".rar":L"",ASIZE(Ext));
+ else
+ {
+ wcsncpyz(Ext,Dot,ASIZE(Ext));
+ *Dot=0;
+ }
+
+ int WeekDay=rlt.wDay==0 ? 6:rlt.wDay-1;
+ int StartWeekDay=rlt.yDay-WeekDay;
+ if (StartWeekDay<0)
+ if (StartWeekDay<=-4)
+ StartWeekDay+=IsLeapYear(rlt.Year-1) ? 366:365;
+ else
+ StartWeekDay=0;
+ int CurWeek=StartWeekDay/7+1;
+ if (StartWeekDay%7>=4)
+ CurWeek++;
+
+ char Field[10][11];
+
+ sprintf(Field[0],"%04u",rlt.Year);
+ sprintf(Field[1],"%02u",rlt.Month);
+ sprintf(Field[2],"%02u",rlt.Day);
+ sprintf(Field[3],"%02u",rlt.Hour);
+ sprintf(Field[4],"%02u",rlt.Minute);
+ sprintf(Field[5],"%02u",rlt.Second);
+ sprintf(Field[6],"%02u",(uint)CurWeek);
+ sprintf(Field[7],"%u",(uint)WeekDay+1);
+ sprintf(Field[8],"%03u",rlt.yDay+1);
+ sprintf(Field[9],"%05u",ArcNumber);
+
+ const wchar *MaskChars=L"YMDHISWAEN";
+
+ // How many times every modifier character was encountered in the mask.
+ int CField[sizeof(Field)/sizeof(Field[0])];
+
+ memset(CField,0,sizeof(CField));
+ QuoteMode=false;
+ for (uint I=0;Mask[I]!=0;I++)
+ {
+ if (Mask[I]=='{' || Mask[I]=='}')
+ {
+ QuoteMode=(Mask[I]=='{');
+ continue;
+ }
+ if (QuoteMode)
+ continue;
+ const wchar *ChPtr=wcschr(MaskChars,toupperw(Mask[I]));
+ if (ChPtr!=NULL)
+ CField[ChPtr-MaskChars]++;
+ }
+
+ wchar DateText[MAX_GENERATE_MASK];
+ *DateText=0;
+ QuoteMode=false;
+ for (size_t I=0,J=0;Mask[I]!=0 && J<ASIZE(DateText)-1;I++)
+ {
+ if (Mask[I]=='{' || Mask[I]=='}')
+ {
+ QuoteMode=(Mask[I]=='{');
+ continue;
+ }
+ const wchar *ChPtr=wcschr(MaskChars,toupperw(Mask[I]));
+ if (ChPtr==NULL || QuoteMode)
+ {
+ DateText[J]=Mask[I];
+#ifdef _WIN_ALL
+ // We do not allow ':' in Windows because of NTFS streams.
+ // Users had problems after specifying hh:mm mask.
+ if (DateText[J]==':')
+ DateText[J]='_';
+#endif
+ }
+ else
+ {
+ size_t FieldPos=ChPtr-MaskChars;
+ int CharPos=(int)strlen(Field[FieldPos])-CField[FieldPos]--;
+
+ // CField[FieldPos] shall have exactly 3 "MMM" symbols, so we do not
+ // repeat the month name in case "MMMMMMMM" mask. But since we
+ // decremented CField[FieldPos] above, we compared it with 2.
+ if (FieldPos==1 && CField[FieldPos]==2 &&
+ toupperw(Mask[I+1])=='M' && toupperw(Mask[I+2])=='M')
+ {
+ wcsncpyz(DateText+J,GetMonthName(rlt.Month-1),ASIZE(DateText)-J);
+ J=wcslen(DateText);
+ I+=2;
+ continue;
+ }
+ // If CharPos is negative, we have more modifier characters than
+ // matching time data. We prefer to issue a modifier character
+ // instead of repeating time data from beginning, so user can notice
+ // excessive modifiers added by mistake.
+ if (CharPos<0)
+ DateText[J]=Mask[I];
+ else
+ DateText[J]=Field[FieldPos][CharPos];
+ }
+ DateText[++J]=0;
+ }
+
+ if (Prefix)
+ {
+ wchar NewName[NM];
+ GetFilePath(ArcName,NewName,ASIZE(NewName));
+ AddEndSlash(NewName,ASIZE(NewName));
+ wcsncatz(NewName,DateText,ASIZE(NewName));
+ wcsncatz(NewName,PointToName(ArcName),ASIZE(NewName));
+ wcsncpyz(ArcName,NewName,MaxSize);
+ }
+ else
+ wcsncatz(ArcName,DateText,MaxSize);
+ wcsncatz(ArcName,Ext,MaxSize);
+}
+
+
+void GenerateArchiveName(wchar *ArcName,size_t MaxSize,const wchar *GenerateMask,bool Archiving)
+{
+ wchar NewName[NM];
+ uint ArcNumber=1;
+ while (true) // Loop for 'N' (archive number) processing.
+ {
+ wcsncpyz(NewName,ArcName,ASIZE(NewName));
+
+ bool ArcNumPresent=false;
-wchar* GetWideName(const char *Name,const wchar *NameW,wchar *DestW)
+ GenArcName(NewName,ASIZE(NewName),GenerateMask,ArcNumber,ArcNumPresent);
+
+ if (!ArcNumPresent)
+ break;
+ if (!FileExist(NewName))
+ {
+ if (!Archiving && ArcNumber>1)
+ {
+ // If we perform non-archiving operation, we need to use the last
+ // existing archive before the first unused name. So we generate
+ // the name for (ArcNumber-1) below.
+ wcsncpyz(NewName,NullToEmpty(ArcName),ASIZE(NewName));
+ GenArcName(NewName,ASIZE(NewName),GenerateMask,ArcNumber-1,ArcNumPresent);
+ }
+ break;
+ }
+ ArcNumber++;
+ }
+ wcsncpyz(ArcName,NewName,MaxSize);
+}
+#endif
+
+
+wchar* GetWideName(const char *Name,const wchar *NameW,wchar *DestW,size_t DestSize)
{
if (NameW!=NULL && *NameW!=0)
{
if (DestW!=NameW)
- strcpyw(DestW,NameW);
+ wcsncpy(DestW,NameW,DestSize);
+ }
+ else
+ if (Name!=NULL)
+ CharToWide(Name,DestW,DestSize);
+ else
+ *DestW=0;
+
+ // Ensure that we return a zero terminate string for security reasons.
+ if (DestSize>0)
+ DestW[DestSize-1]=0;
+
+ return DestW;
+}
+
+
+#ifdef _WIN_ALL
+// We should return 'true' even if resulting path is shorter than MAX_PATH,
+// because we can also use this function to open files with non-standard
+// characters, even if their path length is normal.
+bool GetWinLongPath(const wchar *Src,wchar *Dest,size_t MaxSize)
+{
+ if (*Src==0)
+ return false;
+ const wchar *Prefix=L"\\\\?\\";
+ const size_t PrefixLength=4;
+ bool FullPath=IsDriveLetter(Src) && IsPathDiv(Src[2]);
+ size_t SrcLength=wcslen(Src);
+ if (IsFullPath(Src)) // Paths in d:\path\name format.
+ {
+ if (IsDriveLetter(Src))
+ {
+ if (MaxSize<=PrefixLength+SrcLength)
+ return false;
+ wcsncpyz(Dest,Prefix,MaxSize);
+ wcsncatz(Dest,Src,MaxSize); // "\\?\D:\very long path".
+ return true;
+ }
+ else
+ if (Src[0]=='\\' && Src[1]=='\\')
+ {
+ if (MaxSize<=PrefixLength+SrcLength+2)
+ return false;
+ wcsncpyz(Dest,Prefix,MaxSize);
+ wcsncatz(Dest,L"UNC",MaxSize);
+ wcsncatz(Dest,Src+1,MaxSize); // "\\?\UNC\server\share".
+ return true;
+ }
+ // We may be here only if we modify IsFullPath in the future.
+ return false;
}
else
- CharToWide(Name,DestW);
- return(DestW);
+ {
+ wchar CurDir[NM];
+ DWORD DirCode=GetCurrentDirectory(ASIZE(CurDir)-1,CurDir);
+ if (DirCode==0 || DirCode>ASIZE(CurDir)-1)
+ return false;
+
+ if (IsPathDiv(Src[0])) // Paths in \path\name format.
+ {
+ if (MaxSize<=PrefixLength+SrcLength+2)
+ return false;
+ wcsncpyz(Dest,Prefix,MaxSize);
+ CurDir[2]=0;
+ wcsncatz(Dest,CurDir,MaxSize); // Copy drive letter 'd:'.
+ wcsncatz(Dest,Src,MaxSize);
+ return true;
+ }
+ else // Paths in path\name format.
+ {
+ AddEndSlash(CurDir,ASIZE(CurDir));
+ if (MaxSize<=PrefixLength+wcslen(CurDir)+SrcLength)
+ return false;
+ wcsncpyz(Dest,Prefix,MaxSize);
+ wcsncatz(Dest,CurDir,MaxSize);
+
+ if (Src[0]=='.' && IsPathDiv(Src[1])) // Remove leading .\ in pathname.
+ Src+=2;
+
+ wcsncatz(Dest,Src,MaxSize);
+ return true;
+ }
+ }
+ return false;
}
+// Convert Unix, OS X and Android decomposed chracters to Windows precomposed.
+void ConvertToPrecomposed(wchar *Name,size_t NameSize)
+{
+ wchar FileName[NM];
+ if (WinNT()>=WNT_VISTA && // MAP_PRECOMPOSED is not supported in XP.
+ FoldString(MAP_PRECOMPOSED,Name,-1,FileName,ASIZE(FileName))!=0)
+ {
+ FileName[ASIZE(FileName)-1]=0;
+ wcsncpyz(Name,FileName,NameSize);
+ }
+}
+
+
+void MakeNameCompatible(wchar *Name,size_t MaxSize)
+{
+ // Remove trailing spaces and dots in file name and in dir names in path.
+ int Src=0,Dest=0;
+ while (true)
+ {
+ if (IsPathDiv(Name[Src]) || Name[Src]==0)
+ for (int I=Dest-1;I>0 && (Name[I]==' ' || Name[I]=='.');I--)
+ {
+ // Permit path1/./path2 and ../path1 paths.
+ if (Name[I]=='.' && (IsPathDiv(Name[I-1]) || Name[I-1]=='.' && I==1))
+ break;
+ Dest--;
+ }
+ Name[Dest]=Name[Src];
+ if (Name[Src]==0)
+ break;
+ Src++;
+ Dest++;
+ }
+
+ // Rename reserved device names, such as aux.txt to _aux.txt.
+ // We check them in path components too, where they are also prohibited.
+ for (uint I=0;Name[I]!=0;I++)
+ if (I==0 || I>0 && IsPathDiv(Name[I-1]))
+ {
+ static const wchar *Devices[]={L"CON",L"PRN",L"AUX",L"NUL",L"COM#",L"LPT#"};
+ wchar *s=Name+I;
+ bool MatchFound=false;
+ for (uint J=0;J<ASIZE(Devices);J++)
+ for (uint K=0;;K++)
+ if (Devices[J][K]=='#')
+ {
+ if (!IsDigit(s[K]))
+ break;
+ }
+ else
+ if (Devices[J][K]==0)
+ {
+ // Names like aux.txt are accessible without \\?\ prefix
+ // since Windows 11. Pure aux is still prohibited.
+ MatchFound=s[K]==0 || s[K]=='.' && !IsWindows11OrGreater() || IsPathDiv(s[K]);
+ break;
+ }
+ else
+ if (Devices[J][K]!=toupperw(s[K]))
+ break;
+ if (MatchFound)
+ {
+ wchar OrigName[NM];
+ wcsncpyz(OrigName,Name,ASIZE(OrigName));
+ if (MaxSize>I+1) // I+1, because we do not move the trailing 0.
+ memmove(s+1,s,(MaxSize-I-1)*sizeof(*s));
+ *s='_';
+#ifndef SFX_MODULE
+ uiMsg(UIMSG_CORRECTINGNAME,nullptr);
+ uiMsg(UIERROR_RENAMING,nullptr,OrigName,Name);
+#endif
+ }
+ }
+}
+#endif
diff --git a/unrar/unrar/pathfn.hpp b/unrar/unrar/pathfn.hpp
index 6b0ac31..62cae0a 100644
--- a/unrar/unrar/pathfn.hpp
+++ b/unrar/unrar/pathfn.hpp
@@ -1,49 +1,76 @@
#ifndef _RAR_PATHFN_
#define _RAR_PATHFN_
-char* PointToName(const char *Path);
wchar* PointToName(const wchar *Path);
-char* PointToLastChar(const char *Path);
-char* ConvertPath(const char *SrcPath,char *DestPath);
-wchar* ConvertPath(const wchar *SrcPath,wchar *DestPath);
-void SetExt(char *Name,const char *NewExt);
-void SetExt(wchar *Name,const wchar *NewExt);
-void SetSFXExt(char *SFXName);
-void SetSFXExt(wchar *SFXName);
-char *GetExt(const char *Name);
+wchar* PointToLastChar(const wchar *Path);
+wchar* ConvertPath(const wchar *SrcPath,wchar *DestPath,size_t DestSize);
+void SetName(wchar *FullName,const wchar *Name,size_t MaxSize);
+void SetExt(wchar *Name,const wchar *NewExt,size_t MaxSize);
+void SetSFXExt(wchar *SFXName,size_t MaxSize);
wchar *GetExt(const wchar *Name);
-bool CmpExt(const char *Name,const char *Ext);
-bool IsWildcard(const char *Str,const wchar *StrW=NULL);
+bool CmpExt(const wchar *Name,const wchar *Ext);
+bool IsWildcard(const wchar *Str);
bool IsPathDiv(int Ch);
bool IsDriveDiv(int Ch);
-int GetPathDisk(const char *Path);
-void AddEndSlash(char *Path);
-void AddEndSlash(wchar *Path);
-void GetFilePath(const char *FullName,char *Path,int MaxLength);
-void GetFilePath(const wchar *FullName,wchar *Path,int MaxLength);
-void RemoveNameFromPath(char *Path);
+bool IsDriveLetter(const wchar *Path);
+int GetPathDisk(const wchar *Path);
+void AddEndSlash(wchar *Path,size_t MaxLength);
+void MakeName(const wchar *Path,const wchar *Name,wchar *Pathname,size_t MaxSize);
+void GetFilePath(const wchar *FullName,wchar *Path,size_t MaxLength);
void RemoveNameFromPath(wchar *Path);
-void GetAppDataPath(char *Path);
-void GetRarDataPath(char *Path);
-bool EnumConfigPaths(char *Path,int Number);
-void GetConfigName(const char *Name,char *FullName,bool CheckExist);
-char* GetVolNumPart(char *ArcName);
-void NextVolumeName(char *ArcName,wchar *ArcNameW,uint MaxLength,bool OldNumbering);
-bool IsNameUsable(const char *Name);
-void MakeNameUsable(char *Name,bool Extended);
-char* UnixSlashToDos(char *SrcName,char *DestName=NULL,uint MaxLength=NM);
-char* DosSlashToUnix(char *SrcName,char *DestName=NULL,uint MaxLength=NM);
-wchar* UnixSlashToDos(wchar *SrcName,wchar *DestName=NULL,uint MaxLength=NM);
-bool IsFullPath(const char *Path);
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
+bool GetAppDataPath(wchar *Path,size_t MaxSize,bool Create);
+void GetRarDataPath(wchar *Path,size_t MaxSize,bool Create);
+#endif
+#ifndef SFX_MODULE
+bool EnumConfigPaths(uint Number,wchar *Path,size_t MaxSize,bool Create);
+void GetConfigName(const wchar *Name,wchar *FullName,size_t MaxSize,bool CheckExist,bool Create);
+#endif
+wchar* GetVolNumPart(const wchar *ArcName);
+void NextVolumeName(wchar *ArcName,uint MaxLength,bool OldNumbering);
+bool IsNameUsable(const wchar *Name);
+void MakeNameUsable(wchar *Name,bool Extended);
+
+void UnixSlashToDos(const char *SrcName,char *DestName,size_t MaxLength);
+void DosSlashToUnix(const char *SrcName,char *DestName,size_t MaxLength);
+void UnixSlashToDos(const wchar *SrcName,wchar *DestName,size_t MaxLength);
+void DosSlashToUnix(const wchar *SrcName,wchar *DestName,size_t MaxLength);
+
+inline void SlashToNative(const char *SrcName,char *DestName,size_t MaxLength)
+{
+#ifdef _WIN_ALL
+ UnixSlashToDos(SrcName,DestName,MaxLength);
+#else
+ DosSlashToUnix(SrcName,DestName,MaxLength);
+#endif
+}
+
+inline void SlashToNative(const wchar *SrcName,wchar *DestName,size_t MaxLength)
+{
+#ifdef _WIN_ALL
+ UnixSlashToDos(SrcName,DestName,MaxLength);
+#else
+ DosSlashToUnix(SrcName,DestName,MaxLength);
+#endif
+}
+
+void ConvertNameToFull(const wchar *Src,wchar *Dest,size_t MaxSize);
bool IsFullPath(const wchar *Path);
-bool IsDiskLetter(const char *Path);
-bool IsDiskLetter(const wchar *Path);
-void GetPathRoot(const char *Path,char *Root);
-int ParseVersionFileName(char *Name,wchar *NameW,bool Truncate);
-char* VolNameToFirstName(const char *VolName,char *FirstName,bool NewNumbering);
-wchar* GetWideName(const char *Name,const wchar *NameW,wchar *DestW);
+bool IsFullRootPath(const wchar *Path);
+void GetPathRoot(const wchar *Path,wchar *Root,size_t MaxSize);
+int ParseVersionFileName(wchar *Name,bool Truncate);
+wchar* VolNameToFirstName(const wchar *VolName,wchar *FirstName,size_t MaxSize,bool NewNumbering);
+wchar* GetWideName(const char *Name,const wchar *NameW,wchar *DestW,size_t DestSize);
+#ifndef SFX_MODULE
+void GenerateArchiveName(wchar *ArcName,size_t MaxSize,const wchar *GenerateMask,bool Archiving);
+#endif
+
+#ifdef _WIN_ALL
+bool GetWinLongPath(const wchar *Src,wchar *Dest,size_t MaxSize);
+void ConvertToPrecomposed(wchar *Name,size_t NameSize);
+void MakeNameCompatible(wchar *Name,size_t MaxSize);
+#endif
-inline char* GetOutputName(const char *Name) {return((char *)Name);};
#endif
diff --git a/unrar/unrar/qopen.cpp b/unrar/unrar/qopen.cpp
new file mode 100644
index 0000000..d906d06
--- /dev/null
+++ b/unrar/unrar/qopen.cpp
@@ -0,0 +1,300 @@
+#include "rar.hpp"
+
+QuickOpen::QuickOpen()
+{
+ Buf=NULL;
+ Init(NULL,false);
+}
+
+
+QuickOpen::~QuickOpen()
+{
+ Close();
+ delete[] Buf;
+}
+
+
+void QuickOpen::Init(Archive *Arc,bool WriteMode)
+{
+ if (Arc!=NULL) // Unless called from constructor.
+ Close();
+
+ QuickOpen::Arc=Arc;
+ QuickOpen::WriteMode=WriteMode;
+
+ ListStart=NULL;
+ ListEnd=NULL;
+
+ if (Buf==NULL)
+ Buf=new byte[MaxBufSize];
+
+ CurBufSize=0; // Current size of buffered data in write mode.
+
+ Loaded=false;
+}
+
+
+void QuickOpen::Close()
+{
+ QuickOpenItem *Item=ListStart;
+ while (Item!=NULL)
+ {
+ QuickOpenItem *Next=Item->Next;
+ delete[] Item->Header;
+ delete Item;
+ Item=Next;
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+void QuickOpen::Load(uint64 BlockPos)
+{
+ if (!Loaded)
+ {
+ // If loading for the first time, perform additional intialization.
+ SeekPos=Arc->Tell();
+ UnsyncSeekPos=false;
+
+ int64 SavePos=SeekPos;
+ Arc->Seek(BlockPos,SEEK_SET);
+
+ // If BlockPos points to original main header, we'll have the infinite
+ // recursion, because ReadHeader() for main header will attempt to load
+ // QOpen and call QuickOpen::Load again. If BlockPos points to long chain
+ // of other main headers, we'll have multiple recursive calls of this
+ // function wasting resources. So we prohibit QOpen temporarily to
+ // prevent this. ReadHeader() calls QOpen.Init and sets MainHead Locator
+ // and QOpenOffset fields, so we cannot use them to prohibit QOpen.
+ Arc->SetProhibitQOpen(true);
+ size_t ReadSize=Arc->ReadHeader();
+ Arc->SetProhibitQOpen(false);
+
+ if (ReadSize==0 || Arc->GetHeaderType()!=HEAD_SERVICE ||
+ !Arc->SubHead.CmpName(SUBHEAD_TYPE_QOPEN))
+ {
+ Arc->Seek(SavePos,SEEK_SET);
+ return;
+ }
+ QOHeaderPos=Arc->CurBlockPos;
+ RawDataStart=Arc->Tell();
+ RawDataSize=Arc->SubHead.UnpSize;
+ Arc->Seek(SavePos,SEEK_SET);
+
+ Loaded=true; // Set only after all file processing calls like Tell, Seek, ReadHeader.
+ }
+
+ if (Arc->SubHead.Encrypted)
+ {
+ CommandData *Cmd=Arc->GetCommandData();
+#ifndef RAR_NOCRYPT
+ if (Cmd->Password.IsSet())
+ Crypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,Arc->SubHead.Salt,
+ Arc->SubHead.InitV,Arc->SubHead.Lg2Count,
+ Arc->SubHead.HashKey,Arc->SubHead.PswCheck);
+ else
+#endif
+ {
+ Loaded=false;
+ return;
+ }
+ }
+
+ RawDataPos=0;
+ ReadBufSize=0;
+ ReadBufPos=0;
+ LastReadHeader.Reset();
+ LastReadHeaderPos=0;
+
+ ReadBuffer();
+}
+
+
+bool QuickOpen::Read(void *Data,size_t Size,size_t &Result)
+{
+ if (!Loaded)
+ return false;
+ // Find next suitable cached block.
+ while (LastReadHeaderPos+LastReadHeader.Size()<=SeekPos)
+ if (!ReadNext())
+ break;
+ if (!Loaded)
+ {
+ // If something wrong happened, let's set the correct file pointer
+ // and stop further quick open processing.
+ if (UnsyncSeekPos)
+ Arc->File::Seek(SeekPos,SEEK_SET);
+ return false;
+ }
+
+ if (SeekPos>=LastReadHeaderPos && SeekPos+Size<=LastReadHeaderPos+LastReadHeader.Size())
+ {
+ memcpy(Data,LastReadHeader+size_t(SeekPos-LastReadHeaderPos),Size);
+ Result=Size;
+ SeekPos+=Size;
+ UnsyncSeekPos=true;
+ }
+ else
+ {
+ if (UnsyncSeekPos)
+ {
+ Arc->File::Seek(SeekPos,SEEK_SET);
+ UnsyncSeekPos=false;
+ }
+ int ReadSize=Arc->File::Read(Data,Size);
+ if (ReadSize<0)
+ {
+ Loaded=false;
+ return false;
+ }
+ Result=ReadSize;
+ SeekPos+=ReadSize;
+ }
+
+ return true;
+}
+
+
+bool QuickOpen::Seek(int64 Offset,int Method)
+{
+ if (!Loaded)
+ return false;
+
+ // Normally we process an archive sequentially from beginning to end,
+ // so we read quick open data sequentially. But some operations like
+ // archive updating involve several passes. So if we detect that file
+ // pointer is moved back, we reload quick open data from beginning.
+ if (Method==SEEK_SET && (uint64)Offset<SeekPos && (uint64)Offset<LastReadHeaderPos)
+ Load(QOHeaderPos);
+
+ if (Method==SEEK_SET)
+ SeekPos=Offset;
+ if (Method==SEEK_CUR)
+ SeekPos+=Offset;
+ UnsyncSeekPos=true;
+
+ if (Method==SEEK_END)
+ {
+ Arc->File::Seek(Offset,SEEK_END);
+ SeekPos=Arc->File::Tell();
+ UnsyncSeekPos=false;
+ }
+ return true;
+}
+
+
+bool QuickOpen::Tell(int64 *Pos)
+{
+ if (!Loaded)
+ return false;
+ *Pos=SeekPos;
+ return true;
+}
+
+
+uint QuickOpen::ReadBuffer()
+{
+ int64 SavePos=Arc->Tell();
+ Arc->File::Seek(RawDataStart+RawDataPos,SEEK_SET);
+ size_t SizeToRead=(size_t)Min(RawDataSize-RawDataPos,MaxBufSize-ReadBufSize);
+ if (Arc->SubHead.Encrypted)
+ SizeToRead &= ~CRYPT_BLOCK_MASK;
+ int ReadSize=0;
+ if (SizeToRead!=0)
+ {
+ ReadSize=Arc->File::Read(Buf+ReadBufSize,SizeToRead);
+ if (ReadSize<=0)
+ ReadSize=0;
+ else
+ {
+#ifndef RAR_NOCRYPT
+ if (Arc->SubHead.Encrypted)
+ Crypt.DecryptBlock(Buf+ReadBufSize,ReadSize & ~CRYPT_BLOCK_MASK);
+#endif
+ RawDataPos+=ReadSize;
+ ReadBufSize+=ReadSize;
+ }
+ }
+ Arc->Seek(SavePos,SEEK_SET);
+ return ReadSize;
+}
+
+
+// Fill RawRead object from buffer.
+bool QuickOpen::ReadRaw(RawRead &Raw)
+{
+ if (MaxBufSize-ReadBufPos<0x100) // We are close to end of buffer.
+ {
+ // Ensure that we have enough data to read CRC and header size.
+ size_t DataLeft=ReadBufSize-ReadBufPos;
+ memcpy(Buf,Buf+ReadBufPos,DataLeft);
+ ReadBufPos=0;
+ ReadBufSize=DataLeft;
+ ReadBuffer();
+ }
+ const size_t FirstReadSize=7;
+ if (ReadBufPos+FirstReadSize>ReadBufSize)
+ return false;
+ Raw.Read(Buf+ReadBufPos,FirstReadSize);
+ ReadBufPos+=FirstReadSize;
+
+ uint SavedCRC=Raw.Get4();
+ uint SizeBytes=Raw.GetVSize(4);
+ uint64 BlockSize=Raw.GetV();
+ int SizeToRead=int(BlockSize);
+ SizeToRead-=FirstReadSize-SizeBytes-4; // Adjust overread size bytes if any.
+ if (SizeToRead<0 || SizeBytes==0 || BlockSize==0)
+ {
+ Loaded=false; // Invalid data.
+ return false;
+ }
+
+ // If rest of block data crosses Buf boundary, read it in loop.
+ while (SizeToRead>0)
+ {
+ size_t DataLeft=ReadBufSize-ReadBufPos;
+ size_t CurSizeToRead=Min(DataLeft,(size_t)SizeToRead);
+ Raw.Read(Buf+ReadBufPos,CurSizeToRead);
+ ReadBufPos+=CurSizeToRead;
+ SizeToRead-=int(CurSizeToRead);
+ if (SizeToRead>0) // We read the entire buffer and still need more data.
+ {
+ ReadBufPos=0;
+ ReadBufSize=0;
+ if (ReadBuffer()==0)
+ return false;
+ }
+ }
+
+ return SavedCRC==Raw.GetCRC50();
+}
+
+
+// Read next cached header.
+bool QuickOpen::ReadNext()
+{
+ RawRead Raw(NULL);
+ if (!ReadRaw(Raw)) // Read internal quick open header preceding stored block.
+ return false;
+ uint Flags=(uint)Raw.GetV();
+ uint64 Offset=Raw.GetV();
+ size_t HeaderSize=(size_t)Raw.GetV();
+ if (HeaderSize>MAX_HEADER_SIZE_RAR5)
+ return false;
+ LastReadHeader.Alloc(HeaderSize);
+ Raw.GetB(&LastReadHeader[0],HeaderSize);
+ // Calculate the absolute position as offset from quick open service header.
+ LastReadHeaderPos=QOHeaderPos-Offset;
+ return true;
+}
diff --git a/unrar/unrar/qopen.hpp b/unrar/unrar/qopen.hpp
new file mode 100644
index 0000000..d745cea
--- /dev/null
+++ b/unrar/unrar/qopen.hpp
@@ -0,0 +1,61 @@
+#ifndef _RAR_QOPEN_
+#define _RAR_QOPEN_
+
+struct QuickOpenItem
+{
+ byte *Header;
+ size_t HeaderSize;
+ uint64 ArcPos;
+ QuickOpenItem *Next;
+};
+
+
+class Archive;
+class RawRead;
+
+class QuickOpen
+{
+ private:
+ void Close();
+
+
+ uint ReadBuffer();
+ bool ReadRaw(RawRead &Raw);
+ bool ReadNext();
+
+ Archive *Arc;
+ bool WriteMode;
+
+ QuickOpenItem *ListStart;
+ QuickOpenItem *ListEnd;
+
+ byte *Buf; // Read quick open data here.
+ static const size_t MaxBufSize=0x10000; // Buf size, must be multiple of CRYPT_BLOCK_SIZE.
+ size_t CurBufSize; // Current size of buffered data in write mode.
+#ifndef RAR_NOCRYPT // For shell extension.
+ CryptData Crypt;
+#endif
+
+ bool Loaded;
+ uint64 QOHeaderPos; // Main QO header position.
+ uint64 RawDataStart; // Start of QO data, just after the main header.
+ uint64 RawDataSize; // Size of entire QO data.
+ uint64 RawDataPos; // Current read position in QO data.
+ size_t ReadBufSize; // Size of Buf data currently read from QO.
+ size_t ReadBufPos; // Current read position in Buf data.
+ Array<byte> LastReadHeader;
+ uint64 LastReadHeaderPos;
+ uint64 SeekPos;
+ bool UnsyncSeekPos; // QOpen SeekPos does not match an actual file pointer.
+ public:
+ QuickOpen();
+ ~QuickOpen();
+ void Init(Archive *Arc,bool WriteMode);
+ void Load(uint64 BlockPos);
+ void Unload() { Loaded=false; }
+ bool Read(void *Data,size_t Size,size_t &Result);
+ bool Seek(int64 Offset,int Method);
+ bool Tell(int64 *Pos);
+};
+
+#endif
diff --git a/unrar/unrar/rar.cpp b/unrar/unrar/rar.cpp
index a55fd55..34b4b27 100644
--- a/unrar/unrar/rar.cpp
+++ b/unrar/unrar/rar.cpp
@@ -1,143 +1,106 @@
#include "rar.hpp"
-
-#include "smallfn.cpp"
-
-#ifdef _DJGPP
-extern "C" char **__crt0_glob_function (char *arg) { return 0; }
-extern "C" void __crt0_load_environment_file (char *progname) { }
-#endif
-
-#if !defined(GUI) && !defined(RARDLL)
+#if !defined(RARDLL)
int main(int argc, char *argv[])
{
+
#ifdef _UNIX
setlocale(LC_ALL,"");
#endif
-#if defined(_EMX) && !defined(_DJGPP)
- uni_init(0);
-#endif
-
-#if !defined(_SFX_RTL_) && !defined(_WIN_32)
- setbuf(stdout,NULL);
-#endif
-
-#if !defined(SFX_MODULE) && defined(_EMX)
- EnumConfigPaths(argv[0],-1);
-#endif
-
+ InitConsole();
ErrHandler.SetSignalHandlers(true);
- RARInitData();
-
#ifdef SFX_MODULE
- char ModuleName[NM];
-#ifdef _WIN_32
- GetModuleFileName(NULL,ModuleName,sizeof(ModuleName));
+ wchar ModuleName[NM];
+#ifdef _WIN_ALL
+ GetModuleFileName(NULL,ModuleName,ASIZE(ModuleName));
#else
- strcpy(ModuleName,argv[0]);
+ CharToWide(argv[0],ModuleName,ASIZE(ModuleName));
#endif
#endif
-#ifdef _WIN_32
+#ifdef _WIN_ALL
SetErrorMode(SEM_NOALIGNMENTFAULTEXCEPT|SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
#endif
-#if defined(_WIN_32) && !defined(SFX_MODULE) && !defined(SHELL_EXT)
- bool ShutdownOnClose;
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
+ // Must be initialized, normal initialization can be skipped in case of
+ // exception.
+ POWER_MODE ShutdownOnClose=POWERMODE_KEEP;
#endif
-#ifdef ALLOW_EXCEPTIONS
try
-#endif
{
- CommandData Cmd;
+ CommandData *Cmd=new CommandData;
#ifdef SFX_MODULE
- strcpy(Cmd.Command,"X");
- char *Switch=NULL;
-#ifdef _SFX_RTL_
- char *CmdLine=GetCommandLine();
- if (CmdLine!=NULL && *CmdLine=='\"')
- CmdLine=strchr(CmdLine+1,'\"');
- if (CmdLine!=NULL && (CmdLine=strpbrk(CmdLine," /"))!=NULL)
- {
- while (isspace(*CmdLine))
- CmdLine++;
- Switch=CmdLine;
- }
-#else
- Switch=argc>1 ? argv[1]:NULL;
-#endif
- if (Switch!=NULL && Cmd.IsSwitch(Switch[0]))
+ wcsncpyz(Cmd->Command,L"X",ASIZE(Cmd->Command));
+ char *Switch=argc>1 ? argv[1]:NULL;
+ if (Switch!=NULL && Cmd->IsSwitch(Switch[0]))
{
int UpperCmd=etoupper(Switch[1]);
switch(UpperCmd)
{
case 'T':
case 'V':
- Cmd.Command[0]=UpperCmd;
+ Cmd->Command[0]=UpperCmd;
break;
case '?':
- Cmd.OutHelp();
+ Cmd->OutHelp(RARX_SUCCESS);
break;
}
}
- Cmd.AddArcName(ModuleName,NULL);
-#else
- if (Cmd.IsConfigEnabled(argc,argv))
+ Cmd->AddArcName(ModuleName);
+ Cmd->ParseDone();
+ Cmd->AbsoluteLinks=true; // If users runs SFX, he trusts an archive source.
+#else // !SFX_MODULE
+ Cmd->ParseCommandLine(true,argc,argv);
+ if (!Cmd->ConfigDisabled)
{
- Cmd.ReadConfig(argc,argv);
- Cmd.ParseEnvVar();
+ Cmd->ReadConfig();
+ Cmd->ParseEnvVar();
}
- for (int I=1;I<argc;I++)
- Cmd.ParseArg(argv[I],NULL);
+ Cmd->ParseCommandLine(false,argc,argv);
#endif
- Cmd.ParseDone();
-#if defined(_WIN_32) && !defined(SFX_MODULE) && !defined(SHELL_EXT)
- ShutdownOnClose=Cmd.Shutdown;
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
+ ShutdownOnClose=Cmd->Shutdown;
+ if (ShutdownOnClose)
+ ShutdownCheckAnother(true);
#endif
- InitConsoleOptions(Cmd.MsgStream,Cmd.Sound);
- InitLogOptions(Cmd.LogName);
- ErrHandler.SetSilent(Cmd.AllYes || Cmd.MsgStream==MSG_NULL);
- ErrHandler.SetShutdown(Cmd.Shutdown);
+ uiInit(Cmd->Sound);
+ InitLogOptions(Cmd->LogName,Cmd->ErrlogCharset);
+ ErrHandler.SetSilent(Cmd->AllYes || Cmd->MsgStream==MSG_NULL);
- Cmd.OutTitle();
- Cmd.ProcessCommand();
+ Cmd->OutTitle();
+ Cmd->ProcessCommand();
+ delete Cmd;
}
-#ifdef ALLOW_EXCEPTIONS
- catch (int ErrCode)
+ catch (RAR_EXIT ErrCode)
{
ErrHandler.SetErrorCode(ErrCode);
}
-#ifdef ENABLE_BAD_ALLOC
- catch (bad_alloc)
+ catch (std::bad_alloc&)
{
- ErrHandler.SetErrorCode(MEMORY_ERROR);
+ ErrHandler.MemoryErrorMsg();
+ ErrHandler.SetErrorCode(RARX_MEMORY);
}
-#endif
catch (...)
{
- ErrHandler.SetErrorCode(FATAL_ERROR);
+ ErrHandler.SetErrorCode(RARX_FATAL);
}
+
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
+ if (ShutdownOnClose!=POWERMODE_KEEP && ErrHandler.IsShutdownEnabled() &&
+ !ShutdownCheckAnother(false))
+ Shutdown(ShutdownOnClose);
#endif
- File::RemoveCreated();
-#if defined(SFX_MODULE) && defined(_DJGPP)
- _chmod(ModuleName,1,0x20);
-#endif
-#if defined(_EMX) && !defined(_DJGPP)
- uni_done();
-#endif
-#if defined(_WIN_32) && !defined(SFX_MODULE) && !defined(SHELL_EXT)
- if (ShutdownOnClose)
- Shutdown();
-#endif
- return(ErrHandler.GetErrorCode());
+ ErrHandler.MainExit=true;
+ return ErrHandler.GetErrorCode();
}
#endif
diff --git a/unrar/unrar/rar.hpp b/unrar/unrar/rar.hpp
index 229ee09..67edb67 100644
--- a/unrar/unrar/rar.hpp
+++ b/unrar/unrar/rar.hpp
@@ -2,63 +2,70 @@
#define _RAR_RARCOMMON_
#include "raros.hpp"
+#include "rartypes.hpp"
#include "os.hpp"
-
#ifdef RARDLL
#include "dll.hpp"
#endif
-#ifndef _WIN_CE
#include "version.hpp"
-#endif
-#include "rartypes.hpp"
#include "rardefs.hpp"
#include "rarlang.hpp"
-#include "int64.hpp"
+#include "rawint.hpp"
#include "unicode.hpp"
#include "errhnd.hpp"
+#include "secpassword.hpp"
#include "array.hpp"
+#include "strlist.hpp"
#include "timefn.hpp"
+#include "sha1.hpp"
+#include "sha256.hpp"
+#include "blake2s.hpp"
+#include "hash.hpp"
#include "options.hpp"
+#include "rijndael.hpp"
+#include "crypt.hpp"
+#include "headers5.hpp"
#include "headers.hpp"
-#include "rarfn.hpp"
#include "pathfn.hpp"
#include "strfn.hpp"
-#include "strlist.hpp"
+#ifdef _WIN_ALL
+#include "isnt.hpp"
+#endif
#include "file.hpp"
-#include "sha1.hpp"
#include "crc.hpp"
-#include "rijndael.hpp"
-#include "crypt.hpp"
#include "filefn.hpp"
#include "filestr.hpp"
#include "find.hpp"
#include "scantree.hpp"
-#include "savepos.hpp"
#include "getbits.hpp"
#include "rdwrfn.hpp"
+#ifdef USE_QOPEN
+#include "qopen.hpp"
+#endif
#include "archive.hpp"
#include "match.hpp"
#include "cmddata.hpp"
+#include "ui.hpp"
#include "filcreat.hpp"
#include "consio.hpp"
#include "system.hpp"
-#include "isnt.hpp"
#include "log.hpp"
#include "rawread.hpp"
#include "encname.hpp"
#include "resource.hpp"
#include "compress.hpp"
-
#include "rarvm.hpp"
#include "model.hpp"
+#include "threadpool.hpp"
#include "unpack.hpp"
+
#include "extinfo.hpp"
#include "extract.hpp"
@@ -67,14 +74,23 @@
#include "list.hpp"
-
#include "rs.hpp"
+#include "rs16.hpp"
+
+
+
#include "recvol.hpp"
#include "volume.hpp"
#include "smallfn.hpp"
-#include "ulinks.hpp"
#include "global.hpp"
+#if 0
+#include "benchmark.hpp"
+#endif
+
+
+
+
#endif
diff --git a/unrar/unrar/rardefs.hpp b/unrar/unrar/rardefs.hpp
index 24f97b5..6858d39 100644
--- a/unrar/unrar/rardefs.hpp
+++ b/unrar/unrar/rardefs.hpp
@@ -4,21 +4,32 @@
#define Min(x,y) (((x)<(y)) ? (x):(y))
#define Max(x,y) (((x)>(y)) ? (x):(y))
+// Universal replacement of abs function.
+#define Abs(x) (((x)<0) ? -(x):(x))
+
#define ASIZE(x) (sizeof(x)/sizeof(x[0]))
-#define MAXPASSWORD 128
-#define MAXSFXSIZE 0x80000
+// MAXPASSWORD and MAXPASSWORD_RAR are expected to be multiple of
+// CRYPTPROTECTMEMORY_BLOCK_SIZE (16) for CryptProtectMemory in SecPassword.
+// We allow a larger MAXPASSWORD to unpack archives with lengthy passwords
+// in non-RAR formats in GUI versions. For RAR format we set MAXPASSWORD_RAR
+// to 128 for compatibility and because it is enough for AES-256.
+#define MAXPASSWORD 512
+#define MAXPASSWORD_RAR 128
+
+#define MAXSFXSIZE 0x200000
+
+#define MAXCMTSIZE 0x40000
-#define DefSFXName "default.sfx"
-#define DefSortListName "rarfiles.lst"
+#define DefSFXName L"default.sfx"
+#define DefSortListName L"rarfiles.lst"
-#ifndef FA_RDONLY
- #define FA_RDONLY 0x01
- #define FA_HIDDEN 0x02
- #define FA_SYSTEM 0x04
- #define FA_LABEL 0x08
- #define FA_DIREC 0x10
- #define FA_ARCH 0x20
+
+#ifndef SFX_MODULE
+#define USE_QOPEN
#endif
+// Produce the value, which is equal or larger than 'v' and aligned to 'a'.
+#define ALIGN_VALUE(v,a) (size_t(v) + ( (~size_t(v) + 1) & (a - 1) ) )
+
#endif
diff --git a/unrar/unrar/rarfn.hpp b/unrar/unrar/rarfn.hpp
deleted file mode 100644
index 05ffdbc..0000000
--- a/unrar/unrar/rarfn.hpp
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef _RAR_FN_
-#define _RAR_FN_
-
-void RARInitData();
-
-
-#endif
diff --git a/unrar/unrar/raros.hpp b/unrar/unrar/raros.hpp
index e686798..4f4f2ae 100644
--- a/unrar/unrar/raros.hpp
+++ b/unrar/unrar/raros.hpp
@@ -11,22 +11,17 @@
#endif
#if defined(__WIN32__) || defined(_WIN32)
- #define _WIN_32
-#endif
-
-#ifdef _WIN32_WCE
- #define _WIN_32
- #define _WIN_CE
- #ifdef WM_FILECHANGEINFO
- #define PC2002
+ #define _WIN_ALL // Defined for all Windows platforms, 32 and 64 bit, mobile and desktop.
+ #ifdef _M_X64
+ #define _WIN_64
#else
- #undef PC2002
+ #define _WIN_32
#endif
#endif
-#ifdef __BEOS__
+#if defined(ANDROID) || defined(__ANDROID__)
#define _UNIX
- #define _BEOS
+ #define _ANDROID
#endif
#ifdef __APPLE__
@@ -34,7 +29,7 @@
#define _APPLE
#endif
-#if !defined(_EMX) && !defined(_WIN_32) && !defined(_BEOS) && !defined(_APPLE)
+#if !defined(_EMX) && !defined(_WIN_ALL) && !defined(_BEOS) && !defined(_APPLE)
#define _UNIX
#endif
diff --git a/unrar/unrar/rarpch.cpp b/unrar/unrar/rarpch.cpp
new file mode 100644
index 0000000..c070cf7
--- /dev/null
+++ b/unrar/unrar/rarpch.cpp
@@ -0,0 +1,2 @@
+// We use rarpch.cpp to create precompiled headers for MS Visual C++.
+#include "rar.hpp"
diff --git a/unrar/unrar/rartypes.hpp b/unrar/unrar/rartypes.hpp
index 56fccf6..3d3111b 100644
--- a/unrar/unrar/rartypes.hpp
+++ b/unrar/unrar/rartypes.hpp
@@ -1,21 +1,32 @@
#ifndef _RAR_TYPES_
#define _RAR_TYPES_
-typedef unsigned char byte; //8 bits
-typedef unsigned short ushort; //preferably 16 bits, but can be more
-typedef unsigned int uint; //32 bits or more
+#include <stdint.h>
-typedef unsigned int uint32; //32 bits exactly
-typedef int sint32; //signed 32 bits exactly
-#define PRESENT_INT32
+typedef uint8_t byte; // Unsigned 8 bits.
+typedef uint16_t ushort; // Preferably 16 bits, but can be more.
+typedef unsigned int uint; // 32 bits or more.
+typedef uint32_t uint32; // 32 bits exactly.
+typedef int32_t int32; // Signed 32 bits exactly.
+typedef uint64_t uint64; // 64 bits exactly.
+typedef int64_t int64; // Signed 64 bits exactly.
+typedef wchar_t wchar; // Unicode character
-#if defined(_WIN_32) || defined(__GNUC__) || defined(__sgi) || defined(_AIX) || defined(__sun) || defined(__hpux) || defined(_OSF_SOURCE)
-typedef wchar_t wchar;
-#else
-typedef ushort wchar;
-#endif
+// Get lowest 16 bits.
+#define GET_SHORT16(x) (sizeof(ushort)==2 ? (ushort)(x):((x)&0xffff))
+
+// Make 64 bit integer from two 32 bit.
+#define INT32TO64(high,low) ((((uint64)(high))<<32)+((uint64)low))
+
+// Maximum int64 value.
+#define MAX_INT64 int64(INT32TO64(0x7fffffff,0xffffffff))
-#define SHORT16(x) (sizeof(ushort)==2 ? (ushort)(x):((x)&0xffff))
-#define UINT32(x) (sizeof(uint32)==4 ? (uint32)(x):((x)&0xffffffff))
+// Special int64 value, large enough to never be found in real life
+// and small enough to fit to both signed and unsigned 64-bit ints.
+// We use it in situations, when we need to indicate that parameter
+// is not defined and probably should be calculated inside of function.
+// Lower part is intentionally 0x7fffffff, not 0xffffffff, to make it
+// compatible with 32 bit int64 if 64 bit type is not supported.
+#define INT64NDF INT32TO64(0x7fffffff,0x7fffffff)
#endif
diff --git a/unrar/unrar/rarvm.cpp b/unrar/unrar/rarvm.cpp
index 987e3ae..8d8675a 100644
--- a/unrar/unrar/rarvm.cpp
+++ b/unrar/unrar/rarvm.cpp
@@ -1,7 +1,5 @@
#include "rar.hpp"
-#include "rarvmtbl.cpp"
-
RarVM::RarVM()
{
Mem=NULL;
@@ -20,691 +18,53 @@ void RarVM::Init()
Mem=new byte[VM_MEMSIZE+4];
}
-/*********************************************************************
- IS_VM_MEM macro checks if address belongs to VM memory pool (Mem).
- Only Mem data are always low endian regardless of machine architecture,
- so we need to convert them to native format when reading or writing.
- VM registers have endianness of host machine.
-**********************************************************************/
-#define IS_VM_MEM(a) (((byte*)a)>=Mem && ((byte*)a)<Mem+VM_MEMSIZE)
-
-inline uint RarVM::GetValue(bool ByteMode,uint *Addr)
-{
- if (ByteMode)
- {
-#ifdef BIG_ENDIAN
- if (IS_VM_MEM(Addr))
- return(*(byte *)Addr);
- else
- return(*Addr & 0xff);
-#else
- return(*(byte *)Addr);
-#endif
- }
- else
- {
-#if defined(BIG_ENDIAN) || !defined(ALLOW_NOT_ALIGNED_INT)
- if (IS_VM_MEM(Addr))
- {
- byte *B=(byte *)Addr;
- return UINT32((uint)B[0]|((uint)B[1]<<8)|((uint)B[2]<<16)|((uint)B[3]<<24));
- }
- else
- return UINT32(*Addr);
-#else
- return UINT32(*Addr);
-#endif
- }
-}
-
-#if defined(BIG_ENDIAN) || !defined(ALLOW_NOT_ALIGNED_INT)
- #define GET_VALUE(ByteMode,Addr) GetValue(ByteMode,(uint *)Addr)
-#else
- #define GET_VALUE(ByteMode,Addr) ((ByteMode) ? (*(byte *)(Addr)):UINT32(*(uint *)(Addr)))
-#endif
-
-
-inline void RarVM::SetValue(bool ByteMode,uint *Addr,uint Value)
-{
- if (ByteMode)
- {
-#ifdef BIG_ENDIAN
- if (IS_VM_MEM(Addr))
- *(byte *)Addr=Value;
- else
- *Addr=(*Addr & ~0xff)|(Value & 0xff);
-#else
- *(byte *)Addr=Value;
-#endif
- }
- else
- {
-#if defined(BIG_ENDIAN) || !defined(ALLOW_NOT_ALIGNED_INT) || !defined(PRESENT_INT32)
- if (IS_VM_MEM(Addr))
- {
- ((byte *)Addr)[0]=(byte)Value;
- ((byte *)Addr)[1]=(byte)(Value>>8);
- ((byte *)Addr)[2]=(byte)(Value>>16);
- ((byte *)Addr)[3]=(byte)(Value>>24);
- }
- else
- *(uint *)Addr=Value;
-#else
- *(uint32 *)Addr=Value;
-#endif
- }
-}
-
-#if defined(BIG_ENDIAN) || !defined(ALLOW_NOT_ALIGNED_INT) || !defined(PRESENT_INT32)
- #define SET_VALUE(ByteMode,Addr,Value) SetValue(ByteMode,(uint *)Addr,Value)
-#else
- #define SET_VALUE(ByteMode,Addr,Value) ((ByteMode) ? (*(byte *)(Addr)=(Value)):(*(uint32 *)(Addr)=((uint32)(Value))))
-#endif
-
-
-void RarVM::SetLowEndianValue(uint *Addr,uint Value)
-{
-#if defined(BIG_ENDIAN) || !defined(ALLOW_NOT_ALIGNED_INT) || !defined(PRESENT_INT32)
- ((byte *)Addr)[0]=(byte)Value;
- ((byte *)Addr)[1]=(byte)(Value>>8);
- ((byte *)Addr)[2]=(byte)(Value>>16);
- ((byte *)Addr)[3]=(byte)(Value>>24);
-#else
- *(uint32 *)Addr=Value;
-#endif
-}
-
-
-inline uint* RarVM::GetOperand(VM_PreparedOperand *CmdOp)
-{
- if (CmdOp->Type==VM_OPREGMEM)
- return((uint *)&Mem[(*CmdOp->Addr+CmdOp->Base)&VM_MEMMASK]);
- else
- return(CmdOp->Addr);
-}
-
void RarVM::Execute(VM_PreparedProgram *Prg)
{
memcpy(R,Prg->InitR,sizeof(Prg->InitR));
- unsigned int GlobalSize=Min(Prg->GlobalData.Size(),VM_GLOBALMEMSIZE);
- if (GlobalSize)
- memcpy(Mem+VM_GLOBALMEMADDR,&Prg->GlobalData[0],GlobalSize);
- unsigned int StaticSize=Min(Prg->StaticData.Size(),VM_GLOBALMEMSIZE-GlobalSize);
- if (StaticSize)
- memcpy(Mem+VM_GLOBALMEMADDR+GlobalSize,&Prg->StaticData[0],StaticSize);
-
- R[7]=VM_MEMSIZE;
- Flags=0;
-
- VM_PreparedCommand *PreparedCode=Prg->AltCmd ? Prg->AltCmd:&Prg->Cmd[0];
- if (!ExecuteCode(PreparedCode,Prg->CmdCount))
- PreparedCode[0].OpCode=VM_RET;
- uint NewBlockPos=GET_VALUE(false,&Mem[VM_GLOBALMEMADDR+0x20])&VM_MEMMASK;
- uint NewBlockSize=GET_VALUE(false,&Mem[VM_GLOBALMEMADDR+0x1c])&VM_MEMMASK;
- if (NewBlockPos+NewBlockSize>=VM_MEMSIZE)
- NewBlockPos=NewBlockSize=0;
- Prg->FilteredData=Mem+NewBlockPos;
- Prg->FilteredDataSize=NewBlockSize;
-
- Prg->GlobalData.Reset();
-
- uint DataSize=Min(GET_VALUE(false,(uint*)&Mem[VM_GLOBALMEMADDR+0x30]),VM_GLOBALMEMSIZE-VM_FIXEDGLOBALSIZE);
- if (DataSize!=0)
+ Prg->FilteredData=NULL;
+ if (Prg->Type!=VMSF_NONE)
{
- Prg->GlobalData.Add(DataSize+VM_FIXEDGLOBALSIZE);
- memcpy(&Prg->GlobalData[0],&Mem[VM_GLOBALMEMADDR],DataSize+VM_FIXEDGLOBALSIZE);
- }
-}
-
-
-/*
-Note:
- Due to performance considerations RAR VM may set VM_FS, VM_FC, VM_FZ
- incorrectly for byte operands. These flags are always valid only
- for 32-bit operands. Check implementation of concrete VM command
- to see if it sets flags right.
-*/
-
-#define SET_IP(IP) \
- if ((IP)>=CodeSize) \
- return(true); \
- if (--MaxOpCount<=0) \
- return(false); \
- Cmd=PreparedCode+(IP);
-
-bool RarVM::ExecuteCode(VM_PreparedCommand *PreparedCode,int CodeSize)
-{
- int MaxOpCount=25000000;
- VM_PreparedCommand *Cmd=PreparedCode;
- while (1)
- {
-#ifndef NORARVM
- uint *Op1=GetOperand(&Cmd->Op1);
- uint *Op2=GetOperand(&Cmd->Op2);
-#endif
- switch(Cmd->OpCode)
- {
-#ifndef NORARVM
- case VM_MOV:
- SET_VALUE(Cmd->ByteMode,Op1,GET_VALUE(Cmd->ByteMode,Op2));
- break;
-#ifdef VM_OPTIMIZE
- case VM_MOVB:
- SET_VALUE(true,Op1,GET_VALUE(true,Op2));
- break;
- case VM_MOVD:
- SET_VALUE(false,Op1,GET_VALUE(false,Op2));
- break;
-#endif
- case VM_CMP:
- {
- uint Value1=GET_VALUE(Cmd->ByteMode,Op1);
- uint Result=UINT32(Value1-GET_VALUE(Cmd->ByteMode,Op2));
- Flags=Result==0 ? VM_FZ:(Result>Value1)|(Result&VM_FS);
- }
- break;
-#ifdef VM_OPTIMIZE
- case VM_CMPB:
- {
- uint Value1=GET_VALUE(true,Op1);
- uint Result=UINT32(Value1-GET_VALUE(true,Op2));
- Flags=Result==0 ? VM_FZ:(Result>Value1)|(Result&VM_FS);
- }
- break;
- case VM_CMPD:
- {
- uint Value1=GET_VALUE(false,Op1);
- uint Result=UINT32(Value1-GET_VALUE(false,Op2));
- Flags=Result==0 ? VM_FZ:(Result>Value1)|(Result&VM_FS);
- }
- break;
-#endif
- case VM_ADD:
- {
- uint Value1=GET_VALUE(Cmd->ByteMode,Op1);
- uint Result=UINT32(Value1+GET_VALUE(Cmd->ByteMode,Op2));
- if (Cmd->ByteMode)
- {
- Result&=0xff;
- Flags=(Result<Value1)|(Result==0 ? VM_FZ:((Result&0x80) ? VM_FS:0));
- }
- else
- Flags=(Result<Value1)|(Result==0 ? VM_FZ:(Result&VM_FS));
- SET_VALUE(Cmd->ByteMode,Op1,Result);
- }
- break;
-#ifdef VM_OPTIMIZE
- case VM_ADDB:
- SET_VALUE(true,Op1,GET_VALUE(true,Op1)+GET_VALUE(true,Op2));
- break;
- case VM_ADDD:
- SET_VALUE(false,Op1,GET_VALUE(false,Op1)+GET_VALUE(false,Op2));
- break;
-#endif
- case VM_SUB:
- {
- uint Value1=GET_VALUE(Cmd->ByteMode,Op1);
- uint Result=UINT32(Value1-GET_VALUE(Cmd->ByteMode,Op2));
- Flags=Result==0 ? VM_FZ:(Result>Value1)|(Result&VM_FS);
- SET_VALUE(Cmd->ByteMode,Op1,Result);
- }
- break;
-#ifdef VM_OPTIMIZE
- case VM_SUBB:
- SET_VALUE(true,Op1,GET_VALUE(true,Op1)-GET_VALUE(true,Op2));
- break;
- case VM_SUBD:
- SET_VALUE(false,Op1,GET_VALUE(false,Op1)-GET_VALUE(false,Op2));
- break;
-#endif
- case VM_JZ:
- if ((Flags & VM_FZ)!=0)
- {
- SET_IP(GET_VALUE(false,Op1));
- continue;
- }
- break;
- case VM_JNZ:
- if ((Flags & VM_FZ)==0)
- {
- SET_IP(GET_VALUE(false,Op1));
- continue;
- }
- break;
- case VM_INC:
- {
- uint Result=UINT32(GET_VALUE(Cmd->ByteMode,Op1)+1);
- if (Cmd->ByteMode)
- Result&=0xff;
- SET_VALUE(Cmd->ByteMode,Op1,Result);
- Flags=Result==0 ? VM_FZ:Result&VM_FS;
- }
- break;
-#ifdef VM_OPTIMIZE
- case VM_INCB:
- SET_VALUE(true,Op1,GET_VALUE(true,Op1)+1);
- break;
- case VM_INCD:
- SET_VALUE(false,Op1,GET_VALUE(false,Op1)+1);
- break;
-#endif
- case VM_DEC:
- {
- uint Result=UINT32(GET_VALUE(Cmd->ByteMode,Op1)-1);
- SET_VALUE(Cmd->ByteMode,Op1,Result);
- Flags=Result==0 ? VM_FZ:Result&VM_FS;
- }
- break;
-#ifdef VM_OPTIMIZE
- case VM_DECB:
- SET_VALUE(true,Op1,GET_VALUE(true,Op1)-1);
- break;
- case VM_DECD:
- SET_VALUE(false,Op1,GET_VALUE(false,Op1)-1);
- break;
-#endif
- case VM_JMP:
- SET_IP(GET_VALUE(false,Op1));
- continue;
- case VM_XOR:
- {
- uint Result=UINT32(GET_VALUE(Cmd->ByteMode,Op1)^GET_VALUE(Cmd->ByteMode,Op2));
- Flags=Result==0 ? VM_FZ:Result&VM_FS;
- SET_VALUE(Cmd->ByteMode,Op1,Result);
- }
- break;
- case VM_AND:
- {
- uint Result=UINT32(GET_VALUE(Cmd->ByteMode,Op1)&GET_VALUE(Cmd->ByteMode,Op2));
- Flags=Result==0 ? VM_FZ:Result&VM_FS;
- SET_VALUE(Cmd->ByteMode,Op1,Result);
- }
- break;
- case VM_OR:
- {
- uint Result=UINT32(GET_VALUE(Cmd->ByteMode,Op1)|GET_VALUE(Cmd->ByteMode,Op2));
- Flags=Result==0 ? VM_FZ:Result&VM_FS;
- SET_VALUE(Cmd->ByteMode,Op1,Result);
- }
- break;
- case VM_TEST:
- {
- uint Result=UINT32(GET_VALUE(Cmd->ByteMode,Op1)&GET_VALUE(Cmd->ByteMode,Op2));
- Flags=Result==0 ? VM_FZ:Result&VM_FS;
- }
- break;
- case VM_JS:
- if ((Flags & VM_FS)!=0)
- {
- SET_IP(GET_VALUE(false,Op1));
- continue;
- }
- break;
- case VM_JNS:
- if ((Flags & VM_FS)==0)
- {
- SET_IP(GET_VALUE(false,Op1));
- continue;
- }
- break;
- case VM_JB:
- if ((Flags & VM_FC)!=0)
- {
- SET_IP(GET_VALUE(false,Op1));
- continue;
- }
- break;
- case VM_JBE:
- if ((Flags & (VM_FC|VM_FZ))!=0)
- {
- SET_IP(GET_VALUE(false,Op1));
- continue;
- }
- break;
- case VM_JA:
- if ((Flags & (VM_FC|VM_FZ))==0)
- {
- SET_IP(GET_VALUE(false,Op1));
- continue;
- }
- break;
- case VM_JAE:
- if ((Flags & VM_FC)==0)
- {
- SET_IP(GET_VALUE(false,Op1));
- continue;
- }
- break;
- case VM_PUSH:
- R[7]-=4;
- SET_VALUE(false,(uint *)&Mem[R[7]&VM_MEMMASK],GET_VALUE(false,Op1));
- break;
- case VM_POP:
- SET_VALUE(false,Op1,GET_VALUE(false,(uint *)&Mem[R[7] & VM_MEMMASK]));
- R[7]+=4;
- break;
- case VM_CALL:
- R[7]-=4;
- SET_VALUE(false,(uint *)&Mem[R[7]&VM_MEMMASK],Cmd-PreparedCode+1);
- SET_IP(GET_VALUE(false,Op1));
- continue;
- case VM_NOT:
- SET_VALUE(Cmd->ByteMode,Op1,~GET_VALUE(Cmd->ByteMode,Op1));
- break;
- case VM_SHL:
- {
- uint Value1=GET_VALUE(Cmd->ByteMode,Op1);
- uint Value2=GET_VALUE(Cmd->ByteMode,Op2);
- uint Result=UINT32(Value1<<Value2);
- Flags=(Result==0 ? VM_FZ:(Result&VM_FS))|((Value1<<(Value2-1))&0x80000000 ? VM_FC:0);
- SET_VALUE(Cmd->ByteMode,Op1,Result);
- }
- break;
- case VM_SHR:
- {
- uint Value1=GET_VALUE(Cmd->ByteMode,Op1);
- uint Value2=GET_VALUE(Cmd->ByteMode,Op2);
- uint Result=UINT32(Value1>>Value2);
- Flags=(Result==0 ? VM_FZ:(Result&VM_FS))|((Value1>>(Value2-1))&VM_FC);
- SET_VALUE(Cmd->ByteMode,Op1,Result);
- }
- break;
- case VM_SAR:
- {
- uint Value1=GET_VALUE(Cmd->ByteMode,Op1);
- uint Value2=GET_VALUE(Cmd->ByteMode,Op2);
- uint Result=UINT32(((int)Value1)>>Value2);
- Flags=(Result==0 ? VM_FZ:(Result&VM_FS))|((Value1>>(Value2-1))&VM_FC);
- SET_VALUE(Cmd->ByteMode,Op1,Result);
- }
- break;
- case VM_NEG:
- {
- uint Result=UINT32(-GET_VALUE(Cmd->ByteMode,Op1));
- Flags=Result==0 ? VM_FZ:VM_FC|(Result&VM_FS);
- SET_VALUE(Cmd->ByteMode,Op1,Result);
- }
- break;
-#ifdef VM_OPTIMIZE
- case VM_NEGB:
- SET_VALUE(true,Op1,-GET_VALUE(true,Op1));
- break;
- case VM_NEGD:
- SET_VALUE(false,Op1,-GET_VALUE(false,Op1));
- break;
-#endif
- case VM_PUSHA:
- {
- const int RegCount=sizeof(R)/sizeof(R[0]);
- for (int I=0,SP=R[7]-4;I<RegCount;I++,SP-=4)
- SET_VALUE(false,(uint *)&Mem[SP & VM_MEMMASK],R[I]);
- R[7]-=RegCount*4;
- }
- break;
- case VM_POPA:
- {
- const int RegCount=sizeof(R)/sizeof(R[0]);
- for (uint I=0,SP=R[7];I<RegCount;I++,SP+=4)
- R[7-I]=GET_VALUE(false,(uint *)&Mem[SP & VM_MEMMASK]);
- }
- break;
- case VM_PUSHF:
- R[7]-=4;
- SET_VALUE(false,(uint *)&Mem[R[7]&VM_MEMMASK],Flags);
- break;
- case VM_POPF:
- Flags=GET_VALUE(false,(uint *)&Mem[R[7] & VM_MEMMASK]);
- R[7]+=4;
- break;
- case VM_MOVZX:
- SET_VALUE(false,Op1,GET_VALUE(true,Op2));
- break;
- case VM_MOVSX:
- SET_VALUE(false,Op1,(signed char)GET_VALUE(true,Op2));
- break;
- case VM_XCHG:
- {
- uint Value1=GET_VALUE(Cmd->ByteMode,Op1);
- SET_VALUE(Cmd->ByteMode,Op1,GET_VALUE(Cmd->ByteMode,Op2));
- SET_VALUE(Cmd->ByteMode,Op2,Value1);
- }
- break;
- case VM_MUL:
- {
- uint Result=GET_VALUE(Cmd->ByteMode,Op1)*GET_VALUE(Cmd->ByteMode,Op2);
- SET_VALUE(Cmd->ByteMode,Op1,Result);
- }
- break;
- case VM_DIV:
- {
- uint Divider=GET_VALUE(Cmd->ByteMode,Op2);
- if (Divider!=0)
- {
- uint Result=GET_VALUE(Cmd->ByteMode,Op1)/Divider;
- SET_VALUE(Cmd->ByteMode,Op1,Result);
- }
- }
- break;
- case VM_ADC:
- {
- uint Value1=GET_VALUE(Cmd->ByteMode,Op1);
- uint FC=(Flags&VM_FC);
- uint Result=UINT32(Value1+GET_VALUE(Cmd->ByteMode,Op2)+FC);
- if (Cmd->ByteMode)
- Result&=0xff;
- Flags=(Result<Value1 || Result==Value1 && FC)|(Result==0 ? VM_FZ:(Result&VM_FS));
- SET_VALUE(Cmd->ByteMode,Op1,Result);
- }
- break;
- case VM_SBB:
- {
- uint Value1=GET_VALUE(Cmd->ByteMode,Op1);
- uint FC=(Flags&VM_FC);
- uint Result=UINT32(Value1-GET_VALUE(Cmd->ByteMode,Op2)-FC);
- if (Cmd->ByteMode)
- Result&=0xff;
- Flags=(Result>Value1 || Result==Value1 && FC)|(Result==0 ? VM_FZ:(Result&VM_FS));
- SET_VALUE(Cmd->ByteMode,Op1,Result);
- }
- break;
-#endif // for #ifndef NORARVM
- case VM_RET:
- if (R[7]>=VM_MEMSIZE)
- return(true);
- SET_IP(GET_VALUE(false,(uint *)&Mem[R[7] & VM_MEMMASK]));
- R[7]+=4;
- continue;
-#ifdef VM_STANDARDFILTERS
- case VM_STANDARD:
- ExecuteStandardFilter((VM_StandardFilters)Cmd->Op1.Data);
- break;
-#endif
- case VM_PRINT:
- break;
- }
- Cmd++;
- --MaxOpCount;
+ bool Success=ExecuteStandardFilter(Prg->Type);
+ uint BlockSize=Prg->InitR[4] & VM_MEMMASK;
+ Prg->FilteredDataSize=BlockSize;
+ if (Prg->Type==VMSF_DELTA || Prg->Type==VMSF_RGB || Prg->Type==VMSF_AUDIO)
+ Prg->FilteredData=2*BlockSize>VM_MEMSIZE || !Success ? Mem:Mem+BlockSize;
+ else
+ Prg->FilteredData=Mem;
}
}
-
-
-void RarVM::Prepare(byte *Code,int CodeSize,VM_PreparedProgram *Prg)
+void RarVM::Prepare(byte *Code,uint CodeSize,VM_PreparedProgram *Prg)
{
- InitBitInput();
- memcpy(InBuf,Code,Min(CodeSize,BitInput::MAX_SIZE));
-
+ // Calculate the single byte XOR checksum to check validity of VM code.
byte XorSum=0;
- for (int I=1;I<CodeSize;I++)
+ for (uint I=1;I<CodeSize;I++)
XorSum^=Code[I];
- faddbits(8);
+ if (XorSum!=Code[0])
+ return;
- Prg->CmdCount=0;
- if (XorSum==Code[0])
+ struct StandardFilters
{
-#ifdef VM_STANDARDFILTERS
- VM_StandardFilters FilterType=IsStandardFilter(Code,CodeSize);
- if (FilterType!=VMSF_NONE)
- {
- Prg->Cmd.Add(1);
- VM_PreparedCommand *CurCmd=&Prg->Cmd[Prg->CmdCount++];
- CurCmd->OpCode=VM_STANDARD;
- CurCmd->Op1.Data=FilterType;
- CurCmd->Op1.Addr=&CurCmd->Op1.Data;
- CurCmd->Op2.Addr=&CurCmd->Op2.Data;
- CurCmd->Op1.Type=CurCmd->Op2.Type=VM_OPNONE;
- CodeSize=0;
- }
-#endif
- uint DataFlag=fgetbits();
- faddbits(1);
-
-/* Read static data contained in DB operators. This data cannot be changed,
- it is a part of VM code, not a filter parameter.
-*/
- if (DataFlag&0x8000)
- {
- int DataSize=ReadData(*this)+1;
- for (int I=0;InAddr<CodeSize && I<DataSize;I++)
- {
- Prg->StaticData.Add(1);
- Prg->StaticData[I]=fgetbits()>>8;
- faddbits(8);
- }
- }
-
- while (InAddr<CodeSize)
- {
- Prg->Cmd.Add(1);
- VM_PreparedCommand *CurCmd=&Prg->Cmd[Prg->CmdCount];
- uint Data=fgetbits();
- if ((Data&0x8000)==0)
- {
- CurCmd->OpCode=(VM_Commands)(Data>>12);
- faddbits(4);
- }
- else
- {
- CurCmd->OpCode=(VM_Commands)((Data>>10)-24);
- faddbits(6);
- }
- if (VM_CmdFlags[CurCmd->OpCode] & VMCF_BYTEMODE)
- {
- CurCmd->ByteMode=fgetbits()>>15;
- faddbits(1);
- }
- else
- CurCmd->ByteMode=0;
- CurCmd->Op1.Type=CurCmd->Op2.Type=VM_OPNONE;
- int OpNum=(VM_CmdFlags[CurCmd->OpCode] & VMCF_OPMASK);
- CurCmd->Op1.Addr=CurCmd->Op2.Addr=NULL;
- if (OpNum>0)
- {
- DecodeArg(CurCmd->Op1,CurCmd->ByteMode);
- if (OpNum==2)
- DecodeArg(CurCmd->Op2,CurCmd->ByteMode);
- else
- {
- if (CurCmd->Op1.Type==VM_OPINT && (VM_CmdFlags[CurCmd->OpCode]&(VMCF_JUMP|VMCF_PROC)))
- {
- int Distance=CurCmd->Op1.Data;
- if (Distance>=256)
- Distance-=256;
- else
- {
- if (Distance>=136)
- Distance-=264;
- else
- if (Distance>=16)
- Distance-=8;
- else
- if (Distance>=8)
- Distance-=16;
- Distance+=Prg->CmdCount;
- }
- CurCmd->Op1.Data=Distance;
- }
- }
- }
- Prg->CmdCount++;
- }
- }
- Prg->Cmd.Add(1);
- VM_PreparedCommand *CurCmd=&Prg->Cmd[Prg->CmdCount++];
- CurCmd->OpCode=VM_RET;
- CurCmd->Op1.Addr=&CurCmd->Op1.Data;
- CurCmd->Op2.Addr=&CurCmd->Op2.Data;
- CurCmd->Op1.Type=CurCmd->Op2.Type=VM_OPNONE;
-
- for (int I=0;I<Prg->CmdCount;I++)
- {
- VM_PreparedCommand *Cmd=&Prg->Cmd[I];
- if (Cmd->Op1.Addr==NULL)
- Cmd->Op1.Addr=&Cmd->Op1.Data;
- if (Cmd->Op2.Addr==NULL)
- Cmd->Op2.Addr=&Cmd->Op2.Data;
- }
-
-#ifdef VM_OPTIMIZE
- if (CodeSize!=0)
- Optimize(Prg);
-#endif
-}
-
-
-void RarVM::DecodeArg(VM_PreparedOperand &Op,bool ByteMode)
-{
- uint Data=fgetbits();
- if (Data & 0x8000)
- {
- Op.Type=VM_OPREG;
- Op.Data=(Data>>12)&7;
- Op.Addr=&R[Op.Data];
- faddbits(4);
- }
- else
- if ((Data & 0xc000)==0)
- {
- Op.Type=VM_OPINT;
- if (ByteMode)
- {
- Op.Data=(Data>>6) & 0xff;
- faddbits(10);
- }
- else
- {
- faddbits(2);
- Op.Data=ReadData(*this);
- }
- }
- else
+ uint Length;
+ uint CRC;
+ VM_StandardFilters Type;
+ } static StdList[]={
+ 53, 0xad576887, VMSF_E8,
+ 57, 0x3cd7e57e, VMSF_E8E9,
+ 120, 0x3769893f, VMSF_ITANIUM,
+ 29, 0x0e06077d, VMSF_DELTA,
+ 149, 0x1c2c5dc8, VMSF_RGB,
+ 216, 0xbc85e701, VMSF_AUDIO
+ };
+ uint CodeCRC=CRC32(0xffffffff,Code,CodeSize)^0xffffffff;
+ for (uint I=0;I<ASIZE(StdList);I++)
+ if (StdList[I].CRC==CodeCRC && StdList[I].Length==CodeSize)
{
- Op.Type=VM_OPREGMEM;
- if ((Data & 0x2000)==0)
- {
- Op.Data=(Data>>10)&7;
- Op.Addr=&R[Op.Data];
- Op.Base=0;
- faddbits(6);
- }
- else
- {
- if ((Data & 0x1000)==0)
- {
- Op.Data=(Data>>9)&7;
- Op.Addr=&R[Op.Data];
- faddbits(7);
- }
- else
- {
- Op.Data=0;
- faddbits(4);
- }
- Op.Base=ReadData(*this);
- }
+ Prg->Type=StdList[I].Type;
+ break;
}
}
@@ -716,7 +76,7 @@ uint RarVM::ReadData(BitInput &Inp)
{
case 0:
Inp.faddbits(6);
- return((Data>>10)&0xf);
+ return (Data>>10)&0xf;
case 0x4000:
if ((Data&0x3c00)==0)
{
@@ -728,113 +88,38 @@ uint RarVM::ReadData(BitInput &Inp)
Data=(Data>>6)&0xff;
Inp.faddbits(10);
}
- return(Data);
+ return Data;
case 0x8000:
Inp.faddbits(2);
Data=Inp.fgetbits();
Inp.faddbits(16);
- return(Data);
+ return Data;
default:
Inp.faddbits(2);
Data=(Inp.fgetbits()<<16);
Inp.faddbits(16);
Data|=Inp.fgetbits();
Inp.faddbits(16);
- return(Data);
+ return Data;
}
}
-void RarVM::SetMemory(unsigned int Pos,byte *Data,unsigned int DataSize)
+void RarVM::SetMemory(size_t Pos,byte *Data,size_t DataSize)
{
if (Pos<VM_MEMSIZE && Data!=Mem+Pos)
- memmove(Mem+Pos,Data,Min(DataSize,VM_MEMSIZE-Pos));
-}
-
-
-#ifdef VM_OPTIMIZE
-void RarVM::Optimize(VM_PreparedProgram *Prg)
-{
- VM_PreparedCommand *Code=&Prg->Cmd[0];
- int CodeSize=Prg->CmdCount;
-
- for (int I=0;I<CodeSize;I++)
{
- VM_PreparedCommand *Cmd=Code+I;
- switch(Cmd->OpCode)
- {
- case VM_MOV:
- Cmd->OpCode=Cmd->ByteMode ? VM_MOVB:VM_MOVD;
- continue;
- case VM_CMP:
- Cmd->OpCode=Cmd->ByteMode ? VM_CMPB:VM_CMPD;
- continue;
- }
- if ((VM_CmdFlags[Cmd->OpCode] & VMCF_CHFLAGS)==0)
- continue;
- bool FlagsRequired=false;
- for (int J=I+1;J<CodeSize;J++)
- {
- int Flags=VM_CmdFlags[Code[J].OpCode];
- if (Flags & (VMCF_JUMP|VMCF_PROC|VMCF_USEFLAGS))
- {
- FlagsRequired=true;
- break;
- }
- if (Flags & VMCF_CHFLAGS)
- break;
- }
- if (FlagsRequired)
- continue;
- switch(Cmd->OpCode)
- {
- case VM_ADD:
- Cmd->OpCode=Cmd->ByteMode ? VM_ADDB:VM_ADDD;
- continue;
- case VM_SUB:
- Cmd->OpCode=Cmd->ByteMode ? VM_SUBB:VM_SUBD;
- continue;
- case VM_INC:
- Cmd->OpCode=Cmd->ByteMode ? VM_INCB:VM_INCD;
- continue;
- case VM_DEC:
- Cmd->OpCode=Cmd->ByteMode ? VM_DECB:VM_DECD;
- continue;
- case VM_NEG:
- Cmd->OpCode=Cmd->ByteMode ? VM_NEGB:VM_NEGD;
- continue;
- }
+ // We can have NULL Data for invalid filters with DataSize==0. While most
+ // sensible memmove implementations do not care about data if size is 0,
+ // let's follow the standard and check the size first.
+ size_t CopySize=Min(DataSize,VM_MEMSIZE-Pos);
+ if (CopySize!=0)
+ memmove(Mem+Pos,Data,CopySize);
}
}
-#endif
-#ifdef VM_STANDARDFILTERS
-VM_StandardFilters RarVM::IsStandardFilter(byte *Code,int CodeSize)
-{
- struct StandardFilterSignature
- {
- int Length;
- uint CRC;
- VM_StandardFilters Type;
- } StdList[]={
- 53, 0xad576887, VMSF_E8,
- 57, 0x3cd7e57e, VMSF_E8E9,
- 120, 0x3769893f, VMSF_ITANIUM,
- 29, 0x0e06077d, VMSF_DELTA,
- 149, 0x1c2c5dc8, VMSF_RGB,
- 216, 0xbc85e701, VMSF_AUDIO,
- 40, 0x46b9c560, VMSF_UPCASE
- };
- uint CodeCRC=CRC(0xffffffff,Code,CodeSize)^0xffffffff;
- for (int I=0;I<sizeof(StdList)/sizeof(StdList[0]);I++)
- if (StdList[I].CRC==CodeCRC && StdList[I].Length==CodeSize)
- return(StdList[I].Type);
- return(VMSF_NONE);
-}
-
-
-void RarVM::ExecuteStandardFilter(VM_StandardFilters FilterType)
+bool RarVM::ExecuteStandardFilter(VM_StandardFilters FilterType)
{
switch(FilterType)
{
@@ -842,43 +127,32 @@ void RarVM::ExecuteStandardFilter(VM_StandardFilters FilterType)
case VMSF_E8E9:
{
byte *Data=Mem;
- int DataSize=R[4];
- uint FileOffset=R[6];
+ uint DataSize=R[4],FileOffset=R[6];
- if (DataSize>=VM_GLOBALMEMADDR || DataSize<4)
- break;
+ if (DataSize>VM_MEMSIZE || DataSize<4)
+ return false;
- const int FileSize=0x1000000;
+ const uint FileSize=0x1000000;
byte CmpByte2=FilterType==VMSF_E8E9 ? 0xe9:0xe8;
- for (int CurPos=0;CurPos<DataSize-4;)
+ for (uint CurPos=0;CurPos<DataSize-4;)
{
byte CurByte=*(Data++);
CurPos++;
if (CurByte==0xe8 || CurByte==CmpByte2)
{
-#ifdef PRESENT_INT32
- sint32 Offset=CurPos+FileOffset;
- sint32 Addr=GET_VALUE(false,Data);
- if (Addr<0)
+ uint Offset=CurPos+FileOffset;
+ uint Addr=RawGet4(Data);
+
+ // We check 0x80000000 bit instead of '< 0' comparison
+ // not assuming int32 presence or uint size and endianness.
+ if ((Addr & 0x80000000)!=0) // Addr<0
{
- if (Addr+Offset>=0)
- SET_VALUE(false,Data,Addr+FileSize);
+ if (((Addr+Offset) & 0x80000000)==0) // Addr+Offset>=0
+ RawPut4(Addr+FileSize,Data);
}
else
- if (Addr<FileSize)
- SET_VALUE(false,Data,Addr-Offset);
-#else
- long Offset=CurPos+FileOffset;
- long Addr=GET_VALUE(false,Data);
- if ((Addr & 0x80000000)!=0)
- {
- if (((Addr+Offset) & 0x80000000)==0)
- SET_VALUE(false,Data,Addr+FileSize);
- }
- else
- if (((Addr-FileSize) & 0x80000000)!=0)
- SET_VALUE(false,Data,Addr-Offset);
-#endif
+ if (((Addr-FileSize) & 0x80000000)!=0) // Addr<FileSize
+ RawPut4(Addr-Offset,Data);
Data+=4;
CurPos+=4;
}
@@ -888,13 +162,12 @@ void RarVM::ExecuteStandardFilter(VM_StandardFilters FilterType)
case VMSF_ITANIUM:
{
byte *Data=Mem;
- int DataSize=R[4];
- uint FileOffset=R[6];
+ uint DataSize=R[4],FileOffset=R[6];
- if (DataSize>=VM_GLOBALMEMADDR || DataSize<21)
- break;
+ if (DataSize>VM_MEMSIZE || DataSize<21)
+ return false;
- int CurPos=0;
+ uint CurPos=0;
FileOffset>>=4;
@@ -906,14 +179,14 @@ void RarVM::ExecuteStandardFilter(VM_StandardFilters FilterType)
static byte Masks[16]={4,4,6,6,0,0,7,7,4,4,0,0,4,4,0,0};
byte CmdMask=Masks[Byte];
if (CmdMask!=0)
- for (int I=0;I<=2;I++)
+ for (uint I=0;I<=2;I++)
if (CmdMask & (1<<I))
{
- int StartPos=I*41+5;
- int OpType=FilterItanium_GetBits(Data,StartPos+37,4);
+ uint StartPos=I*41+5;
+ uint OpType=FilterItanium_GetBits(Data,StartPos+37,4);
if (OpType==5)
{
- int Offset=FilterItanium_GetBits(Data,StartPos+13,20);
+ uint Offset=FilterItanium_GetBits(Data,StartPos+13,20);
FilterItanium_SetBits(Data,(Offset-FileOffset)&0xfffff,StartPos+13,20);
}
}
@@ -926,43 +199,39 @@ void RarVM::ExecuteStandardFilter(VM_StandardFilters FilterType)
break;
case VMSF_DELTA:
{
- int DataSize=R[4],Channels=R[0],SrcPos=0,Border=DataSize*2;
- SET_VALUE(false,&Mem[VM_GLOBALMEMADDR+0x20],DataSize);
- if (DataSize>=VM_GLOBALMEMADDR/2)
- break;
+ uint DataSize=R[4],Channels=R[0],SrcPos=0,Border=DataSize*2;
+ if (DataSize>VM_MEMSIZE/2 || Channels>MAX3_UNPACK_CHANNELS || Channels==0)
+ return false;
-// bytes from same channels are grouped to continual data blocks,
-// so we need to place them back to their interleaving positions
-
- for (int CurChannel=0;CurChannel<Channels;CurChannel++)
+ // Bytes from same channels are grouped to continual data blocks,
+ // so we need to place them back to their interleaving positions.
+ for (uint CurChannel=0;CurChannel<Channels;CurChannel++)
{
byte PrevByte=0;
- for (int DestPos=DataSize+CurChannel;DestPos<Border;DestPos+=Channels)
+ for (uint DestPos=DataSize+CurChannel;DestPos<Border;DestPos+=Channels)
Mem[DestPos]=(PrevByte-=Mem[SrcPos++]);
}
}
break;
case VMSF_RGB:
{
- int DataSize=R[4],Width=R[0]-3,PosR=R[1];
+ uint DataSize=R[4],Width=R[0]-3,PosR=R[1];
+ if (DataSize>VM_MEMSIZE/2 || DataSize<3 || Width>DataSize || PosR>2)
+ return false;
byte *SrcData=Mem,*DestData=SrcData+DataSize;
- const int Channels=3;
- SET_VALUE(false,&Mem[VM_GLOBALMEMADDR+0x20],DataSize);
- if (DataSize>=VM_GLOBALMEMADDR/2 || PosR<0)
- break;
- for (int CurChannel=0;CurChannel<Channels;CurChannel++)
+ const uint Channels=3;
+ for (uint CurChannel=0;CurChannel<Channels;CurChannel++)
{
- unsigned int PrevByte=0;
+ uint PrevByte=0;
- for (int I=CurChannel;I<DataSize;I+=Channels)
+ for (uint I=CurChannel;I<DataSize;I+=Channels)
{
- unsigned int Predicted;
- int UpperPos=I-Width;
- if (UpperPos>=3)
+ uint Predicted;
+ if (I>=Width+3)
{
- byte *UpperData=DestData+UpperPos;
- unsigned int UpperByte=*UpperData;
- unsigned int UpperLeftByte=*(UpperData-3);
+ byte *UpperData=DestData+I-Width;
+ uint UpperByte=*UpperData;
+ uint UpperLeftByte=*(UpperData-3);
Predicted=PrevByte+UpperByte-UpperLeftByte;
int pa=abs((int)(Predicted-PrevByte));
int pb=abs((int)(Predicted-UpperByte));
@@ -980,7 +249,7 @@ void RarVM::ExecuteStandardFilter(VM_StandardFilters FilterType)
DestData[I]=PrevByte=(byte)(Predicted-*(SrcData++));
}
}
- for (int I=PosR,Border=DataSize-2;I<Border;I+=3)
+ for (uint I=PosR,Border=DataSize-2;I<Border;I+=3)
{
byte G=DestData[I+1];
DestData[I]+=G;
@@ -990,35 +259,38 @@ void RarVM::ExecuteStandardFilter(VM_StandardFilters FilterType)
break;
case VMSF_AUDIO:
{
- int DataSize=R[4],Channels=R[0];
+ uint DataSize=R[4],Channels=R[0];
byte *SrcData=Mem,*DestData=SrcData+DataSize;
- SET_VALUE(false,&Mem[VM_GLOBALMEMADDR+0x20],DataSize);
- if (DataSize>=VM_GLOBALMEMADDR/2)
- break;
- for (int CurChannel=0;CurChannel<Channels;CurChannel++)
+ // In fact, audio channels never exceed 4.
+ if (DataSize>VM_MEMSIZE/2 || Channels>128 || Channels==0)
+ return false;
+ for (uint CurChannel=0;CurChannel<Channels;CurChannel++)
{
- unsigned int PrevByte=0,PrevDelta=0,Dif[7];
+ uint PrevByte=0,PrevDelta=0,Dif[7];
int D1=0,D2=0,D3;
int K1=0,K2=0,K3=0;
memset(Dif,0,sizeof(Dif));
- for (int I=CurChannel,ByteCount=0;I<DataSize;I+=Channels,ByteCount++)
+ for (uint I=CurChannel,ByteCount=0;I<DataSize;I+=Channels,ByteCount++)
{
D3=D2;
D2=PrevDelta-D1;
D1=PrevDelta;
- unsigned int Predicted=8*PrevByte+K1*D1+K2*D2+K3*D3;
+ uint Predicted=8*PrevByte+K1*D1+K2*D2+K3*D3;
Predicted=(Predicted>>3) & 0xff;
- unsigned int CurByte=*(SrcData++);
+ uint CurByte=*(SrcData++);
Predicted-=CurByte;
DestData[I]=Predicted;
PrevDelta=(signed char)(Predicted-PrevByte);
PrevByte=Predicted;
- int D=((signed char)CurByte)<<3;
+ int D=(signed char)CurByte;
+ // Left shift of negative value is undefined behavior in C++,
+ // so we cast it to unsigned to follow the standard.
+ D=(uint)D<<3;
Dif[0]+=abs(D);
Dif[1]+=abs(D-D1);
@@ -1030,9 +302,9 @@ void RarVM::ExecuteStandardFilter(VM_StandardFilters FilterType)
if ((ByteCount & 0x1f)==0)
{
- unsigned int MinDif=Dif[0],NumMinDif=0;
+ uint MinDif=Dif[0],NumMinDif=0;
Dif[0]=0;
- for (int J=1;J<sizeof(Dif)/sizeof(Dif[0]);J++)
+ for (uint J=1;J<ASIZE(Dif);J++)
{
if (Dif[J]<MinDif)
{
@@ -1055,50 +327,34 @@ void RarVM::ExecuteStandardFilter(VM_StandardFilters FilterType)
}
}
break;
- case VMSF_UPCASE:
- {
- int DataSize=R[4],SrcPos=0,DestPos=DataSize;
- if (DataSize>=VM_GLOBALMEMADDR/2)
- break;
- while (SrcPos<DataSize)
- {
- byte CurByte=Mem[SrcPos++];
- if (CurByte==2 && (CurByte=Mem[SrcPos++])!=2)
- CurByte-=32;
- Mem[DestPos++]=CurByte;
- }
- SET_VALUE(false,&Mem[VM_GLOBALMEMADDR+0x1c],DestPos-DataSize);
- SET_VALUE(false,&Mem[VM_GLOBALMEMADDR+0x20],DataSize);
- }
- break;
}
+ return true;
}
-unsigned int RarVM::FilterItanium_GetBits(byte *Data,int BitPos,int BitCount)
+uint RarVM::FilterItanium_GetBits(byte *Data,uint BitPos,uint BitCount)
{
- int InAddr=BitPos/8;
- int InBit=BitPos&7;
- unsigned int BitField=(uint)Data[InAddr++];
+ uint InAddr=BitPos/8;
+ uint InBit=BitPos&7;
+ uint BitField=(uint)Data[InAddr++];
BitField|=(uint)Data[InAddr++] << 8;
BitField|=(uint)Data[InAddr++] << 16;
BitField|=(uint)Data[InAddr] << 24;
BitField >>= InBit;
- return(BitField & (0xffffffff>>(32-BitCount)));
+ return BitField & (0xffffffff>>(32-BitCount));
}
-void RarVM::FilterItanium_SetBits(byte *Data,unsigned int BitField,int BitPos,
- int BitCount)
+void RarVM::FilterItanium_SetBits(byte *Data,uint BitField,uint BitPos,uint BitCount)
{
- int InAddr=BitPos/8;
- int InBit=BitPos&7;
- unsigned int AndMask=0xffffffff>>(32-BitCount);
+ uint InAddr=BitPos/8;
+ uint InBit=BitPos&7;
+ uint AndMask=0xffffffff>>(32-BitCount);
AndMask=~(AndMask<<InBit);
BitField<<=InBit;
- for (int I=0;I<4;I++)
+ for (uint I=0;I<4;I++)
{
Data[InAddr+I]&=AndMask;
Data[InAddr+I]|=BitField;
@@ -1106,4 +362,3 @@ void RarVM::FilterItanium_SetBits(byte *Data,unsigned int BitField,int BitPos,
BitField>>=8;
}
}
-#endif
diff --git a/unrar/unrar/rarvm.hpp b/unrar/unrar/rarvm.hpp
index 0a6ff8a..e65c4b1 100644
--- a/unrar/unrar/rarvm.hpp
+++ b/unrar/unrar/rarvm.hpp
@@ -1,108 +1,43 @@
#ifndef _RAR_VM_
#define _RAR_VM_
-#define VM_STANDARDFILTERS
-
-#ifndef SFX_MODULE
-#define VM_OPTIMIZE
-#endif
-
-
#define VM_MEMSIZE 0x40000
#define VM_MEMMASK (VM_MEMSIZE-1)
-#define VM_GLOBALMEMADDR 0x3C000
-#define VM_GLOBALMEMSIZE 0x2000
-#define VM_FIXEDGLOBALSIZE 64
-
-enum VM_Commands
-{
- VM_MOV, VM_CMP, VM_ADD, VM_SUB, VM_JZ, VM_JNZ, VM_INC, VM_DEC,
- VM_JMP, VM_XOR, VM_AND, VM_OR, VM_TEST, VM_JS, VM_JNS, VM_JB,
- VM_JBE, VM_JA, VM_JAE, VM_PUSH, VM_POP, VM_CALL, VM_RET, VM_NOT,
- VM_SHL, VM_SHR, VM_SAR, VM_NEG, VM_PUSHA,VM_POPA, VM_PUSHF,VM_POPF,
- VM_MOVZX,VM_MOVSX,VM_XCHG, VM_MUL, VM_DIV, VM_ADC, VM_SBB, VM_PRINT,
-
-#ifdef VM_OPTIMIZE
- VM_MOVB, VM_MOVD, VM_CMPB, VM_CMPD,
-
- VM_ADDB, VM_ADDD, VM_SUBB, VM_SUBD, VM_INCB, VM_INCD, VM_DECB, VM_DECD,
- VM_NEGB, VM_NEGD,
-#endif
-
- VM_STANDARD
-};
enum VM_StandardFilters {
VMSF_NONE, VMSF_E8, VMSF_E8E9, VMSF_ITANIUM, VMSF_RGB, VMSF_AUDIO,
- VMSF_DELTA, VMSF_UPCASE
+ VMSF_DELTA
};
-enum VM_Flags {VM_FC=1,VM_FZ=2,VM_FS=0x80000000};
-
-enum VM_OpType {VM_OPREG,VM_OPINT,VM_OPREGMEM,VM_OPNONE};
-
-struct VM_PreparedOperand
-{
- VM_OpType Type;
- uint Data;
- uint Base;
- uint *Addr;
-};
-
-struct VM_PreparedCommand
-{
- VM_Commands OpCode;
- bool ByteMode;
- VM_PreparedOperand Op1,Op2;
-};
-
-
struct VM_PreparedProgram
{
- VM_PreparedProgram() {AltCmd=NULL;}
-
- Array<VM_PreparedCommand> Cmd;
- VM_PreparedCommand *AltCmd;
- int CmdCount;
-
- Array<byte> GlobalData;
- Array<byte> StaticData; // static data contained in DB operators
+ VM_PreparedProgram()
+ {
+ FilteredDataSize=0;
+ Type=VMSF_NONE;
+ }
+ VM_StandardFilters Type;
uint InitR[7];
-
byte *FilteredData;
- unsigned int FilteredDataSize;
+ uint FilteredDataSize;
};
-class RarVM:private BitInput
+class RarVM
{
private:
- inline uint GetValue(bool ByteMode,uint *Addr);
- inline void SetValue(bool ByteMode,uint *Addr,uint Value);
- inline uint* GetOperand(VM_PreparedOperand *CmdOp);
- void DecodeArg(VM_PreparedOperand &Op,bool ByteMode);
-#ifdef VM_OPTIMIZE
- void Optimize(VM_PreparedProgram *Prg);
-#endif
- bool ExecuteCode(VM_PreparedCommand *PreparedCode,int CodeSize);
-#ifdef VM_STANDARDFILTERS
- VM_StandardFilters IsStandardFilter(byte *Code,int CodeSize);
- void ExecuteStandardFilter(VM_StandardFilters FilterType);
- unsigned int FilterItanium_GetBits(byte *Data,int BitPos,int BitCount);
- void FilterItanium_SetBits(byte *Data,unsigned int BitField,int BitPos,
- int BitCount);
-#endif
+ bool ExecuteStandardFilter(VM_StandardFilters FilterType);
+ uint FilterItanium_GetBits(byte *Data,uint BitPos,uint BitCount);
+ void FilterItanium_SetBits(byte *Data,uint BitField,uint BitPos,uint BitCount);
byte *Mem;
uint R[8];
- uint Flags;
public:
RarVM();
~RarVM();
void Init();
- void Prepare(byte *Code,int CodeSize,VM_PreparedProgram *Prg);
+ void Prepare(byte *Code,uint CodeSize,VM_PreparedProgram *Prg);
void Execute(VM_PreparedProgram *Prg);
- void SetLowEndianValue(uint *Addr,uint Value);
- void SetMemory(unsigned int Pos,byte *Data,unsigned int DataSize);
+ void SetMemory(size_t Pos,byte *Data,size_t DataSize);
static uint ReadData(BitInput &Inp);
};
diff --git a/unrar/unrar/rarvmtbl.cpp b/unrar/unrar/rarvmtbl.cpp
deleted file mode 100644
index b5e6c72..0000000
--- a/unrar/unrar/rarvmtbl.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-#define VMCF_OP0 0
-#define VMCF_OP1 1
-#define VMCF_OP2 2
-#define VMCF_OPMASK 3
-#define VMCF_BYTEMODE 4
-#define VMCF_JUMP 8
-#define VMCF_PROC 16
-#define VMCF_USEFLAGS 32
-#define VMCF_CHFLAGS 64
-
-static byte VM_CmdFlags[]=
-{
- /* VM_MOV */ VMCF_OP2 | VMCF_BYTEMODE ,
- /* VM_CMP */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
- /* VM_ADD */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
- /* VM_SUB */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
- /* VM_JZ */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS ,
- /* VM_JNZ */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS ,
- /* VM_INC */ VMCF_OP1 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
- /* VM_DEC */ VMCF_OP1 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
- /* VM_JMP */ VMCF_OP1 | VMCF_JUMP ,
- /* VM_XOR */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
- /* VM_AND */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
- /* VM_OR */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
- /* VM_TEST */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
- /* VM_JS */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS ,
- /* VM_JNS */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS ,
- /* VM_JB */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS ,
- /* VM_JBE */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS ,
- /* VM_JA */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS ,
- /* VM_JAE */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS ,
- /* VM_PUSH */ VMCF_OP1 ,
- /* VM_POP */ VMCF_OP1 ,
- /* VM_CALL */ VMCF_OP1 | VMCF_PROC ,
- /* VM_RET */ VMCF_OP0 | VMCF_PROC ,
- /* VM_NOT */ VMCF_OP1 | VMCF_BYTEMODE ,
- /* VM_SHL */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
- /* VM_SHR */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
- /* VM_SAR */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
- /* VM_NEG */ VMCF_OP1 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
- /* VM_PUSHA */ VMCF_OP0 ,
- /* VM_POPA */ VMCF_OP0 ,
- /* VM_PUSHF */ VMCF_OP0 | VMCF_USEFLAGS ,
- /* VM_POPF */ VMCF_OP0 | VMCF_CHFLAGS ,
- /* VM_MOVZX */ VMCF_OP2 ,
- /* VM_MOVSX */ VMCF_OP2 ,
- /* VM_XCHG */ VMCF_OP2 | VMCF_BYTEMODE ,
- /* VM_MUL */ VMCF_OP2 | VMCF_BYTEMODE ,
- /* VM_DIV */ VMCF_OP2 | VMCF_BYTEMODE ,
- /* VM_ADC */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_USEFLAGS | VMCF_CHFLAGS ,
- /* VM_SBB */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_USEFLAGS | VMCF_CHFLAGS ,
- /* VM_PRINT */ VMCF_OP0
-};
diff --git a/unrar/unrar/rawint.hpp b/unrar/unrar/rawint.hpp
new file mode 100644
index 0000000..c8cd86f
--- /dev/null
+++ b/unrar/unrar/rawint.hpp
@@ -0,0 +1,122 @@
+#ifndef _RAR_RAWINT_
+#define _RAR_RAWINT_
+
+#define rotls(x,n,xsize) (((x)<<(n)) | ((x)>>(xsize-(n))))
+#define rotrs(x,n,xsize) (((x)>>(n)) | ((x)<<(xsize-(n))))
+#define rotl32(x,n) rotls(x,n,32)
+#define rotr32(x,n) rotrs(x,n,32)
+
+inline uint RawGet2(const void *Data)
+{
+ byte *D=(byte *)Data;
+ return D[0]+(D[1]<<8);
+}
+
+
+inline uint32 RawGet4(const void *Data)
+{
+#if defined(BIG_ENDIAN) || !defined(ALLOW_MISALIGNED)
+ byte *D=(byte *)Data;
+ return D[0]+(D[1]<<8)+(D[2]<<16)+(D[3]<<24);
+#else
+ return *(uint32 *)Data;
+#endif
+}
+
+
+inline uint64 RawGet8(const void *Data)
+{
+#if defined(BIG_ENDIAN) || !defined(ALLOW_MISALIGNED)
+ byte *D=(byte *)Data;
+ return INT32TO64(RawGet4(D+4),RawGet4(D));
+#else
+ return *(uint64 *)Data;
+#endif
+}
+
+
+inline void RawPut2(uint Field,void *Data)
+{
+ byte *D=(byte *)Data;
+ D[0]=(byte)(Field);
+ D[1]=(byte)(Field>>8);
+}
+
+
+inline void RawPut4(uint32 Field,void *Data)
+{
+#if defined(BIG_ENDIAN) || !defined(ALLOW_MISALIGNED)
+ byte *D=(byte *)Data;
+ D[0]=(byte)(Field);
+ D[1]=(byte)(Field>>8);
+ D[2]=(byte)(Field>>16);
+ D[3]=(byte)(Field>>24);
+#else
+ *(uint32 *)Data=Field;
+#endif
+}
+
+
+inline void RawPut8(uint64 Field,void *Data)
+{
+#if defined(BIG_ENDIAN) || !defined(ALLOW_MISALIGNED)
+ byte *D=(byte *)Data;
+ D[0]=(byte)(Field);
+ D[1]=(byte)(Field>>8);
+ D[2]=(byte)(Field>>16);
+ D[3]=(byte)(Field>>24);
+ D[4]=(byte)(Field>>32);
+ D[5]=(byte)(Field>>40);
+ D[6]=(byte)(Field>>48);
+ D[7]=(byte)(Field>>56);
+#else
+ *(uint64 *)Data=Field;
+#endif
+}
+
+
+#if defined(LITTLE_ENDIAN) && defined(ALLOW_MISALIGNED)
+#define USE_MEM_BYTESWAP
+#endif
+
+// Load 4 big endian bytes from memory and return uint32.
+inline uint32 RawGetBE4(const byte *m)
+{
+#if defined(USE_MEM_BYTESWAP) && defined(_MSC_VER)
+ return _byteswap_ulong(*(uint32 *)m);
+#elif defined(USE_MEM_BYTESWAP) && (defined(__clang__) || defined(__GNUC__))
+ return __builtin_bswap32(*(uint32 *)m);
+#else
+ return uint32(m[0]<<24) | uint32(m[1]<<16) | uint32(m[2]<<8) | m[3];
+#endif
+}
+
+
+// Save integer to memory as big endian.
+inline void RawPutBE4(uint32 i,byte *mem)
+{
+#if defined(USE_MEM_BYTESWAP) && defined(_MSC_VER)
+ *(uint32*)mem = _byteswap_ulong(i);
+#elif defined(USE_MEM_BYTESWAP) && (defined(__clang__) || defined(__GNUC__))
+ *(uint32*)mem = __builtin_bswap32(i);
+#else
+ mem[0]=byte(i>>24);
+ mem[1]=byte(i>>16);
+ mem[2]=byte(i>>8);
+ mem[3]=byte(i);
+#endif
+}
+
+
+inline uint32 ByteSwap32(uint32 i)
+{
+#ifdef _MSC_VER
+ return _byteswap_ulong(i);
+#elif defined(__clang__) || defined(__GNUC__)
+ return __builtin_bswap32(i);
+#else
+ return (rotl32(i,24)&0xFF00FF00)|(rotl32(i,8)&0x00FF00FF);
+#endif
+}
+
+#endif
diff --git a/unrar/unrar/rawread.cpp b/unrar/unrar/rawread.cpp
index bbba948..d99bac8 100644
--- a/unrar/unrar/rawread.cpp
+++ b/unrar/unrar/rawread.cpp
@@ -1,45 +1,69 @@
#include "rar.hpp"
+RawRead::RawRead()
+{
+ RawRead::SrcFile=NULL;
+ Reset();
+}
+
+
RawRead::RawRead(File *SrcFile)
{
RawRead::SrcFile=SrcFile;
+ Reset();
+}
+
+
+void RawRead::Reset()
+{
+ Data.SoftReset();
ReadPos=0;
DataSize=0;
-#ifndef SHELL_EXT
Crypt=NULL;
-#endif
}
-void RawRead::Read(int Size)
+size_t RawRead::Read(size_t Size)
{
-#if !defined(SHELL_EXT) && !defined(NOCRYPT)
+ size_t ReadSize=0;
+#if !defined(RAR_NOCRYPT)
if (Crypt!=NULL)
{
- int CurSize=Data.Size();
- int SizeToRead=Size-(CurSize-DataSize);
- if (SizeToRead>0)
+ // Full size of buffer with already read data including data read
+ // for encryption block alignment.
+ size_t FullSize=Data.Size();
+
+ // Data read for alignment and not processed yet.
+ size_t DataLeft=FullSize-DataSize;
+
+ if (Size>DataLeft) // Need to read more than we already have.
{
- int AlignedReadSize=SizeToRead+((~SizeToRead+1)&0xf);
+ size_t SizeToRead=Size-DataLeft;
+ size_t AlignedReadSize=SizeToRead+((~SizeToRead+1) & CRYPT_BLOCK_MASK);
Data.Add(AlignedReadSize);
- int ReadSize=SrcFile->Read(&Data[CurSize],AlignedReadSize);
- Crypt->DecryptBlock(&Data[CurSize],AlignedReadSize);
+ ReadSize=SrcFile->Read(&Data[FullSize],AlignedReadSize);
+ Crypt->DecryptBlock(&Data[FullSize],AlignedReadSize);
DataSize+=ReadSize==0 ? 0:Size;
}
- else
+ else // Use buffered data, no real read.
+ {
+ ReadSize=Size;
DataSize+=Size;
+ }
}
else
#endif
if (Size!=0)
{
Data.Add(Size);
- DataSize+=SrcFile->Read(&Data[DataSize],Size);
+ ReadSize=SrcFile->Read(&Data[DataSize],Size);
+ DataSize+=ReadSize;
}
+ return ReadSize;
}
-void RawRead::Read(byte *SrcData,int Size)
+void RawRead::Read(byte *SrcData,size_t Size)
{
if (Size!=0)
{
@@ -50,77 +74,124 @@ void RawRead::Read(byte *SrcData,int Size)
}
-void RawRead::Get(byte &Field)
+byte RawRead::Get1()
{
- if (ReadPos<DataSize)
- {
- Field=Data[ReadPos];
- ReadPos++;
- }
- else
- Field=0;
+ return ReadPos<DataSize ? Data[ReadPos++]:0;
}
-void RawRead::Get(ushort &Field)
+ushort RawRead::Get2()
{
if (ReadPos+1<DataSize)
{
- Field=Data[ReadPos]+(Data[ReadPos+1]<<8);
+ ushort Result=Data[ReadPos]+(Data[ReadPos+1]<<8);
ReadPos+=2;
+ return Result;
}
- else
- Field=0;
+ return 0;
}
-void RawRead::Get(uint &Field)
+uint RawRead::Get4()
{
if (ReadPos+3<DataSize)
{
- Field=Data[ReadPos]+(Data[ReadPos+1]<<8)+(Data[ReadPos+2]<<16)+
- (Data[ReadPos+3]<<24);
+ uint Result=Data[ReadPos]+(Data[ReadPos+1]<<8)+(Data[ReadPos+2]<<16)+
+ (Data[ReadPos+3]<<24);
ReadPos+=4;
+ return Result;
}
- else
- Field=0;
+ return 0;
}
-void RawRead::Get8(Int64 &Field)
+uint64 RawRead::Get8()
{
- uint Low,High;
- Get(Low);
- Get(High);
- Field=int32to64(High,Low);
+ uint Low=Get4(),High=Get4();
+ return INT32TO64(High,Low);
}
-void RawRead::Get(byte *Field,int Size)
+uint64 RawRead::GetV()
{
- if (ReadPos+Size-1<DataSize)
+ uint64 Result=0;
+ // Need to check Shift<64, because for shift greater than or equal to
+ // the width of the promoted left operand, the behavior is undefined.
+ for (uint Shift=0;ReadPos<DataSize && Shift<64;Shift+=7)
{
- memcpy(Field,&Data[ReadPos],Size);
- ReadPos+=Size;
+ byte CurByte=Data[ReadPos++];
+ Result+=uint64(CurByte & 0x7f)<<Shift;
+ if ((CurByte & 0x80)==0)
+ return Result; // Decoded successfully.
}
- else
- memset(Field,0,Size);
+ return 0; // Out of buffer border.
}
-void RawRead::Get(wchar *Field,int Size)
+// Return a number of bytes in current variable length integer.
+uint RawRead::GetVSize(size_t Pos)
+{
+ for (size_t CurPos=Pos;CurPos<DataSize;CurPos++)
+ if ((Data[CurPos] & 0x80)==0)
+ return int(CurPos-Pos+1);
+ return 0; // Buffer overflow.
+}
+
+
+size_t RawRead::GetB(void *Field,size_t Size)
+{
+ byte *F=(byte *)Field;
+ size_t CopySize=Min(DataSize-ReadPos,Size);
+ if (CopySize>0)
+ memcpy(F,&Data[ReadPos],CopySize);
+ if (Size>CopySize)
+ memset(F+CopySize,0,Size-CopySize);
+ ReadPos+=CopySize;
+ return CopySize;
+}
+
+
+void RawRead::GetW(wchar *Field,size_t Size)
{
if (ReadPos+2*Size-1<DataSize)
{
RawToWide(&Data[ReadPos],Field,Size);
- ReadPos+=2*Size;
+ ReadPos+=sizeof(wchar)*Size;
}
else
- memset(Field,0,2*Size);
+ memset(Field,0,sizeof(wchar)*Size);
+}
+
+
+uint RawRead::GetCRC15(bool ProcessedOnly) // RAR 1.5 block CRC.
+{
+ if (DataSize<=2)
+ return 0;
+ uint HeaderCRC=CRC32(0xffffffff,&Data[2],(ProcessedOnly ? ReadPos:DataSize)-2);
+ return ~HeaderCRC & 0xffff;
}
-uint RawRead::GetCRC(bool ProcessedOnly)
+uint RawRead::GetCRC50() // RAR 5.0 block CRC.
{
- return(DataSize>2 ? CRC(0xffffffff,&Data[2],(ProcessedOnly ? ReadPos:DataSize)-2):0xffffffff);
+ if (DataSize<=4)
+ return 0xffffffff;
+ return CRC32(0xffffffff,&Data[4],DataSize-4) ^ 0xffffffff;
+}
+
+
+// Read vint from arbitrary byte array.
+uint64 RawGetV(const byte *Data,uint &ReadPos,uint DataSize,bool &Overflow)
+{
+ Overflow=false;
+ uint64 Result=0;
+ for (uint Shift=0;ReadPos<DataSize;Shift+=7)
+ {
+ byte CurByte=Data[ReadPos++];
+ Result+=uint64(CurByte & 0x7f)<<Shift;
+ if ((CurByte & 0x80)==0)
+ return Result; // Decoded successfully.
+ }
+ Overflow=true;
+ return 0; // Out of buffer border.
}
diff --git a/unrar/unrar/rawread.hpp b/unrar/unrar/rawread.hpp
index 47c2bbc..b319898 100644
--- a/unrar/unrar/rawread.hpp
+++ b/unrar/unrar/rawread.hpp
@@ -6,27 +6,36 @@ class RawRead
private:
Array<byte> Data;
File *SrcFile;
- int DataSize;
- int ReadPos;
-#ifndef SHELL_EXT
+ size_t DataSize;
+ size_t ReadPos;
CryptData *Crypt;
-#endif
public:
+ RawRead();
RawRead(File *SrcFile);
- void Read(int Size);
- void Read(byte *SrcData,int Size);
- void Get(byte &Field);
- void Get(ushort &Field);
- void Get(uint &Field);
- void Get8(Int64 &Field);
- void Get(byte *Field,int Size);
- void Get(wchar *Field,int Size);
- uint GetCRC(bool ProcessedOnly);
- int Size() {return DataSize;}
- int PaddedSize() {return Data.Size()-DataSize;}
-#ifndef SHELL_EXT
+ void Reset();
+ size_t Read(size_t Size);
+ void Read(byte *SrcData,size_t Size);
+ byte Get1();
+ ushort Get2();
+ uint Get4();
+ uint64 Get8();
+ uint64 GetV();
+ uint GetVSize(size_t Pos);
+ size_t GetB(void *Field,size_t Size);
+ void GetW(wchar *Field,size_t Size);
+ uint GetCRC15(bool ProcessedOnly);
+ uint GetCRC50();
+ byte* GetDataPtr() {return &Data[0];}
+ size_t Size() {return DataSize;}
+ size_t PaddedSize() {return Data.Size()-DataSize;}
+ size_t DataLeft() {return DataSize-ReadPos;}
+ size_t GetPos() {return ReadPos;}
+ void SetPos(size_t Pos) {ReadPos=Pos;}
+ void Skip(size_t Size) {ReadPos+=Size;}
+ void Rewind() {SetPos(0);}
void SetCrypt(CryptData *Crypt) {RawRead::Crypt=Crypt;}
-#endif
};
+uint64 RawGetV(const byte *Data,uint &ReadPos,uint DataSize,bool &Overflow);
+
#endif
diff --git a/unrar/unrar/rdwrfn.cpp b/unrar/unrar/rdwrfn.cpp
index 2662bdb..fa71376 100644
--- a/unrar/unrar/rdwrfn.cpp
+++ b/unrar/unrar/rdwrfn.cpp
@@ -2,6 +2,11 @@
ComprDataIO::ComprDataIO()
{
+#ifndef RAR_NOCRYPT
+ Crypt=new CryptData;
+ Decrypt=new CryptData;
+#endif
+
Init();
}
@@ -11,73 +16,120 @@ void ComprDataIO::Init()
UnpackFromMemory=false;
UnpackToMemory=false;
UnpPackedSize=0;
+ UnpPackedLeft=0;
ShowProgress=true;
TestMode=false;
SkipUnpCRC=false;
+ NoFileHeader=false;
PackVolume=false;
UnpVolume=false;
NextVolumeMissing=false;
SrcFile=NULL;
DestFile=NULL;
+ UnpWrAddr=NULL;
UnpWrSize=0;
Command=NULL;
- Encryption=0;
- Decryption=0;
- TotalPackRead=0;
+ Encryption=false;
+ Decryption=false;
CurPackRead=CurPackWrite=CurUnpRead=CurUnpWrite=0;
- PackFileCRC=UnpFileCRC=PackedCRC=0xffffffff;
LastPercent=-1;
SubHead=NULL;
SubHeadPos=NULL;
CurrentCommand=0;
- ProcessedArcSize=TotalArcSize=0;
+ ProcessedArcSize=0;
+ LastArcSize=0;
+ TotalArcSize=0;
+}
+
+
+ComprDataIO::~ComprDataIO()
+{
+#ifndef RAR_NOCRYPT
+ delete Crypt;
+ delete Decrypt;
+#endif
}
-int ComprDataIO::UnpRead(byte *Addr,uint Count)
+int ComprDataIO::UnpRead(byte *Addr,size_t Count)
{
- int RetCode=0,TotalRead=0;
+#ifndef RAR_NOCRYPT
+ // In case of encryption we need to align read size to encryption
+ // block size. We can do it by simple masking, because unpack read code
+ // always reads more than CRYPT_BLOCK_SIZE, so we do not risk to make it 0.
+ if (Decryption)
+ Count &= ~CRYPT_BLOCK_MASK;
+#endif
+
+ int ReadSize=0,TotalRead=0;
byte *ReadAddr;
ReadAddr=Addr;
while (Count > 0)
{
Archive *SrcArc=(Archive *)SrcFile;
- uint ReadSize=(Count>UnpPackedSize) ? int64to32(UnpPackedSize):Count;
if (UnpackFromMemory)
{
memcpy(Addr,UnpackFromMemoryAddr,UnpackFromMemorySize);
- RetCode=UnpackFromMemorySize;
+ ReadSize=(int)UnpackFromMemorySize;
UnpackFromMemorySize=0;
}
else
{
- if (!SrcFile->IsOpened())
- return(-1);
- RetCode=SrcFile->Read(ReadAddr,ReadSize);
- FileHeader *hd=SubHead!=NULL ? SubHead:&SrcArc->NewLhd;
- if (hd->Flags & LHD_SPLIT_AFTER)
- PackedCRC=CRC(PackedCRC,ReadAddr,ReadSize);
+ size_t SizeToRead=((int64)Count>UnpPackedLeft) ? (size_t)UnpPackedLeft:Count;
+ if (SizeToRead > 0)
+ {
+ if (UnpVolume && Decryption && (int64)Count>UnpPackedLeft)
+ {
+ // We need aligned blocks for decryption and we want "Keep broken
+ // files" to work efficiently with missing encrypted volumes.
+ // So for last data block in volume we adjust the size to read to
+ // next equal or smaller block producing aligned total block size.
+ // So we'll ask for next volume only when processing few unaligned
+ // bytes left in the end, when most of data is already extracted.
+ size_t NewTotalRead = TotalRead + SizeToRead;
+ size_t Adjust = NewTotalRead - (NewTotalRead & ~CRYPT_BLOCK_MASK);
+ size_t NewSizeToRead = SizeToRead - Adjust;
+ if ((int)NewSizeToRead > 0)
+ SizeToRead = NewSizeToRead;
+ }
+
+ if (!SrcFile->IsOpened())
+ return -1;
+ ReadSize=SrcFile->Read(ReadAddr,SizeToRead);
+ FileHeader *hd=SubHead!=NULL ? SubHead:&SrcArc->FileHead;
+ if (!NoFileHeader && hd->SplitAfter)
+ PackedDataHash.Update(ReadAddr,ReadSize);
+ }
}
- CurUnpRead+=RetCode;
- TotalRead+=RetCode;
+ CurUnpRead+=ReadSize;
+ TotalRead+=ReadSize;
#ifndef NOVOLUME
- // these variable are not used below in NOVOLUME mode, so it is better
- // to exclude these commands to avoid compiler warnings
- ReadAddr+=RetCode;
- Count-=RetCode;
+ // These variable are not used in NOVOLUME mode, so it is better
+ // to exclude commands below to avoid compiler warnings.
+ ReadAddr+=ReadSize;
+ Count-=ReadSize;
#endif
- UnpPackedSize-=RetCode;
- if (UnpPackedSize == 0 && UnpVolume)
+ UnpPackedLeft-=ReadSize;
+
+ // Do not ask for next volume if we read something from current volume.
+ // If next volume is missing, we need to process all data from current
+ // volume before aborting. It helps to recover all possible data
+ // in "Keep broken files" mode. But if we process encrypted data,
+ // we ask for next volume also if we have non-aligned encryption block.
+ // Since we adjust data size for decryption earlier above,
+ // it does not hurt "Keep broken files" mode efficiency.
+ if (UnpVolume && UnpPackedLeft == 0 &&
+ (ReadSize==0 || Decryption && (TotalRead & CRYPT_BLOCK_MASK) != 0) )
{
#ifndef NOVOLUME
if (!MergeArchive(*SrcArc,this,true,CurrentCommand))
#endif
{
NextVolumeMissing=true;
- return(-1);
+ return -1;
}
}
else
@@ -85,55 +137,39 @@ int ComprDataIO::UnpRead(byte *Addr,uint Count)
}
Archive *SrcArc=(Archive *)SrcFile;
if (SrcArc!=NULL)
- ShowUnpRead(SrcArc->CurBlockPos+CurUnpRead,UnpArcSize);
- if (RetCode!=-1)
+ ShowUnpRead(SrcArc->NextBlockPos-UnpPackedSize+CurUnpRead,TotalArcSize);
+ if (ReadSize!=-1)
{
- RetCode=TotalRead;
-#ifndef NOCRYPT
+ ReadSize=TotalRead;
+#ifndef RAR_NOCRYPT
if (Decryption)
-#ifndef SFX_MODULE
- if (Decryption<20)
- Decrypt.Crypt(Addr,RetCode,(Decryption==15) ? NEW_CRYPT : OLD_DECODE);
- else
- if (Decryption==20)
- for (uint I=0;I<RetCode;I+=16)
- Decrypt.DecryptBlock20(&Addr[I]);
- else
-#endif
- {
- int CryptSize=(RetCode & 0xf)==0 ? RetCode:((RetCode & ~0xf)+16);
- Decrypt.DecryptBlock(Addr,CryptSize);
- }
+ Decrypt->DecryptBlock(Addr,ReadSize);
#endif
}
Wait();
- return(RetCode);
+ return ReadSize;
}
-void ComprDataIO::UnpWrite(byte *Addr,uint Count)
+void ComprDataIO::UnpWrite(byte *Addr,size_t Count)
{
+
#ifdef RARDLL
- RAROptions *Cmd=((Archive *)SrcFile)->GetRAROptions();
+ CommandData *Cmd=((Archive *)SrcFile)->GetCommandData();
if (Cmd->DllOpMode!=RAR_SKIP)
{
if (Cmd->Callback!=NULL &&
Cmd->Callback(UCM_PROCESSDATA,Cmd->UserData,(LPARAM)Addr,Count)==-1)
- ErrHandler.Exit(USER_BREAK);
+ ErrHandler.Exit(RARX_USERBREAK);
if (Cmd->ProcessDataProc!=NULL)
{
-#if defined(_WIN_32) && !defined(_MSC_VER) && !defined(__MINGW32__)
- _EBX=_ESP;
-#endif
- int RetCode=Cmd->ProcessDataProc(Addr,Count);
-#if defined(_WIN_32) && !defined(_MSC_VER) && !defined(__MINGW32__)
- _ESP=_EBX;
-#endif
+ int RetCode=Cmd->ProcessDataProc(Addr,(int)Count);
if (RetCode==0)
- ErrHandler.Exit(USER_BREAK);
+ ErrHandler.Exit(RARX_USERBREAK);
}
}
-#endif
+#endif // RARDLL
+
UnpWrAddr=Addr;
UnpWrSize=Count;
if (UnpackToMemory)
@@ -150,12 +186,7 @@ void ComprDataIO::UnpWrite(byte *Addr,uint Count)
DestFile->Write(Addr,Count);
CurUnpWrite+=Count;
if (!SkipUnpCRC)
-#ifndef SFX_MODULE
- if (((Archive *)SrcFile)->OldFormat)
- UnpFileCRC=OldCRC((ushort)UnpFileCRC,Addr,Count);
- else
-#endif
- UnpFileCRC=CRC(UnpFileCRC,Addr,Count);
+ UnpHash.Update(Addr,Count);
ShowUnpWrite();
Wait();
}
@@ -165,24 +196,20 @@ void ComprDataIO::UnpWrite(byte *Addr,uint Count)
-void ComprDataIO::ShowUnpRead(Int64 ArcPos,Int64 ArcSize)
+void ComprDataIO::ShowUnpRead(int64 ArcPos,int64 ArcSize)
{
if (ShowProgress && SrcFile!=NULL)
{
- if (TotalArcSize!=0)
- {
- // important when processing several archives or multivolume archive
- ArcSize=TotalArcSize;
- ArcPos+=ProcessedArcSize;
- }
+ // Important when processing several archives or multivolume archive.
+ ArcPos+=ProcessedArcSize;
Archive *SrcArc=(Archive *)SrcFile;
- RAROptions *Cmd=SrcArc->GetRAROptions();
+ CommandData *Cmd=SrcArc->GetCommandData();
int CurPercent=ToPercent(ArcPos,ArcSize);
if (!Cmd->DisablePercentage && CurPercent!=LastPercent)
{
- mprintf("\b\b\b\b%3d%%",CurPercent);
+ uiExtractProgress(CurUnpWrite,SrcArc->FileHead.UnpSize,ArcPos,ArcSize);
LastPercent=CurPercent;
}
}
@@ -200,6 +227,8 @@ void ComprDataIO::ShowUnpWrite()
+
+
void ComprDataIO::SetFiles(File *SrcFile,File *DestFile)
{
if (SrcFile!=NULL)
@@ -210,46 +239,40 @@ void ComprDataIO::SetFiles(File *SrcFile,File *DestFile)
}
-void ComprDataIO::GetUnpackedData(byte **Data,uint *Size)
+void ComprDataIO::GetUnpackedData(byte **Data,size_t *Size)
{
*Data=UnpWrAddr;
*Size=UnpWrSize;
}
-void ComprDataIO::SetEncryption(int Method,char *Password,byte *Salt,bool Encrypt,bool HandsOffHash)
+void ComprDataIO::SetEncryption(bool Encrypt,CRYPT_METHOD Method,
+ SecPassword *Password,const byte *Salt,const byte *InitV,
+ uint Lg2Cnt,byte *HashKey,byte *PswCheck)
{
+#ifndef RAR_NOCRYPT
if (Encrypt)
- {
- Encryption=*Password ? Method:0;
-#ifndef NOCRYPT
- Crypt.SetCryptKeys(Password,Salt,Encrypt,false,HandsOffHash);
-#endif
- }
+ Encryption=Crypt->SetCryptKeys(true,Method,Password,Salt,InitV,Lg2Cnt,HashKey,PswCheck);
else
- {
- Decryption=*Password ? Method:0;
-#ifndef NOCRYPT
- Decrypt.SetCryptKeys(Password,Salt,Encrypt,Method<29,HandsOffHash);
+ Decryption=Decrypt->SetCryptKeys(false,Method,Password,Salt,InitV,Lg2Cnt,HashKey,PswCheck);
#endif
- }
}
-#if !defined(SFX_MODULE) && !defined(NOCRYPT)
+#if !defined(SFX_MODULE) && !defined(RAR_NOCRYPT)
void ComprDataIO::SetAV15Encryption()
{
- Decryption=15;
- Decrypt.SetAV15Encryption();
+ Decryption=true;
+ Decrypt->SetAV15Encryption();
}
#endif
-#if !defined(SFX_MODULE) && !defined(NOCRYPT)
+#if !defined(SFX_MODULE) && !defined(RAR_NOCRYPT)
void ComprDataIO::SetCmt13Encryption()
{
- Decryption=13;
- Decrypt.SetCmt13Encryption();
+ Decryption=true;
+ Decrypt->SetCmt13Encryption();
}
#endif
@@ -264,3 +287,36 @@ void ComprDataIO::SetUnpackToMemory(byte *Addr,uint Size)
}
+// Extraction progress is based on the position in archive and we adjust
+// the total archives size here, so trailing blocks do not prevent progress
+// reaching 100% at the end of extraction. Alternatively we could print "100%"
+// after completing the entire archive extraction, but then we would need
+// to take into account possible messages like the checksum error after
+// last file percent progress.
+void ComprDataIO::AdjustTotalArcSize(Archive *Arc)
+{
+ // If we know a position of QO or RR blocks, use them to adjust the total
+ // packed size to beginning of these blocks. Earlier we already calculated
+ // the total size based on entire archive sizes. We also set LastArcSize
+ // to start of first trailing block, to add it later to ProcessedArcSize.
+ int64 ArcLength=Arc->IsSeekable() ? Arc->FileLength() : 0;
+ if (Arc->MainHead.QOpenOffset!=0) // QO is always preceding RR record.
+ LastArcSize=Arc->MainHead.QOpenOffset;
+ else
+ if (Arc->MainHead.RROffset!=0)
+ LastArcSize=Arc->MainHead.RROffset;
+ else
+ {
+ // If neither QO nor RR are found, exclude the approximate size of
+ // end of archive block.
+ // We select EndBlock to be larger than typical 8 bytes HEAD_ENDARC,
+ // but to not exceed the smallest 22 bytes HEAD_FILE with 1 byte file
+ // name, so we do not have two files with 100% at the end of archive.
+ const uint EndBlock=23;
+
+ if (ArcLength>EndBlock)
+ LastArcSize=ArcLength-EndBlock;
+ }
+
+ TotalArcSize-=ArcLength-LastArcSize;
+}
diff --git a/unrar/unrar/rdwrfn.hpp b/unrar/unrar/rdwrfn.hpp
index b80ae28..3060a0f 100644
--- a/unrar/unrar/rdwrfn.hpp
+++ b/unrar/unrar/rdwrfn.hpp
@@ -1,33 +1,41 @@
#ifndef _RAR_DATAIO_
#define _RAR_DATAIO_
+class Archive;
class CmdAdd;
class Unpack;
+class ArcFileSearch;
+#if 0
+// We use external i/o calls for Benchmark command.
+#define COMPRDATAIO_EXTIO
+#endif
class ComprDataIO
{
private:
- void ShowUnpRead(Int64 ArcPos,Int64 ArcSize);
+ void ShowUnpRead(int64 ArcPos,int64 ArcSize);
void ShowUnpWrite();
bool UnpackFromMemory;
- uint UnpackFromMemorySize;
+ size_t UnpackFromMemorySize;
byte *UnpackFromMemoryAddr;
bool UnpackToMemory;
- uint UnpackToMemorySize;
+ size_t UnpackToMemorySize;
byte *UnpackToMemoryAddr;
- uint UnpWrSize;
+ size_t UnpWrSize;
byte *UnpWrAddr;
- Int64 UnpPackedSize;
+ int64 UnpPackedSize;
+ int64 UnpPackedLeft;
bool ShowProgress;
bool TestMode;
bool SkipUnpCRC;
+ bool NoFileHeader;
File *SrcFile;
File *DestFile;
@@ -35,54 +43,63 @@ class ComprDataIO
CmdAdd *Command;
FileHeader *SubHead;
- Int64 *SubHeadPos;
+ int64 *SubHeadPos;
-#ifndef NOCRYPT
- CryptData Crypt;
- CryptData Decrypt;
+#ifndef RAR_NOCRYPT
+ CryptData *Crypt;
+ CryptData *Decrypt;
#endif
int LastPercent;
- char CurrentCommand;
+ wchar CurrentCommand;
public:
ComprDataIO();
+ ~ComprDataIO();
void Init();
- int UnpRead(byte *Addr,uint Count);
- void UnpWrite(byte *Addr,uint Count);
+ int UnpRead(byte *Addr,size_t Count);
+ void UnpWrite(byte *Addr,size_t Count);
void EnableShowProgress(bool Show) {ShowProgress=Show;}
- void GetUnpackedData(byte **Data,uint *Size);
- void SetPackedSizeToRead(Int64 Size) {UnpPackedSize=Size;}
+ void GetUnpackedData(byte **Data,size_t *Size);
+ void SetPackedSizeToRead(int64 Size) {UnpPackedSize=UnpPackedLeft=Size;}
void SetTestMode(bool Mode) {TestMode=Mode;}
void SetSkipUnpCRC(bool Skip) {SkipUnpCRC=Skip;}
+ void SetNoFileHeader(bool Mode) {NoFileHeader=Mode;}
void SetFiles(File *SrcFile,File *DestFile);
void SetCommand(CmdAdd *Cmd) {Command=Cmd;}
- void SetSubHeader(FileHeader *hd,Int64 *Pos) {SubHead=hd;SubHeadPos=Pos;}
- void SetEncryption(int Method,char *Password,byte *Salt,bool Encrypt,bool HandsOffHash);
+ void SetSubHeader(FileHeader *hd,int64 *Pos) {SubHead=hd;SubHeadPos=Pos;}
+ void SetEncryption(bool Encrypt,CRYPT_METHOD Method,SecPassword *Password,
+ const byte *Salt,const byte *InitV,uint Lg2Cnt,byte *HashKey,byte *PswCheck);
void SetAV15Encryption();
void SetCmt13Encryption();
void SetUnpackToMemory(byte *Addr,uint Size);
- void SetCurrentCommand(char Cmd) {CurrentCommand=Cmd;}
+ void SetCurrentCommand(wchar Cmd) {CurrentCommand=Cmd;}
+ void AdjustTotalArcSize(Archive *Arc);
+
bool PackVolume;
bool UnpVolume;
bool NextVolumeMissing;
- Int64 TotalPackRead;
- Int64 UnpArcSize;
- Int64 CurPackRead,CurPackWrite,CurUnpRead,CurUnpWrite;
+ int64 CurPackRead,CurPackWrite,CurUnpRead,CurUnpWrite;
+
// Size of already processed archives.
// Used to calculate the total operation progress.
- Int64 ProcessedArcSize;
+ int64 ProcessedArcSize;
+
+ // Last extracted archive size up to QO or RR block.
+ int64 LastArcSize;
- Int64 TotalArcSize;
+ int64 TotalArcSize;
- uint PackFileCRC,UnpFileCRC,PackedCRC;
+ DataHash PackedDataHash; // Packed write and unpack read hash.
+ DataHash PackHash; // Pack read hash.
+ DataHash UnpHash; // Unpack write hash.
- int Encryption;
- int Decryption;
+ bool Encryption;
+ bool Decryption;
};
#endif
diff --git a/unrar/unrar/readme.txt b/unrar/unrar/readme.txt
index 41ee05c..a1f820a 100644
--- a/unrar/unrar/readme.txt
+++ b/unrar/unrar/readme.txt
@@ -4,12 +4,13 @@
1. General
- This package includes freeware Unrar C++ source and a few makefiles
- (makefile.bcc, makefile.msc+msc.dep, makefile.unix). Unrar source
- is subset of RAR and generated from RAR source automatically,
+ This package includes freeware Unrar C++ source and makefile for
+ several Unix compilers.
+
+ Unrar source is subset of RAR and generated from RAR source automatically,
by a small program removing blocks like '#ifndef UNRAR ... #endif'.
- Such method is not perfect and you may find some RAR related
- stuff unnecessary in Unrar, especially in header files.
+ Such method is not perfect and you may find some RAR related stuff
+ unnecessary in Unrar, especially in header files.
If you wish to port Unrar to a new platform, you may need to edit
'#define LITTLE_ENDIAN' in os.hpp and data type definitions
@@ -17,16 +18,10 @@
if computer architecture does not allow not aligned data access,
you need to undefine ALLOW_NOT_ALIGNED_INT and define
- STRICT_ALIGNMENT_REQUIRED in os.h. Note that it will increase memory
- requirements.
-
- If you use Borland C++ makefile (makefile.bcc), you need to define
- BASEPATHCC environment (or makefile) variable containing
- the path to Borland C++ installation.
+ STRICT_ALIGNMENT_REQUIRED in os.h.
- Makefile.unix contains numerous compiler option sets.
- GCC Linux is selected by default. If you need to compile Unrar
- for other platforms, uncomment corresponding lines.
+ UnRAR.vcproj and UnRARDll.vcproj are projects for Microsoft Visual C++.
+ UnRARDll.vcproj lets to build unrar.dll library.
2. Unrar binaries
@@ -38,16 +33,8 @@
3. Acknowledgements
- This source includes parts of code written by the following authors:
-
- Dmitry Shkarin PPMII v.H text compression
- Dmitry Subbotin Carryless rangecoder
- Szymon Stefanek AES encryption
- Brian Gladman AES encryption
- Steve Reid SHA-1 hash function
- Marcus Herbert makefile.unix file
- Tomasz Klim fixes for libunrar.so
- Robert Riebisch makefile.dj and patches for DJGPP
+ This source includes parts of code written by other authors.
+ Please see acknow.txt file for details.
4. Legal stuff
diff --git a/unrar/unrar/recvol.cpp b/unrar/unrar/recvol.cpp
index 6d6f6af..b178207 100644
--- a/unrar/unrar/recvol.cpp
+++ b/unrar/unrar/recvol.cpp
@@ -1,374 +1,111 @@
#include "rar.hpp"
-#define RECVOL_BUFSIZE 0x800
-
-RecVolumes::RecVolumes()
-{
- Buf.Alloc(RECVOL_BUFSIZE*256);
- memset(SrcFile,0,sizeof(SrcFile));
-}
-
-
-RecVolumes::~RecVolumes()
-{
- for (int I=0;I<sizeof(SrcFile)/sizeof(SrcFile[0]);I++)
- delete SrcFile[I];
-}
-
+#include "recvol3.cpp"
+#include "recvol5.cpp"
-bool RecVolumes::Restore(RAROptions *Cmd,const char *Name,
- const wchar *NameW,bool Silent)
+bool RecVolumesRestore(CommandData *Cmd,const wchar *Name,bool Silent)
{
- char ArcName[NM];
- wchar ArcNameW[NM];
- strcpy(ArcName,Name);
- strcpyw(ArcNameW,NameW);
- char *Ext=GetExt(ArcName);
- bool NewStyle=false;
- bool RevName=Ext!=NULL && stricomp(Ext,".rev")==0;
- if (RevName)
+ Archive Arc(Cmd);
+ if (!Arc.Open(Name))
{
- for (int DigitGroup=0;Ext>ArcName && DigitGroup<3;Ext--)
- if (!isdigit(*Ext))
- if (isdigit(*(Ext-1)) && (*Ext=='_' || DigitGroup<2))
- DigitGroup++;
- else
- if (DigitGroup<2)
- {
- NewStyle=true;
- break;
- }
- while (isdigit(*Ext) && Ext>ArcName+1)
- Ext--;
- strcpy(Ext,"*.*");
- FindFile Find;
- Find.SetMask(ArcName);
- struct FindData FD;
- while (Find.Next(&FD))
- {
- Archive Arc(Cmd);
- if (Arc.WOpen(FD.Name,FD.NameW) && Arc.IsArchive(true))
- {
- strcpy(ArcName,FD.Name);
- *ArcNameW=0;
- break;
- }
- }
+ if (!Silent)
+ ErrHandler.OpenErrorMsg(Name);
+ return false;
}
- Archive Arc(Cmd);
- if (!Arc.WCheckOpen(ArcName,ArcNameW))
- return(false);
- if (!Arc.Volume)
+ RARFORMAT Fmt=RARFMT15;
+ if (Arc.IsArchive(true))
+ Fmt=Arc.Format;
+ else
{
-#ifndef SILENT
- Log(ArcName,St(MNotVolume),ArcName);
-#endif
- return(false);
+ byte Sign[REV5_SIGN_SIZE];
+ Arc.Seek(0,SEEK_SET);
+ if (Arc.Read(Sign,REV5_SIGN_SIZE)==REV5_SIGN_SIZE && memcmp(Sign,REV5_SIGN,REV5_SIGN_SIZE)==0)
+ Fmt=RARFMT50;
}
- bool NewNumbering=(Arc.NewMhd.Flags & MHD_NEWNUMBERING);
Arc.Close();
- char *VolNumStart=VolNameToFirstName(ArcName,ArcName,NewNumbering);
- char RecVolMask[NM];
- strcpy(RecVolMask,ArcName);
- int BaseNamePartLength=VolNumStart-ArcName;
- strcpy(RecVolMask+BaseNamePartLength,"*.rev");
-
-#ifndef SILENT
- Int64 RecFileSize=0;
-#endif
-
-#ifndef SILENT
- mprintf(St(MCalcCRCAllVol));
-#endif
- FindFile Find;
- Find.SetMask(RecVolMask);
- struct FindData RecData;
- int FileNumber=0,RecVolNumber=0,FoundRecVolumes=0,MissingVolumes=0;
- char PrevName[NM];
- while (Find.Next(&RecData))
+ // We define RecVol as local variable for proper stack unwinding when
+ // handling exceptions. So it can close and delete files on Cancel.
+ if (Fmt==RARFMT15)
{
- char *Name=RecData.Name;
- int P[3];
- if (!RevName && !NewStyle)
- {
- NewStyle=true;
- char *Dot=GetExt(Name);
- if (Dot!=NULL)
- {
- int LineCount=0;
- Dot--;
- while (Dot>Name && *Dot!='.')
- {
- if (*Dot=='_')
- LineCount++;
- Dot--;
- }
- if (LineCount==2)
- NewStyle=false;
- }
- }
- if (NewStyle)
- {
- File CurFile;
- CurFile.TOpen(Name);
- CurFile.Seek(0,SEEK_END);
- Int64 Length=CurFile.Tell();
- CurFile.Seek(Length-7,SEEK_SET);
- for (int I=0;I<3;I++)
- P[2-I]=CurFile.GetByte()+1;
- uint FileCRC=0;
- for (int I=0;I<4;I++)
- FileCRC|=CurFile.GetByte()<<(I*8);
- if (FileCRC!=CalcFileCRC(&CurFile,Length-4))
- {
-#ifndef SILENT
- mprintf(St(MCRCFailed),Name);
-#endif
- continue;
- }
- }
- else
- {
- char *Dot=GetExt(Name);
- if (Dot==NULL)
- continue;
- bool WrongParam=false;
- for (int I=0;I<sizeof(P)/sizeof(P[0]);I++)
- {
- do
- {
- Dot--;
- } while (isdigit(*Dot) && Dot>=Name+BaseNamePartLength);
- P[I]=atoi(Dot+1);
- if (P[I]==0 || P[I]>255)
- WrongParam=true;
- }
- if (WrongParam)
- continue;
- }
- if (P[1]+P[2]>255)
- continue;
- if (RecVolNumber!=0 && RecVolNumber!=P[1] || FileNumber!=0 && FileNumber!=P[2])
- {
-#ifndef SILENT
- Log(NULL,St(MRecVolDiffSets),Name,PrevName);
-#endif
- return(false);
- }
- RecVolNumber=P[1];
- FileNumber=P[2];
- strcpy(PrevName,Name);
- File *NewFile=new File;
- NewFile->TOpen(Name);
- SrcFile[FileNumber+P[0]-1]=NewFile;
- FoundRecVolumes++;
-#ifndef SILENT
- if (RecFileSize==0)
- RecFileSize=NewFile->FileLength();
-#endif
+ RecVolumes3 RecVol(Cmd,false);
+ return RecVol.Restore(Cmd,Name,Silent);
}
-#ifndef SILENT
- if (!Silent || FoundRecVolumes!=0)
+ else
{
- mprintf(St(MRecVolFound),FoundRecVolumes);
+ RecVolumes5 RecVol(Cmd,false);
+ return RecVol.Restore(Cmd,Name,Silent);
}
-#endif
- if (FoundRecVolumes==0)
- return(false);
-
- bool WriteFlags[256];
- memset(WriteFlags,0,sizeof(WriteFlags));
+}
- char LastVolName[NM];
- *LastVolName=0;
- for (int CurArcNum=0;CurArcNum<FileNumber;CurArcNum++)
+void RecVolumesTest(CommandData *Cmd,Archive *Arc,const wchar *Name)
+{
+ wchar RevName[NM];
+ *RevName=0;
+ if (Arc!=NULL)
{
- Archive *NewFile=new Archive;
- bool ValidVolume=FileExist(ArcName);
- if (ValidVolume)
+ // We received .rar or .exe volume as a parameter, trying to find
+ // the matching .rev file number 1.
+ bool NewNumbering=Arc->NewNumbering;
+
+ wchar ArcName[NM];
+ wcsncpyz(ArcName,Name,ASIZE(ArcName));
+
+ wchar *VolNumStart=VolNameToFirstName(ArcName,ArcName,ASIZE(ArcName),NewNumbering);
+ wchar RecVolMask[NM];
+ wcsncpyz(RecVolMask,ArcName,ASIZE(RecVolMask));
+ size_t BaseNamePartLength=VolNumStart-ArcName;
+ wcsncpyz(RecVolMask+BaseNamePartLength,L"*.rev",ASIZE(RecVolMask)-BaseNamePartLength);
+
+ FindFile Find;
+ Find.SetMask(RecVolMask);
+ FindData RecData;
+
+ while (Find.Next(&RecData))
{
- NewFile->TOpen(ArcName);
- ValidVolume=NewFile->IsArchive(false);
- if (ValidVolume)
- {
- while (NewFile->ReadHeader()!=0)
+ wchar *Num=GetVolNumPart(RecData.Name);
+ if (*Num!='1') // Name must have "0...01" numeric part.
+ continue;
+ bool FirstVol=true;
+ while (--Num>=RecData.Name && IsDigit(*Num))
+ if (*Num!='0')
{
- if (NewFile->GetHeaderType()==ENDARC_HEAD)
- {
- if ((NewFile->EndArcHead.Flags&EARC_DATACRC)!=0 &&
- NewFile->EndArcHead.ArcDataCRC!=CalcFileCRC(NewFile,NewFile->CurBlockPos))
- {
- ValidVolume=false;
-#ifndef SILENT
- mprintf(St(MCRCFailed),ArcName);
-#endif
- }
- break;
- }
- NewFile->SeekToNext();
+ FirstVol=false;
+ break;
}
- }
- if (!ValidVolume)
+ if (FirstVol)
{
- NewFile->Close();
- char NewName[NM];
- strcpy(NewName,ArcName);
- strcat(NewName,".bad");
-#ifndef SILENT
- mprintf(St(MBadArc),ArcName);
- mprintf(St(MRenaming),ArcName,NewName);
-#endif
- rename(ArcName,NewName);
+ wcsncpyz(RevName,RecData.Name,ASIZE(RevName));
+ Name=RevName;
+ break;
}
- NewFile->Seek(0,SEEK_SET);
}
- if (!ValidVolume)
- {
- NewFile->TCreate(ArcName);
- WriteFlags[CurArcNum]=true;
- MissingVolumes++;
-
- if (CurArcNum==FileNumber-1)
- strcpy(LastVolName,ArcName);
-
-#ifndef SILENT
- mprintf(St(MAbsNextVol),ArcName);
-#endif
- }
- SrcFile[CurArcNum]=(File*)NewFile;
- NextVolumeName(ArcName,ArcNameW,ASIZE(ArcName),!NewNumbering);
+ if (*RevName==0) // First .rev file not found.
+ return;
}
-
-#ifndef SILENT
- mprintf(St(MRecVolMissing),MissingVolumes);
-#endif
-
- if (MissingVolumes==0)
+
+ File RevFile;
+ if (!RevFile.Open(Name))
{
-#ifndef SILENT
- mprintf(St(MRecVolAllExist));
-#endif
- return(false);
+ ErrHandler.OpenErrorMsg(Name); // It also sets RARX_OPEN.
+ return;
}
-
- if (MissingVolumes>FoundRecVolumes)
+ mprintf(L"\n");
+ byte Sign[REV5_SIGN_SIZE];
+ bool Rev5=RevFile.Read(Sign,REV5_SIGN_SIZE)==REV5_SIGN_SIZE && memcmp(Sign,REV5_SIGN,REV5_SIGN_SIZE)==0;
+ RevFile.Close();
+ if (Rev5)
{
-#ifndef SILENT
- mprintf(St(MRecVolCannotFix));
-#endif
- return(false);
+ RecVolumes5 RecVol(Cmd,true);
+ RecVol.Test(Cmd,Name);
}
-#ifndef SILENT
- mprintf(St(MReconstructing));
-#endif
-
- RSCoder RSC(RecVolNumber);
-
- int TotalFiles=FileNumber+RecVolNumber;
- int Erasures[256],EraSize=0;
-
- for (int I=0;I<TotalFiles;I++)
- if (WriteFlags[I] || SrcFile[I]==NULL)
- Erasures[EraSize++]=I;
-
-#ifndef SILENT
- Int64 ProcessedSize=0;
-
-/* Modified by Tomas Bzatek - bug in defines */
- int LastPercent=-1;
-#ifndef GUI
- mprintf(" ");
-#endif
-#endif
- int RecCount=0;
-
- while (true)
+ else
{
- if ((++RecCount & 15)==0)
- Wait();
- int MaxRead=0;
- for (int I=0;I<TotalFiles;I++)
- if (WriteFlags[I] || SrcFile[I]==NULL)
- memset(&Buf[I*RECVOL_BUFSIZE],0,RECVOL_BUFSIZE);
- else
- {
- int ReadSize=SrcFile[I]->Read(&Buf[I*RECVOL_BUFSIZE],RECVOL_BUFSIZE);
- if (ReadSize!=RECVOL_BUFSIZE)
- memset(&Buf[I*RECVOL_BUFSIZE+ReadSize],0,RECVOL_BUFSIZE-ReadSize);
- if (ReadSize>MaxRead)
- MaxRead=ReadSize;
- }
- if (MaxRead==0)
- break;
-#ifndef SILENT
- int CurPercent=ToPercent(ProcessedSize,RecFileSize);
- if (!Cmd->DisablePercentage && CurPercent!=LastPercent)
- {
- mprintf("\b\b\b\b%3d%%",CurPercent);
- LastPercent=CurPercent;
- }
- ProcessedSize+=MaxRead;
-#endif
- for (int BufPos=0;BufPos<MaxRead;BufPos++)
- {
- byte Data[256];
- for (int I=0;I<TotalFiles;I++)
- Data[I]=Buf[I*RECVOL_BUFSIZE+BufPos];
- RSC.Decode(Data,TotalFiles,Erasures,EraSize);
- for (int I=0;I<EraSize;I++)
- Buf[Erasures[I]*RECVOL_BUFSIZE+BufPos]=Data[Erasures[I]];
-/*
- for (int I=0;I<FileNumber;I++)
- Buf[I*RECVOL_BUFSIZE+BufPos]=Data[I];
-*/
- }
- for (int I=0;I<FileNumber;I++)
- if (WriteFlags[I])
- SrcFile[I]->Write(&Buf[I*RECVOL_BUFSIZE],MaxRead);
- }
- for (int I=0;I<RecVolNumber+FileNumber;I++)
- if (SrcFile[I]!=NULL)
- {
- File *CurFile=SrcFile[I];
- if (NewStyle && WriteFlags[I])
- {
- Int64 Length=CurFile->Tell();
- CurFile->Seek(Length-7,SEEK_SET);
- for (int J=0;J<7;J++)
- CurFile->PutByte(0);
- }
- CurFile->Close();
- SrcFile[I]=NULL;
- }
- if (*LastVolName)
- {
- Archive Arc(Cmd);
- if (Arc.Open(LastVolName,NULL,false,true) && Arc.IsArchive(true) &&
- Arc.SearchBlock(ENDARC_HEAD))
- {
- Arc.Seek(Arc.NextBlockPos,SEEK_SET);
- char Buf[8192];
- int ReadSize=Arc.Read(Buf,sizeof(Buf));
- int ZeroCount=0;
- while (ZeroCount<ReadSize && Buf[ZeroCount]==0)
- ZeroCount++;
- if (ZeroCount==ReadSize)
- {
- Arc.Seek(Arc.NextBlockPos,SEEK_SET);
- Arc.Truncate();
- }
- }
+ RecVolumes3 RecVol(Cmd,true);
+ RecVol.Test(Cmd,Name);
}
-#if !defined(GUI) && !defined(SILENT)
- if (!Cmd->DisablePercentage)
- mprintf("\b\b\b\b100%%");
- if (!Silent && !Cmd->DisableDone)
- mprintf(St(MDone));
-#endif
- return(true);
}
diff --git a/unrar/unrar/recvol.hpp b/unrar/unrar/recvol.hpp
index 5a0abe5..4a6d663 100644
--- a/unrar/unrar/recvol.hpp
+++ b/unrar/unrar/recvol.hpp
@@ -1,16 +1,88 @@
#ifndef _RAR_RECVOL_
#define _RAR_RECVOL_
-class RecVolumes
+#define REV5_SIGN "Rar!\x1aRev"
+#define REV5_SIGN_SIZE 8
+
+class RecVolumes3
{
private:
File *SrcFile[256];
Array<byte> Buf;
+
+#ifdef RAR_SMP
+ ThreadPool *RSThreadPool;
+#endif
+ public:
+ RecVolumes3(CommandData *Cmd,bool TestOnly);
+ ~RecVolumes3();
+ void Make(CommandData *Cmd,wchar *ArcName);
+ bool Restore(CommandData *Cmd,const wchar *Name,bool Silent);
+ void Test(CommandData *Cmd,const wchar *Name);
+};
+
+
+struct RecVolItem
+{
+ File *f;
+ wchar Name[NM];
+ uint CRC;
+ uint64 FileSize;
+ bool New; // Newly created RAR volume.
+ bool Valid; // If existing RAR volume is valid.
+};
+
+
+class RecVolumes5;
+struct RecRSThreadData
+{
+ RecVolumes5 *RecRSPtr;
+ RSCoder16 *RS;
+ bool Encode;
+ uint DataNum;
+ const byte *Data;
+ size_t StartPos;
+ size_t Size;
+};
+
+class RecVolumes5
+{
+ private:
+ void ProcessRS(CommandData *Cmd,uint DataNum,const byte *Data,uint MaxRead,bool Encode);
+ void ProcessRS(CommandData *Cmd,uint MaxRead,bool Encode);
+ uint ReadHeader(File *RecFile,bool FirstRev);
+
+ Array<RecVolItem> RecItems;
+
+ byte *RealReadBuffer; // Real pointer returned by 'new'.
+ byte *ReadBuffer; // Pointer aligned for SSE instructions.
+
+ byte *RealBuf; // Real pointer returned by 'new'.
+ byte *Buf; // Store ECC or recovered data here, aligned for SSE.
+ size_t RecBufferSize; // Buffer area allocated for single volume.
+
+ uint DataCount; // Number of archives.
+ uint RecCount; // Number of recovery volumes.
+ uint TotalCount; // Total number of archives and recovery volumes.
+
+ bool *ValidFlags; // Volume validity flags for recovering.
+ uint MissingVolumes; // Number of missing or bad RAR volumes.
+
+#ifdef RAR_SMP
+ ThreadPool *RecThreadPool;
+#endif
+ uint MaxUserThreads; // Maximum number of threads defined by user.
+ RecRSThreadData *ThreadData; // Array to store thread parameters.
+ public: // 'public' only because called from thread functions.
+ void ProcessAreaRS(RecRSThreadData *td);
public:
- RecVolumes();
- ~RecVolumes();
- void Make(RAROptions *Cmd,char *ArcName,wchar *ArcNameW);
- bool Restore(RAROptions *Cmd,const char *Name,const wchar *NameW,bool Silent);
+ RecVolumes5(CommandData *Cmd,bool TestOnly);
+ ~RecVolumes5();
+ bool Restore(CommandData *Cmd,const wchar *Name,bool Silent);
+ void Test(CommandData *Cmd,const wchar *Name);
};
+bool RecVolumesRestore(CommandData *Cmd,const wchar *Name,bool Silent);
+void RecVolumesTest(CommandData *Cmd,Archive *Arc,const wchar *Name);
+
#endif
diff --git a/unrar/unrar/recvol3.cpp b/unrar/unrar/recvol3.cpp
new file mode 100644
index 0000000..0138d0f
--- /dev/null
+++ b/unrar/unrar/recvol3.cpp
@@ -0,0 +1,551 @@
+// Buffer size for all volumes involved.
+static const size_t TotalBufferSize=0x4000000;
+
+class RSEncode // Encode or decode data area, one object per one thread.
+{
+ private:
+ RSCoder RSC;
+ public:
+ void EncodeBuf();
+ void DecodeBuf();
+
+ void Init(int RecVolNumber) {RSC.Init(RecVolNumber);}
+ byte *Buf;
+ byte *OutBuf;
+ int BufStart;
+ int BufEnd;
+ int FileNumber;
+ int RecVolNumber;
+ size_t RecBufferSize;
+ int *Erasures;
+ int EraSize;
+};
+
+
+#ifdef RAR_SMP
+THREAD_PROC(RSEncodeThread)
+{
+ RSEncode *rs=(RSEncode *)Data;
+ rs->EncodeBuf();
+}
+
+THREAD_PROC(RSDecodeThread)
+{
+ RSEncode *rs=(RSEncode *)Data;
+ rs->DecodeBuf();
+}
+#endif
+
+RecVolumes3::RecVolumes3(CommandData *Cmd,bool TestOnly)
+{
+ memset(SrcFile,0,sizeof(SrcFile));
+ if (TestOnly)
+ {
+#ifdef RAR_SMP
+ RSThreadPool=NULL;
+#endif
+ }
+ else
+ {
+ Buf.Alloc(TotalBufferSize);
+ memset(SrcFile,0,sizeof(SrcFile));
+#ifdef RAR_SMP
+ RSThreadPool=new ThreadPool(Cmd->Threads);
+#endif
+ }
+}
+
+
+RecVolumes3::~RecVolumes3()
+{
+ for (size_t I=0;I<ASIZE(SrcFile);I++)
+ delete SrcFile[I];
+#ifdef RAR_SMP
+ delete RSThreadPool;
+#endif
+}
+
+
+
+
+void RSEncode::EncodeBuf()
+{
+ for (int BufPos=BufStart;BufPos<BufEnd;BufPos++)
+ {
+ byte Data[256],Code[256];
+ for (int I=0;I<FileNumber;I++)
+ Data[I]=Buf[I*RecBufferSize+BufPos];
+ RSC.Encode(Data,FileNumber,Code);
+ for (int I=0;I<RecVolNumber;I++)
+ OutBuf[I*RecBufferSize+BufPos]=Code[I];
+ }
+}
+
+
+// Check for names like arc5_3_1.rev created by RAR 3.0.
+static bool IsNewStyleRev(const wchar *Name)
+{
+ wchar *Ext=GetExt(Name);
+ if (Ext==NULL)
+ return true;
+ int DigitGroup=0;
+ for (Ext--;Ext>Name;Ext--)
+ if (!IsDigit(*Ext))
+ if (*Ext=='_' && IsDigit(*(Ext-1)))
+ DigitGroup++;
+ else
+ break;
+ return DigitGroup<2;
+}
+
+
+bool RecVolumes3::Restore(CommandData *Cmd,const wchar *Name,bool Silent)
+{
+ wchar ArcName[NM];
+ wcsncpyz(ArcName,Name,ASIZE(ArcName));
+ wchar *Ext=GetExt(ArcName);
+ bool NewStyle=false; // New style .rev volumes are supported since RAR 3.10.
+ bool RevName=Ext!=NULL && wcsicomp(Ext,L".rev")==0;
+ if (RevName)
+ {
+ NewStyle=IsNewStyleRev(ArcName);
+ while (Ext>ArcName+1 && (IsDigit(*(Ext-1)) || *(Ext-1)=='_'))
+ Ext--;
+ wcsncpyz(Ext,L"*.*",ASIZE(ArcName)-(Ext-ArcName));
+
+ FindFile Find;
+ Find.SetMask(ArcName);
+ FindData fd;
+ while (Find.Next(&fd))
+ {
+ Archive Arc(Cmd);
+ if (Arc.WOpen(fd.Name) && Arc.IsArchive(true))
+ {
+ wcsncpyz(ArcName,fd.Name,ASIZE(ArcName));
+ break;
+ }
+ }
+ }
+
+ Archive Arc(Cmd);
+ if (!Arc.WCheckOpen(ArcName))
+ return false;
+ if (!Arc.Volume)
+ {
+ uiMsg(UIERROR_NOTVOLUME,ArcName);
+ return false;
+ }
+ bool NewNumbering=Arc.NewNumbering;
+ Arc.Close();
+
+ wchar *VolNumStart=VolNameToFirstName(ArcName,ArcName,ASIZE(ArcName),NewNumbering);
+ wchar RecVolMask[NM];
+ wcsncpyz(RecVolMask,ArcName,ASIZE(RecVolMask));
+ size_t BaseNamePartLength=VolNumStart-ArcName;
+ wcsncpyz(RecVolMask+BaseNamePartLength,L"*.rev",ASIZE(RecVolMask)-BaseNamePartLength);
+
+ int64 RecFileSize=0;
+
+ // We cannot display "Calculating CRC..." message here, because we do not
+ // know if we'll find any recovery volumes. We'll display it after finding
+ // the first recovery volume.
+ bool CalcCRCMessageDone=false;
+
+ FindFile Find;
+ Find.SetMask(RecVolMask);
+ FindData RecData;
+ int FileNumber=0,RecVolNumber=0,FoundRecVolumes=0,MissingVolumes=0;
+ wchar PrevName[NM];
+ while (Find.Next(&RecData))
+ {
+ wchar *CurName=RecData.Name;
+ int P[3];
+ if (!RevName && !NewStyle)
+ {
+ NewStyle=true;
+
+ wchar *Dot=GetExt(CurName);
+ if (Dot!=NULL)
+ {
+ int LineCount=0;
+ Dot--;
+ while (Dot>CurName && *Dot!='.')
+ {
+ if (*Dot=='_')
+ LineCount++;
+ Dot--;
+ }
+ if (LineCount==2)
+ NewStyle=false;
+ }
+ }
+ if (NewStyle)
+ {
+ if (!CalcCRCMessageDone)
+ {
+ uiMsg(UIMSG_RECVOLCALCCHECKSUM);
+ CalcCRCMessageDone=true;
+ }
+
+ uiMsg(UIMSG_STRING,CurName);
+
+ File CurFile;
+ CurFile.TOpen(CurName);
+ CurFile.Seek(0,SEEK_END);
+ int64 Length=CurFile.Tell();
+ CurFile.Seek(Length-7,SEEK_SET);
+ for (int I=0;I<3;I++)
+ P[2-I]=CurFile.GetByte()+1;
+ uint FileCRC=0;
+ for (int I=0;I<4;I++)
+ FileCRC|=CurFile.GetByte()<<(I*8);
+ uint CalcCRC;
+ CalcFileSum(&CurFile,&CalcCRC,NULL,Cmd->Threads,Length-4);
+ if (FileCRC!=CalcCRC)
+ {
+ uiMsg(UIMSG_CHECKSUM,CurName);
+ continue;
+ }
+ }
+ else
+ {
+ wchar *Dot=GetExt(CurName);
+ if (Dot==NULL)
+ continue;
+ bool WrongParam=false;
+ for (size_t I=0;I<ASIZE(P);I++)
+ {
+ do
+ {
+ Dot--;
+ } while (IsDigit(*Dot) && Dot>=CurName+BaseNamePartLength);
+ P[I]=atoiw(Dot+1);
+ if (P[I]==0 || P[I]>255)
+ WrongParam=true;
+ }
+ if (WrongParam)
+ continue;
+ }
+ if (P[0]<=0 || P[1]<=0 || P[2]<=0 || P[1]+P[2]>255 || P[0]+P[2]-1>255)
+ continue;
+ if (RecVolNumber!=0 && RecVolNumber!=P[1] || FileNumber!=0 && FileNumber!=P[2])
+ {
+ uiMsg(UIERROR_RECVOLDIFFSETS,CurName,PrevName);
+ return false;
+ }
+ RecVolNumber=P[1];
+ FileNumber=P[2];
+ wcsncpyz(PrevName,CurName,ASIZE(PrevName));
+ File *NewFile=new File;
+ NewFile->TOpen(CurName);
+
+ // This check is redundant taking into account P[I]>255 and P[0]+P[2]-1>255
+ // checks above. Still we keep it here for better clarity and security.
+ int SrcPos=FileNumber+P[0]-1;
+ if (SrcPos<0 || SrcPos>=ASIZE(SrcFile))
+ continue;
+ SrcFile[SrcPos]=NewFile;
+
+ FoundRecVolumes++;
+
+ if (RecFileSize==0)
+ RecFileSize=NewFile->FileLength();
+ }
+ if (!Silent || FoundRecVolumes!=0)
+ uiMsg(UIMSG_RECVOLFOUND,FoundRecVolumes);
+ if (FoundRecVolumes==0)
+ return false;
+
+ bool WriteFlags[256];
+ memset(WriteFlags,0,sizeof(WriteFlags));
+
+ wchar LastVolName[NM];
+ *LastVolName=0;
+
+ for (int CurArcNum=0;CurArcNum<FileNumber;CurArcNum++)
+ {
+ Archive *NewFile=new Archive(Cmd);
+ bool ValidVolume=FileExist(ArcName);
+ if (ValidVolume)
+ {
+ NewFile->TOpen(ArcName);
+ ValidVolume=NewFile->IsArchive(false);
+ if (ValidVolume)
+ {
+ while (NewFile->ReadHeader()!=0)
+ {
+ if (NewFile->GetHeaderType()==HEAD_ENDARC)
+ {
+ uiMsg(UIMSG_STRING,ArcName);
+
+ if (NewFile->EndArcHead.DataCRC)
+ {
+ uint CalcCRC;
+ CalcFileSum(NewFile,&CalcCRC,NULL,Cmd->Threads,NewFile->CurBlockPos);
+ if (NewFile->EndArcHead.ArcDataCRC!=CalcCRC)
+ {
+ ValidVolume=false;
+ uiMsg(UIMSG_CHECKSUM,ArcName);
+ }
+ }
+ break;
+ }
+ NewFile->SeekToNext();
+ }
+ }
+ if (!ValidVolume)
+ {
+ NewFile->Close();
+ wchar NewName[NM];
+ wcsncpyz(NewName,ArcName,ASIZE(NewName));
+ wcsncatz(NewName,L".bad",ASIZE(NewName));
+
+ uiMsg(UIMSG_BADARCHIVE,ArcName);
+ uiMsg(UIMSG_RENAMING,ArcName,NewName);
+ RenameFile(ArcName,NewName);
+ }
+ NewFile->Seek(0,SEEK_SET);
+ }
+ if (!ValidVolume)
+ {
+ // It is important to return 'false' instead of aborting here,
+ // so if we are called from extraction, we will be able to continue
+ // extracting. It may happen if .rar and .rev are on read-only disks
+ // like CDs.
+ if (!NewFile->Create(ArcName,FMF_WRITE|FMF_SHAREREAD))
+ {
+ // We need to display the title of operation before the error message,
+ // to make clear for user that create error is related to recovery
+ // volumes. This is why we cannot use WCreate call here. Title must be
+ // before create error, not after that.
+
+ uiMsg(UIERROR_RECVOLFOUND,FoundRecVolumes); // Intentionally not displayed in console mode.
+ uiMsg(UIERROR_RECONSTRUCTING);
+ ErrHandler.CreateErrorMsg(ArcName);
+ return false;
+ }
+
+ WriteFlags[CurArcNum]=true;
+ MissingVolumes++;
+
+ if (CurArcNum==FileNumber-1)
+ wcsncpyz(LastVolName,ArcName,ASIZE(LastVolName));
+
+ uiMsg(UIMSG_MISSINGVOL,ArcName);
+ uiMsg(UIEVENT_NEWARCHIVE,ArcName);
+ }
+ SrcFile[CurArcNum]=(File*)NewFile;
+ NextVolumeName(ArcName,ASIZE(ArcName),!NewNumbering);
+ }
+
+ uiMsg(UIMSG_RECVOLMISSING,MissingVolumes);
+
+ if (MissingVolumes==0)
+ {
+ uiMsg(UIERROR_RECVOLALLEXIST);
+ return false;
+ }
+
+ if (MissingVolumes>FoundRecVolumes)
+ {
+ uiMsg(UIERROR_RECVOLFOUND,FoundRecVolumes); // Intentionally not displayed in console mode.
+ uiMsg(UIERROR_RECVOLCANNOTFIX);
+ return false;
+ }
+
+ uiMsg(UIMSG_RECONSTRUCTING);
+
+ int TotalFiles=FileNumber+RecVolNumber;
+ int Erasures[256],EraSize=0;
+
+ for (int I=0;I<TotalFiles;I++)
+ if (WriteFlags[I] || SrcFile[I]==NULL)
+ Erasures[EraSize++]=I;
+
+ int64 ProcessedSize=0;
+ int LastPercent=-1;
+ mprintf(L" ");
+ // Size of per file buffer.
+ size_t RecBufferSize=TotalBufferSize/TotalFiles;
+
+#ifdef RAR_SMP
+ uint ThreadNumber=Cmd->Threads;
+#else
+ uint ThreadNumber=1;
+#endif
+ RSEncode *rse=new RSEncode[ThreadNumber];
+ for (uint I=0;I<ThreadNumber;I++)
+ rse[I].Init(RecVolNumber);
+
+ while (true)
+ {
+ Wait();
+ int MaxRead=0;
+ for (int I=0;I<TotalFiles;I++)
+ if (WriteFlags[I] || SrcFile[I]==NULL)
+ memset(&Buf[I*RecBufferSize],0,RecBufferSize);
+ else
+ {
+ int ReadSize=SrcFile[I]->Read(&Buf[I*RecBufferSize],RecBufferSize);
+ if ((size_t)ReadSize!=RecBufferSize)
+ memset(&Buf[I*RecBufferSize+ReadSize],0,RecBufferSize-ReadSize);
+ if (ReadSize>MaxRead)
+ MaxRead=ReadSize;
+ }
+ if (MaxRead==0)
+ break;
+
+ int CurPercent=ToPercent(ProcessedSize,RecFileSize);
+ if (!Cmd->DisablePercentage && CurPercent!=LastPercent)
+ {
+ uiProcessProgress("RC",ProcessedSize,RecFileSize);
+ LastPercent=CurPercent;
+ }
+ ProcessedSize+=MaxRead;
+
+ int BlockStart=0;
+ int BlockSize=MaxRead/ThreadNumber;
+ if (BlockSize<0x100)
+ BlockSize=MaxRead;
+
+ for (uint CurThread=0;BlockStart<MaxRead;CurThread++)
+ {
+ // Last thread processes all left data including increasement
+ // from rounding error.
+ if (CurThread==ThreadNumber-1)
+ BlockSize=MaxRead-BlockStart;
+
+ RSEncode *curenc=rse+CurThread;
+ curenc->Buf=&Buf[0];
+ curenc->BufStart=BlockStart;
+ curenc->BufEnd=BlockStart+BlockSize;
+ curenc->FileNumber=TotalFiles;
+ curenc->RecBufferSize=RecBufferSize;
+ curenc->Erasures=Erasures;
+ curenc->EraSize=EraSize;
+
+#ifdef RAR_SMP
+ if (ThreadNumber>1)
+ RSThreadPool->AddTask(RSDecodeThread,(void*)curenc);
+ else
+ curenc->DecodeBuf();
+#else
+ curenc->DecodeBuf();
+#endif
+
+ BlockStart+=BlockSize;
+ }
+
+#ifdef RAR_SMP
+ RSThreadPool->WaitDone();
+#endif // RAR_SMP
+
+ for (int I=0;I<FileNumber;I++)
+ if (WriteFlags[I])
+ SrcFile[I]->Write(&Buf[I*RecBufferSize],MaxRead);
+ }
+ delete[] rse;
+
+ for (int I=0;I<RecVolNumber+FileNumber;I++)
+ if (SrcFile[I]!=NULL)
+ {
+ File *CurFile=SrcFile[I];
+ if (NewStyle && WriteFlags[I])
+ {
+ int64 Length=CurFile->Tell();
+ CurFile->Seek(Length-7,SEEK_SET);
+ for (int J=0;J<7;J++)
+ CurFile->PutByte(0);
+ }
+ CurFile->Close();
+ SrcFile[I]=NULL;
+ }
+ if (*LastVolName!=0)
+ {
+ // Truncate the last volume to its real size.
+ Archive Arc(Cmd);
+ if (Arc.Open(LastVolName,FMF_UPDATE) && Arc.IsArchive(true) &&
+ Arc.SearchBlock(HEAD_ENDARC))
+ {
+ Arc.Seek(Arc.NextBlockPos,SEEK_SET);
+ char Buf[8192];
+ int ReadSize=Arc.Read(Buf,sizeof(Buf));
+ int ZeroCount=0;
+ while (ZeroCount<ReadSize && Buf[ZeroCount]==0)
+ ZeroCount++;
+ if (ZeroCount==ReadSize)
+ {
+ Arc.Seek(Arc.NextBlockPos,SEEK_SET);
+ Arc.Truncate();
+ }
+ }
+ }
+#if !defined(SILENT)
+ if (!Cmd->DisablePercentage)
+ mprintf(L"\b\b\b\b100%%");
+ if (!Silent && !Cmd->DisableDone)
+ mprintf(St(MDone));
+#endif
+ return true;
+}
+
+
+void RSEncode::DecodeBuf()
+{
+ for (int BufPos=BufStart;BufPos<BufEnd;BufPos++)
+ {
+ byte Data[256];
+ for (int I=0;I<FileNumber;I++)
+ Data[I]=Buf[I*RecBufferSize+BufPos];
+ RSC.Decode(Data,FileNumber,Erasures,EraSize);
+ for (int I=0;I<EraSize;I++)
+ Buf[Erasures[I]*RecBufferSize+BufPos]=Data[Erasures[I]];
+ }
+}
+
+
+void RecVolumes3::Test(CommandData *Cmd,const wchar *Name)
+{
+ if (!IsNewStyleRev(Name)) // RAR 3.0 name#_#_#.rev do not include CRC32.
+ {
+ ErrHandler.UnknownMethodMsg(Name,Name);
+ return;
+ }
+
+ wchar VolName[NM];
+ wcsncpyz(VolName,Name,ASIZE(VolName));
+
+ while (FileExist(VolName))
+ {
+ File CurFile;
+ if (!CurFile.Open(VolName))
+ {
+ ErrHandler.OpenErrorMsg(VolName); // It also sets RARX_OPEN.
+ continue;
+ }
+ if (!uiStartFileExtract(VolName,false,true,false))
+ return;
+ mprintf(St(MExtrTestFile),VolName);
+ mprintf(L" ");
+ CurFile.Seek(0,SEEK_END);
+ int64 Length=CurFile.Tell();
+ CurFile.Seek(Length-4,SEEK_SET);
+ uint FileCRC=0;
+ for (int I=0;I<4;I++)
+ FileCRC|=CurFile.GetByte()<<(I*8);
+
+ uint CalcCRC;
+ CalcFileSum(&CurFile,&CalcCRC,NULL,1,Length-4,Cmd->DisablePercentage ? 0 : CALCFSUM_SHOWPROGRESS);
+ if (FileCRC==CalcCRC)
+ {
+ mprintf(L"%s%s ",L"\b\b\b\b\b ",St(MOk));
+ }
+ else
+ {
+ uiMsg(UIERROR_CHECKSUM,VolName,VolName);
+ ErrHandler.SetErrorCode(RARX_CRC);
+ }
+
+ NextVolumeName(VolName,ASIZE(VolName),false);
+ }
+}
diff --git a/unrar/unrar/recvol5.cpp b/unrar/unrar/recvol5.cpp
new file mode 100644
index 0000000..2d9c947
--- /dev/null
+++ b/unrar/unrar/recvol5.cpp
@@ -0,0 +1,538 @@
+static const uint MaxVolumes=65535;
+
+// We select this limit arbitrarily, to prevent user creating too many
+// rev files by mistake.
+#define MAX_REV_TO_DATA_RATIO 10 // 1000% of rev files.
+
+RecVolumes5::RecVolumes5(CommandData *Cmd,bool TestOnly)
+{
+ RealBuf=NULL;
+ RealReadBuffer=NULL;
+
+ DataCount=0;
+ RecCount=0;
+ TotalCount=0;
+ RecBufferSize=0;
+
+#ifdef RAR_SMP
+ MaxUserThreads=Cmd->Threads;
+#else
+ MaxUserThreads=1;
+#endif
+
+ ThreadData=new RecRSThreadData[MaxUserThreads];
+ for (uint I=0;I<MaxUserThreads;I++)
+ {
+ ThreadData[I].RecRSPtr=this;
+ ThreadData[I].RS=NULL;
+ }
+
+ if (TestOnly)
+ {
+#ifdef RAR_SMP
+ RecThreadPool=NULL;
+#endif
+ }
+ else
+ {
+#ifdef RAR_SMP
+ RecThreadPool=new ThreadPool(MaxUserThreads);
+#endif
+ RealBuf=new byte[TotalBufferSize+SSE_ALIGNMENT];
+ Buf=(byte *)ALIGN_VALUE(RealBuf,SSE_ALIGNMENT);
+ }
+}
+
+
+RecVolumes5::~RecVolumes5()
+{
+ delete[] RealBuf;
+ delete[] RealReadBuffer;
+ for (uint I=0;I<RecItems.Size();I++)
+ delete RecItems[I].f;
+ for (uint I=0;I<MaxUserThreads;I++)
+ delete ThreadData[I].RS;
+ delete[] ThreadData;
+#ifdef RAR_SMP
+ delete RecThreadPool;
+#endif
+}
+
+
+
+
+#ifdef RAR_SMP
+THREAD_PROC(RecThreadRS)
+{
+ RecRSThreadData *td=(RecRSThreadData *)Data;
+ td->RecRSPtr->ProcessAreaRS(td);
+}
+#endif
+
+
+void RecVolumes5::ProcessRS(CommandData *Cmd,uint DataNum,const byte *Data,uint MaxRead,bool Encode)
+{
+/*
+ RSCoder16 RS;
+ RS.Init(DataCount,RecCount,Encode ? NULL:ValidFlags);
+ uint Count=Encode ? RecCount : MissingVolumes;
+ for (uint I=0;I<Count;I++)
+ RS.UpdateECC(DataNum, I, Data, Buf+I*RecBufferSize, MaxRead);
+*/
+
+ uint ThreadNumber=MaxUserThreads;
+
+ const uint MinThreadBlock=0x1000;
+ ThreadNumber=Min(ThreadNumber,MaxRead/MinThreadBlock);
+
+ if (ThreadNumber<1)
+ ThreadNumber=1;
+ uint ThreadDataSize=MaxRead/ThreadNumber;
+ ThreadDataSize+=(ThreadDataSize&1); // Must be even for 16-bit RS coder.
+#ifdef USE_SSE
+ ThreadDataSize=ALIGN_VALUE(ThreadDataSize,SSE_ALIGNMENT); // Alignment for SSE operations.
+#endif
+ if (ThreadDataSize<MinThreadBlock)
+ ThreadDataSize=MinThreadBlock;
+
+ for (size_t I=0,CurPos=0;I<ThreadNumber && CurPos<MaxRead;I++)
+ {
+ RecRSThreadData *td=ThreadData+I;
+ if (td->RS==NULL)
+ {
+ td->RS=new RSCoder16;
+ td->RS->Init(DataCount,RecCount,Encode ? NULL:ValidFlags);
+ }
+ td->DataNum=DataNum;
+ td->Data=Data;
+ td->Encode=Encode;
+ td->StartPos=CurPos;
+
+ size_t EndPos=CurPos+ThreadDataSize;
+ if (EndPos>MaxRead || I==ThreadNumber-1)
+ EndPos=MaxRead;
+
+ td->Size=EndPos-CurPos;
+
+ CurPos=EndPos;
+
+#ifdef RAR_SMP
+ if (ThreadNumber>1)
+ RecThreadPool->AddTask(RecThreadRS,(void*)td);
+ else
+ ProcessAreaRS(td);
+#else
+ ProcessAreaRS(td);
+#endif
+ }
+#ifdef RAR_SMP
+ RecThreadPool->WaitDone();
+#endif // RAR_SMP
+}
+
+
+void RecVolumes5::ProcessAreaRS(RecRSThreadData *td)
+{
+ uint Count=td->Encode ? RecCount : MissingVolumes;
+ for (uint I=0;I<Count;I++)
+ td->RS->UpdateECC(td->DataNum, I, td->Data+td->StartPos, Buf+I*RecBufferSize+td->StartPos, td->Size);
+}
+
+
+
+
+bool RecVolumes5::Restore(CommandData *Cmd,const wchar *Name,bool Silent)
+{
+ wchar ArcName[NM];
+ wcsncpyz(ArcName,Name,ASIZE(ArcName));
+
+ wchar *Num=GetVolNumPart(ArcName);
+ while (Num>ArcName && IsDigit(*(Num-1)))
+ Num--;
+ if (Num<=PointToName(ArcName))
+ return false; // Numeric part is missing or entire volume name is numeric, not possible for RAR or REV volume.
+ wcsncpyz(Num,L"*.*",ASIZE(ArcName)-(Num-ArcName));
+
+ wchar FirstVolName[NM];
+ *FirstVolName=0;
+
+ wchar LongestRevName[NM];
+ *LongestRevName=0;
+
+ int64 RecFileSize=0;
+
+ FindFile VolFind;
+ VolFind.SetMask(ArcName);
+ FindData fd;
+ uint FoundRecVolumes=0;
+ while (VolFind.Next(&fd))
+ {
+ Wait();
+
+ Archive *Vol=new Archive(Cmd);
+ int ItemPos=-1;
+ if (!fd.IsDir && Vol->WOpen(fd.Name))
+ {
+ if (CmpExt(fd.Name,L"rev"))
+ {
+ uint RecNum=ReadHeader(Vol,FoundRecVolumes==0);
+ if (RecNum!=0)
+ {
+ if (FoundRecVolumes==0)
+ RecFileSize=Vol->FileLength();
+
+ ItemPos=RecNum;
+ FoundRecVolumes++;
+
+ if (wcslen(fd.Name)>wcslen(LongestRevName))
+ wcsncpyz(LongestRevName,fd.Name,ASIZE(LongestRevName));
+ }
+ }
+ else
+ if (Vol->IsArchive(true) && (Vol->SFXSize>0 || CmpExt(fd.Name,L"rar")))
+ {
+ if (!Vol->Volume && !Vol->BrokenHeader)
+ {
+ uiMsg(UIERROR_NOTVOLUME,ArcName);
+ return false;
+ }
+ // We work with archive as with raw data file, so we do not want
+ // to spend time to QOpen I/O redirection.
+ Vol->QOpenUnload();
+
+ Vol->Seek(0,SEEK_SET);
+
+ // RAR volume found. Get its number, store the handle in appropriate
+ // array slot, clean slots in between if we had to grow the array.
+ wchar *Num=GetVolNumPart(fd.Name);
+ uint VolNum=0;
+ for (uint K=1;Num>=fd.Name && IsDigit(*Num);K*=10,Num--)
+ VolNum+=(*Num-'0')*K;
+ if (VolNum==0 || VolNum>MaxVolumes)
+ continue;
+ size_t CurSize=RecItems.Size();
+ if (VolNum>CurSize)
+ {
+ RecItems.Alloc(VolNum);
+ for (size_t I=CurSize;I<VolNum;I++)
+ RecItems[I].f=NULL;
+ }
+ ItemPos=VolNum-1;
+
+ if (*FirstVolName==0)
+ VolNameToFirstName(fd.Name,FirstVolName,ASIZE(FirstVolName),true);
+ }
+ }
+ if (ItemPos==-1)
+ delete Vol; // Skip found file, it is not RAR or REV volume.
+ else
+ if ((uint)ItemPos<RecItems.Size()) // Check if found more REV than needed.
+ {
+ // Store found RAR or REV volume.
+ RecVolItem *Item=RecItems+ItemPos;
+ Item->f=Vol;
+ Item->New=false;
+ wcsncpyz(Item->Name,fd.Name,ASIZE(Item->Name));
+ }
+ }
+
+ if (!Silent || FoundRecVolumes!=0)
+ uiMsg(UIMSG_RECVOLFOUND,FoundRecVolumes);
+ if (FoundRecVolumes==0)
+ return false;
+
+ // If we did not find even a single .rar volume, create .rar volume name
+ // based on the longest .rev file name. Use longest .rev, so we have
+ // enough space for volume number.
+ if (*FirstVolName==0)
+ {
+ SetExt(LongestRevName,L"rar",ASIZE(LongestRevName));
+ VolNameToFirstName(LongestRevName,FirstVolName,ASIZE(FirstVolName),true);
+ }
+
+ uiMsg(UIMSG_RECVOLCALCCHECKSUM);
+
+ MissingVolumes=0;
+ for (uint I=0;I<TotalCount;I++)
+ {
+ RecVolItem *Item=&RecItems[I];
+ if (Item->f!=NULL)
+ {
+ uiMsg(UIMSG_STRING,Item->Name);
+
+ uint RevCRC;
+ CalcFileSum(Item->f,&RevCRC,NULL,MaxUserThreads,INT64NDF,CALCFSUM_CURPOS);
+ Item->Valid=RevCRC==Item->CRC;
+ if (!Item->Valid)
+ {
+ uiMsg(UIMSG_CHECKSUM,Item->Name);
+
+ // Close only corrupt REV volumes here. We'll close and rename corrupt
+ // RAR volumes later, if we'll know that recovery is possible.
+ if (I>=DataCount)
+ {
+ Item->f->Close();
+ Item->f=NULL;
+ FoundRecVolumes--;
+ }
+ }
+ }
+ if (I<DataCount && (Item->f==NULL || !Item->Valid))
+ MissingVolumes++;
+ }
+
+ uiMsg(UIMSG_RECVOLMISSING,MissingVolumes);
+
+ if (MissingVolumes==0)
+ {
+ uiMsg(UIERROR_RECVOLALLEXIST);
+ return false;
+ }
+
+ if (MissingVolumes>FoundRecVolumes)
+ {
+ uiMsg(UIERROR_RECVOLFOUND,FoundRecVolumes); // Intentionally not displayed in console mode.
+ uiMsg(UIERROR_RECVOLCANNOTFIX);
+ return false;
+ }
+
+ uiMsg(UIMSG_RECONSTRUCTING);
+
+ // Create missing and rename bad volumes.
+ uint64 MaxVolSize=0;
+ for (uint I=0;I<DataCount;I++)
+ {
+ RecVolItem *Item=&RecItems[I];
+ if (Item->FileSize>MaxVolSize)
+ MaxVolSize=Item->FileSize;
+ if (Item->f!=NULL && !Item->Valid)
+ {
+ Item->f->Close();
+
+ wchar NewName[NM];
+ wcsncpyz(NewName,Item->Name,ASIZE(NewName));
+ wcsncatz(NewName,L".bad",ASIZE(NewName));
+
+ uiMsg(UIMSG_BADARCHIVE,Item->Name);
+ uiMsg(UIMSG_RENAMING,Item->Name,NewName);
+ RenameFile(Item->Name,NewName);
+ delete Item->f;
+ Item->f=NULL;
+ }
+
+ if ((Item->New=(Item->f==NULL))==true)
+ {
+ wcsncpyz(Item->Name,FirstVolName,ASIZE(Item->Name));
+ uiMsg(UIMSG_CREATING,Item->Name);
+ uiMsg(UIEVENT_NEWARCHIVE,Item->Name);
+ File *NewVol=new File;
+ bool UserReject;
+ if (!FileCreate(Cmd,NewVol,Item->Name,ASIZE(Item->Name),&UserReject))
+ {
+ if (!UserReject)
+ ErrHandler.CreateErrorMsg(Item->Name);
+ ErrHandler.Exit(UserReject ? RARX_USERBREAK:RARX_CREATE);
+ }
+ NewVol->Prealloc(Item->FileSize);
+ Item->f=NewVol;
+ }
+ NextVolumeName(FirstVolName,ASIZE(FirstVolName),false);
+ }
+
+
+ int64 ProcessedSize=0;
+ int LastPercent=-1;
+ mprintf(L" ");
+
+ // Even though we already preliminary calculated missing volume number,
+ // let's do it again now, when we have the final and exact information.
+ MissingVolumes=0;
+
+ ValidFlags=new bool[TotalCount];
+ for (uint I=0;I<TotalCount;I++)
+ {
+ ValidFlags[I]=RecItems[I].f!=NULL && !RecItems[I].New;
+ if (I<DataCount && !ValidFlags[I])
+ MissingVolumes++;
+ }
+
+ // Size of per file buffer.
+ RecBufferSize=TotalBufferSize/MissingVolumes;
+ if ((RecBufferSize&1)==1) // Must be even for our RS16 codec.
+ RecBufferSize--;
+#ifdef USE_SSE
+ RecBufferSize&=~(SSE_ALIGNMENT-1); // Align for SSE.
+#endif
+
+ RSCoder16 RS;
+ if (!RS.Init(DataCount,RecCount,ValidFlags))
+ {
+ uiMsg(UIERROR_OPFAILED);
+ delete[] ValidFlags;
+ return false; // Should not happen, we check parameter validity above.
+ }
+
+ RealReadBuffer=new byte[RecBufferSize+SSE_ALIGNMENT];
+ byte *ReadBuf=(byte *)ALIGN_VALUE(RealReadBuffer,SSE_ALIGNMENT);
+
+ while (true)
+ {
+ Wait();
+
+ int MaxRead=0;
+ for (uint I=0,J=DataCount;I<DataCount;I++)
+ {
+ uint VolNum=I;
+ if (!ValidFlags[I]) // If next RAR volume is missing or invalid.
+ {
+ while (!ValidFlags[J]) // Find next valid REV volume.
+ J++;
+ VolNum=J++; // Use next valid REV volume data instead of RAR.
+ }
+ RecVolItem *Item=RecItems+VolNum;
+
+ byte *B=&ReadBuf[0];
+ int ReadSize=0;
+ if (Item->f!=NULL && !Item->New)
+ ReadSize=Item->f->Read(B,RecBufferSize);
+ if (ReadSize!=RecBufferSize)
+ memset(B+ReadSize,0,RecBufferSize-ReadSize);
+ if (ReadSize>MaxRead)
+ MaxRead=ReadSize;
+
+ // We can have volumes of different size. Let's use data chunk
+ // for largest volume size.
+ uint DataToProcess=(uint)Min(RecBufferSize,MaxVolSize-ProcessedSize);
+ ProcessRS(Cmd,I,B,DataToProcess,false);
+ }
+ if (MaxRead==0)
+ break;
+
+ for (uint I=0,J=0;I<DataCount;I++)
+ if (!ValidFlags[I])
+ {
+ RecVolItem *Item=RecItems+I;
+ size_t WriteSize=(size_t)Min(MaxRead,Item->FileSize);
+ Item->f->Write(Buf+(J++)*RecBufferSize,WriteSize);
+ Item->FileSize-=WriteSize;
+ }
+
+ int CurPercent=ToPercent(ProcessedSize,RecFileSize);
+ if (!Cmd->DisablePercentage && CurPercent!=LastPercent)
+ {
+ uiProcessProgress("RV",ProcessedSize,RecFileSize);
+ LastPercent=CurPercent;
+ }
+ ProcessedSize+=MaxRead;
+ }
+
+ for (uint I=0;I<TotalCount;I++)
+ if (RecItems[I].f!=NULL)
+ RecItems[I].f->Close();
+
+ delete[] ValidFlags;
+#if !defined(SILENT)
+ if (!Cmd->DisablePercentage)
+ mprintf(L"\b\b\b\b100%%");
+ if (!Silent && !Cmd->DisableDone)
+ mprintf(St(MDone));
+#endif
+ return true;
+}
+
+
+uint RecVolumes5::ReadHeader(File *RecFile,bool FirstRev)
+{
+ const size_t FirstReadSize=REV5_SIGN_SIZE+8;
+ byte ShortBuf[FirstReadSize];
+ if (RecFile->Read(ShortBuf,FirstReadSize)!=FirstReadSize)
+ return 0;
+ if (memcmp(ShortBuf,REV5_SIGN,REV5_SIGN_SIZE)!=0)
+ return 0;
+ uint HeaderSize=RawGet4(ShortBuf+REV5_SIGN_SIZE+4);
+ if (HeaderSize>0x100000 || HeaderSize<=5)
+ return 0;
+ uint BlockCRC=RawGet4(ShortBuf+REV5_SIGN_SIZE);
+
+ RawRead Raw(RecFile);
+ if (Raw.Read(HeaderSize)!=HeaderSize)
+ return 0;
+
+ // Calculate CRC32 of entire header including 4 byte size field.
+ uint CalcCRC=CRC32(0xffffffff,ShortBuf+REV5_SIGN_SIZE+4,4);
+ if ((CRC32(CalcCRC,Raw.GetDataPtr(),HeaderSize)^0xffffffff)!=BlockCRC)
+ return 0;
+
+ if (Raw.Get1()!=1) // Version check.
+ return 0;
+ DataCount=Raw.Get2();
+ RecCount=Raw.Get2();
+ TotalCount=DataCount+RecCount;
+ uint RecNum=Raw.Get2(); // Number of recovery volume.
+ if (RecNum>=TotalCount || TotalCount>MaxVolumes)
+ return 0;
+ uint RevCRC=Raw.Get4(); // CRC of current REV volume.
+
+ if (FirstRev)
+ {
+ // If we have read the first valid REV file, init data structures
+ // using information from REV header.
+ size_t CurSize=RecItems.Size();
+ RecItems.Alloc(TotalCount);
+ for (size_t I=CurSize;I<TotalCount;I++)
+ RecItems[I].f=NULL;
+ for (uint I=0;I<DataCount;I++)
+ {
+ RecItems[I].FileSize=Raw.Get8();
+ RecItems[I].CRC=Raw.Get4();
+ }
+ }
+
+ RecItems[RecNum].CRC=RevCRC; // Assign it here, after allocating RecItems.
+
+ return RecNum;
+}
+
+
+void RecVolumes5::Test(CommandData *Cmd,const wchar *Name)
+{
+ wchar VolName[NM];
+ wcsncpyz(VolName,Name,ASIZE(VolName));
+
+ uint FoundRecVolumes=0;
+ while (FileExist(VolName))
+ {
+ File CurFile;
+ if (!CurFile.Open(VolName))
+ {
+ ErrHandler.OpenErrorMsg(VolName); // It also sets RARX_OPEN.
+ continue;
+ }
+ if (!uiStartFileExtract(VolName,false,true,false))
+ return;
+ mprintf(St(MExtrTestFile),VolName);
+ mprintf(L" ");
+ bool Valid=false;
+ uint RecNum=ReadHeader(&CurFile,FoundRecVolumes==0);
+ if (RecNum!=0)
+ {
+ FoundRecVolumes++;
+
+ uint RevCRC;
+ CalcFileSum(&CurFile,&RevCRC,NULL,1,INT64NDF,CALCFSUM_CURPOS|(Cmd->DisablePercentage ? 0 : CALCFSUM_SHOWPROGRESS));
+ Valid=RevCRC==RecItems[RecNum].CRC;
+ }
+
+ if (Valid)
+ {
+ mprintf(L"%s%s ",L"\b\b\b\b\b ",St(MOk));
+ }
+ else
+ {
+ uiMsg(UIERROR_CHECKSUM,VolName,VolName);
+ ErrHandler.SetErrorCode(RARX_CRC);
+ }
+
+ NextVolumeName(VolName,ASIZE(VolName),false);
+ }
+}
diff --git a/unrar/unrar/resource.cpp b/unrar/unrar/resource.cpp
index f2a4657..dadd072 100644
--- a/unrar/unrar/resource.cpp
+++ b/unrar/unrar/resource.cpp
@@ -2,11 +2,21 @@
-#if !defined(SILENT) || !defined(RARDLL)
-const char *St(MSGID StringId)
+
+
+#ifndef RARDLL
+const wchar* St(MSGID StringId)
{
- return(StringId);
+ return StringId;
}
-#endif
+// Needed for Unix swprintf to convert %s to %ls in legacy language resources.
+const wchar *StF(MSGID StringId)
+{
+ static wchar FormattedStr[512];
+ PrintfPrepareFmt(St(StringId),FormattedStr,ASIZE(FormattedStr));
+ return FormattedStr;
+}
+#endif
+
diff --git a/unrar/unrar/resource.hpp b/unrar/unrar/resource.hpp
index 581b34b..62c5bf4 100644
--- a/unrar/unrar/resource.hpp
+++ b/unrar/unrar/resource.hpp
@@ -1,14 +1,13 @@
#ifndef _RAR_RESOURCE_
#define _RAR_RESOURCE_
-#if defined(SILENT) && defined(RARDLL)
-#define St(x) ("")
+#ifdef RARDLL
+#define St(x) (L"")
+#define StF(x) (L"")
#else
-const char *St(MSGID StringId);
+const wchar *St(MSGID StringId);
+const wchar *StF(MSGID StringId);
#endif
-inline const char *StT(MSGID StringId) {return(St(StringId));}
-
-
#endif
diff --git a/unrar/unrar/rijndael.cpp b/unrar/unrar/rijndael.cpp
index d0d71b9..02c4d14 100644
--- a/unrar/unrar/rijndael.cpp
+++ b/unrar/unrar/rijndael.cpp
@@ -1,30 +1,51 @@
/**************************************************************************
- * This code is based on Szymon Stefanek AES implementation: *
- * http://www.esat.kuleuven.ac.be/~rijmen/rijndael/rijndael-cpplib.tar.gz *
- * *
- * Dynamic tables generation is based on the Brian Gladman work: *
- * http://fp.gladman.plus.com/cryptography_technology/rijndael *
+ * This code is based on Szymon Stefanek public domain AES implementation *
**************************************************************************/
#include "rar.hpp"
-const int uKeyLenInBytes=16, m_uRounds=10;
+#ifdef USE_SSE
+#include <wmmintrin.h>
+#endif
+
+static byte S[256]=
+{
+ 99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, 118,
+ 202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175, 156, 164, 114, 192,
+ 183, 253, 147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113, 216, 49, 21,
+ 4, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226, 235, 39, 178, 117,
+ 9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, 179, 41, 227, 47, 132,
+ 83, 209, 0, 237, 32, 252, 177, 91, 106, 203, 190, 57, 74, 76, 88, 207,
+ 208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168,
+ 81, 163, 64, 143, 146, 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210,
+ 205, 12, 19, 236, 95, 151, 68, 23, 196, 167, 126, 61, 100, 93, 25, 115,
+ 96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94, 11, 219,
+ 224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121,
+ 231, 200, 55, 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174, 8,
+ 186, 120, 37, 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139, 138,
+ 112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158,
+ 225, 248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223,
+ 140, 161, 137, 13, 191, 230, 66, 104, 65, 153, 45, 15, 176, 84, 187, 22
+};
+
+static byte S5[256];
+
+// Round constants. 10 items are used by AES-128, 8 by AES-192, 7 by AES-256.
+static byte rcon[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x1b,0x36};
-static byte S[256],S5[256],rcon[30];
static byte T1[256][4],T2[256][4],T3[256][4],T4[256][4];
static byte T5[256][4],T6[256][4],T7[256][4],T8[256][4];
static byte U1[256][4],U2[256][4],U3[256][4],U4[256][4];
-
-inline void Xor128(byte *dest,const byte *arg1,const byte *arg2)
+inline void Xor128(void *dest,const void *arg1,const void *arg2)
{
-#if defined(PRESENT_INT32) && defined(ALLOW_NOT_ALIGNED_INT)
+#ifdef ALLOW_MISALIGNED
((uint32*)dest)[0]=((uint32*)arg1)[0]^((uint32*)arg2)[0];
((uint32*)dest)[1]=((uint32*)arg1)[1]^((uint32*)arg2)[1];
((uint32*)dest)[2]=((uint32*)arg1)[2]^((uint32*)arg2)[2];
((uint32*)dest)[3]=((uint32*)arg1)[3]^((uint32*)arg2)[3];
#else
for (int I=0;I<16;I++)
- dest[I]=arg1[I]^arg2[I];
+ ((byte*)dest)[I]=((byte*)arg1)[I]^((byte*)arg2)[I];
#endif
}
@@ -32,7 +53,7 @@ inline void Xor128(byte *dest,const byte *arg1,const byte *arg2)
inline void Xor128(byte *dest,const byte *arg1,const byte *arg2,
const byte *arg3,const byte *arg4)
{
-#if defined(PRESENT_INT32) && defined(ALLOW_NOT_ALIGNED_INT)
+#ifdef ALLOW_MISALIGNED
(*(uint32*)dest)=(*(uint32*)arg1)^(*(uint32*)arg2)^(*(uint32*)arg3)^(*(uint32*)arg4);
#else
for (int I=0;I<4;I++)
@@ -43,7 +64,7 @@ inline void Xor128(byte *dest,const byte *arg1,const byte *arg2,
inline void Copy128(byte *dest,const byte *src)
{
-#if defined(PRESENT_INT32) && defined(ALLOW_NOT_ALIGNED_INT)
+#ifdef ALLOW_MISALIGNED
((uint32*)dest)[0]=((uint32*)src)[0];
((uint32*)dest)[1]=((uint32*)src)[1];
((uint32*)dest)[2]=((uint32*)src)[2];
@@ -61,59 +82,349 @@ inline void Copy128(byte *dest,const byte *src)
Rijndael::Rijndael()
{
- if (S[0]==0)
+ if (S5[0]==0)
GenerateTables();
+ CBCMode = true; // Always true for RAR.
}
-void Rijndael::init(Direction dir,const byte * key,byte * initVector)
+void Rijndael::Init(bool Encrypt,const byte *key,uint keyLen,const byte * initVector)
{
- m_direction = dir;
+ // Check SIMD here instead of constructor, so if object is a part of some
+ // structure memset'ed before use, these variables are not lost.
+#if defined(USE_SSE)
+ int CPUInfo[4];
+ __cpuid(CPUInfo, 0);
+ if (CPUInfo[0]>=1) // Check the maximum supported cpuid function.
+ {
+ __cpuid(CPUInfo, 1);
+ AES_NI=(CPUInfo[2] & 0x2000000)!=0;
+ }
+ else
+ AES_NI=false;
+#elif defined(USE_NEON)
+ AES_Neon=(getauxval(AT_HWCAP) & HWCAP_AES)!=0;
+#endif
+
+ // Other developers asked us to initialize it to suppress "may be used
+ // uninitialized" warning in code below in some compilers.
+ uint uKeyLenInBytes=0;
+
+ switch(keyLen)
+ {
+ case 128:
+ uKeyLenInBytes = 16;
+ m_uRounds = 10;
+ break;
+ case 192:
+ uKeyLenInBytes = 24;
+ m_uRounds = 12;
+ break;
+ case 256:
+ uKeyLenInBytes = 32;
+ m_uRounds = 14;
+ break;
+ }
byte keyMatrix[_MAX_KEY_COLUMNS][4];
- for(uint i = 0;i < uKeyLenInBytes;i++)
+ for(uint i = 0; i < uKeyLenInBytes; i++)
keyMatrix[i >> 2][i & 3] = key[i];
- for(int i = 0;i < MAX_IV_SIZE;i++)
- m_initVector[i] = initVector[i];
+ if (initVector==NULL)
+ memset(m_initVector, 0, sizeof(m_initVector));
+ else
+ for(int i = 0; i < MAX_IV_SIZE; i++)
+ m_initVector[i] = initVector[i];
keySched(keyMatrix);
- if(m_direction == Decrypt)
+ if(!Encrypt)
keyEncToDec();
}
+void Rijndael::blockEncrypt(const byte *input,size_t inputLen,byte *outBuffer)
+{
+ if (inputLen <= 0)
+ return;
+
+ size_t numBlocks = inputLen/16;
+#if defined(USE_SSE)
+ if (AES_NI)
+ {
+ blockEncryptSSE(input,numBlocks,outBuffer);
+ return;
+ }
+#elif defined(USE_NEON)
+ if (AES_Neon)
+ {
+ blockEncryptNeon(input,numBlocks,outBuffer);
+ return;
+ }
+#endif
+
+ byte *prevBlock = m_initVector;
+ for(size_t i = numBlocks;i > 0;i--)
+ {
+ byte block[16];
+ if (CBCMode)
+ Xor128(block,prevBlock,input);
+ else
+ Copy128(block,input);
+
+ byte temp[4][4];
+
+ Xor128(temp,block,m_expandedKey[0]);
+ Xor128(outBuffer, T1[temp[0][0]],T2[temp[1][1]],T3[temp[2][2]],T4[temp[3][3]]);
+ Xor128(outBuffer+4, T1[temp[1][0]],T2[temp[2][1]],T3[temp[3][2]],T4[temp[0][3]]);
+ Xor128(outBuffer+8, T1[temp[2][0]],T2[temp[3][1]],T3[temp[0][2]],T4[temp[1][3]]);
+ Xor128(outBuffer+12,T1[temp[3][0]],T2[temp[0][1]],T3[temp[1][2]],T4[temp[2][3]]);
+
+ for(int r = 1; r < m_uRounds-1; r++)
+ {
+ Xor128(temp,outBuffer,m_expandedKey[r]);
+ Xor128(outBuffer, T1[temp[0][0]],T2[temp[1][1]],T3[temp[2][2]],T4[temp[3][3]]);
+ Xor128(outBuffer+4, T1[temp[1][0]],T2[temp[2][1]],T3[temp[3][2]],T4[temp[0][3]]);
+ Xor128(outBuffer+8, T1[temp[2][0]],T2[temp[3][1]],T3[temp[0][2]],T4[temp[1][3]]);
+ Xor128(outBuffer+12,T1[temp[3][0]],T2[temp[0][1]],T3[temp[1][2]],T4[temp[2][3]]);
+ }
+ Xor128(temp,outBuffer,m_expandedKey[m_uRounds-1]);
+ outBuffer[ 0] = T1[temp[0][0]][1];
+ outBuffer[ 1] = T1[temp[1][1]][1];
+ outBuffer[ 2] = T1[temp[2][2]][1];
+ outBuffer[ 3] = T1[temp[3][3]][1];
+ outBuffer[ 4] = T1[temp[1][0]][1];
+ outBuffer[ 5] = T1[temp[2][1]][1];
+ outBuffer[ 6] = T1[temp[3][2]][1];
+ outBuffer[ 7] = T1[temp[0][3]][1];
+ outBuffer[ 8] = T1[temp[2][0]][1];
+ outBuffer[ 9] = T1[temp[3][1]][1];
+ outBuffer[10] = T1[temp[0][2]][1];
+ outBuffer[11] = T1[temp[1][3]][1];
+ outBuffer[12] = T1[temp[3][0]][1];
+ outBuffer[13] = T1[temp[0][1]][1];
+ outBuffer[14] = T1[temp[1][2]][1];
+ outBuffer[15] = T1[temp[2][3]][1];
+ Xor128(outBuffer,outBuffer,m_expandedKey[m_uRounds]);
+ prevBlock=outBuffer;
+
+ outBuffer += 16;
+ input += 16;
+ }
+ Copy128(m_initVector,prevBlock);
+}
+
+
+#ifdef USE_SSE
+void Rijndael::blockEncryptSSE(const byte *input,size_t numBlocks,byte *outBuffer)
+{
+ __m128i v = _mm_loadu_si128((__m128i*)m_initVector);
+ __m128i *src=(__m128i*)input;
+ __m128i *dest=(__m128i*)outBuffer;
+ __m128i *rkey=(__m128i*)m_expandedKey;
+ while (numBlocks > 0)
+ {
+ __m128i d = _mm_loadu_si128(src++);
+ if (CBCMode)
+ v = _mm_xor_si128(v, d);
+ else
+ v = d;
+ __m128i r0 = _mm_loadu_si128(rkey);
+ v = _mm_xor_si128(v, r0);
+
+ for (int i=1; i<m_uRounds; i++)
+ {
+ __m128i ri = _mm_loadu_si128(rkey + i);
+ v = _mm_aesenc_si128(v, ri);
+ }
+
+ __m128i rl = _mm_loadu_si128(rkey + m_uRounds);
+ v = _mm_aesenclast_si128(v, rl);
+ _mm_storeu_si128(dest++,v);
+ numBlocks--;
+ }
+ _mm_storeu_si128((__m128i*)m_initVector,v);
+}
+#endif
+
+
+#ifdef USE_NEON
+void Rijndael::blockEncryptNeon(const byte *input,size_t numBlocks,byte *outBuffer)
+{
+ byte *prevBlock = m_initVector;
+ while (numBlocks > 0)
+ {
+ byte block[16];
+ if (CBCMode)
+ vst1q_u8(block, veorq_u8(vld1q_u8(prevBlock), vld1q_u8(input)));
+ else
+ vst1q_u8(block, vld1q_u8(input));
+
+ uint8x16_t data = vld1q_u8(block);
+ for (uint i = 0; i < m_uRounds-1; i++)
+ {
+ data = vaeseq_u8(data, vld1q_u8((byte *)m_expandedKey[i]));
+ data = vaesmcq_u8(data);
+ }
+ data = vaeseq_u8(data, vld1q_u8((byte *)(m_expandedKey[m_uRounds-1])));
+ data = veorq_u8(data, vld1q_u8((byte *)(m_expandedKey[m_uRounds])));
+ vst1q_u8(outBuffer, data);
+
+ prevBlock=outBuffer;
+
+ outBuffer += 16;
+ input += 16;
+ numBlocks--;
+ }
+ vst1q_u8(m_initVector, vld1q_u8(prevBlock));
+ return;
+}
+#endif
+
-int Rijndael::blockDecrypt(const byte *input, int inputLen, byte *outBuffer)
+void Rijndael::blockDecrypt(const byte *input, size_t inputLen, byte *outBuffer)
{
- if (input == 0 || inputLen <= 0)
- return 0;
+ if (inputLen <= 0)
+ return;
+
+ size_t numBlocks=inputLen/16;
+#if defined(USE_SSE)
+ if (AES_NI)
+ {
+ blockDecryptSSE(input,numBlocks,outBuffer);
+ return;
+ }
+#elif defined(USE_NEON)
+ if (AES_Neon)
+ {
+ blockDecryptNeon(input,numBlocks,outBuffer);
+ return;
+ }
+#endif
byte block[16], iv[4][4];
memcpy(iv,m_initVector,16);
- int numBlocks=inputLen/16;
- for (int i = numBlocks; i > 0; i--)
+ for (size_t i = numBlocks; i > 0; i--)
{
- decrypt(input, block);
- Xor128(block,block,(byte*)iv);
-#if STRICT_ALIGN
- memcpy(iv, input, 16);
- memcpy(outBuf, block, 16);
-#else
+ byte temp[4][4];
+
+ Xor128(temp,input,m_expandedKey[m_uRounds]);
+
+ Xor128(block, T5[temp[0][0]],T6[temp[3][1]],T7[temp[2][2]],T8[temp[1][3]]);
+ Xor128(block+4, T5[temp[1][0]],T6[temp[0][1]],T7[temp[3][2]],T8[temp[2][3]]);
+ Xor128(block+8, T5[temp[2][0]],T6[temp[1][1]],T7[temp[0][2]],T8[temp[3][3]]);
+ Xor128(block+12,T5[temp[3][0]],T6[temp[2][1]],T7[temp[1][2]],T8[temp[0][3]]);
+
+ for(int r = m_uRounds-1; r > 1; r--)
+ {
+ Xor128(temp,block,m_expandedKey[r]);
+ Xor128(block, T5[temp[0][0]],T6[temp[3][1]],T7[temp[2][2]],T8[temp[1][3]]);
+ Xor128(block+4, T5[temp[1][0]],T6[temp[0][1]],T7[temp[3][2]],T8[temp[2][3]]);
+ Xor128(block+8, T5[temp[2][0]],T6[temp[1][1]],T7[temp[0][2]],T8[temp[3][3]]);
+ Xor128(block+12,T5[temp[3][0]],T6[temp[2][1]],T7[temp[1][2]],T8[temp[0][3]]);
+ }
+
+ Xor128(temp,block,m_expandedKey[1]);
+ block[ 0] = S5[temp[0][0]];
+ block[ 1] = S5[temp[3][1]];
+ block[ 2] = S5[temp[2][2]];
+ block[ 3] = S5[temp[1][3]];
+ block[ 4] = S5[temp[1][0]];
+ block[ 5] = S5[temp[0][1]];
+ block[ 6] = S5[temp[3][2]];
+ block[ 7] = S5[temp[2][3]];
+ block[ 8] = S5[temp[2][0]];
+ block[ 9] = S5[temp[1][1]];
+ block[10] = S5[temp[0][2]];
+ block[11] = S5[temp[3][3]];
+ block[12] = S5[temp[3][0]];
+ block[13] = S5[temp[2][1]];
+ block[14] = S5[temp[1][2]];
+ block[15] = S5[temp[0][3]];
+ Xor128(block,block,m_expandedKey[0]);
+
+ if (CBCMode)
+ Xor128(block,block,iv);
+
Copy128((byte*)iv,input);
Copy128(outBuffer,block);
+
+ input += 16;
+ outBuffer += 16;
+ }
+
+ memcpy(m_initVector,iv,16);
+}
+
+
+#ifdef USE_SSE
+void Rijndael::blockDecryptSSE(const byte *input, size_t numBlocks, byte *outBuffer)
+{
+ __m128i initVector = _mm_loadu_si128((__m128i*)m_initVector);
+ __m128i *src=(__m128i*)input;
+ __m128i *dest=(__m128i*)outBuffer;
+ __m128i *rkey=(__m128i*)m_expandedKey;
+ while (numBlocks > 0)
+ {
+ __m128i rl = _mm_loadu_si128(rkey + m_uRounds);
+ __m128i d = _mm_loadu_si128(src++);
+ __m128i v = _mm_xor_si128(rl, d);
+
+ for (int i=m_uRounds-1; i>0; i--)
+ {
+ __m128i ri = _mm_loadu_si128(rkey + i);
+ v = _mm_aesdec_si128(v, ri);
+ }
+
+ __m128i r0 = _mm_loadu_si128(rkey);
+ v = _mm_aesdeclast_si128(v, r0);
+
+ if (CBCMode)
+ v = _mm_xor_si128(v, initVector);
+ initVector = d;
+ _mm_storeu_si128(dest++,v);
+ numBlocks--;
+ }
+ _mm_storeu_si128((__m128i*)m_initVector,initVector);
+}
#endif
+
+
+#ifdef USE_NEON
+void Rijndael::blockDecryptNeon(const byte *input, size_t numBlocks, byte *outBuffer)
+{
+ byte iv[16];
+ memcpy(iv,m_initVector,16);
+
+ while (numBlocks > 0)
+ {
+ uint8x16_t data = vld1q_u8(input);
+
+ for (int i=m_uRounds-1; i>0; i--)
+ {
+ data = vaesdq_u8(data, vld1q_u8((byte *)m_expandedKey[i+1]));
+ data = vaesimcq_u8(data);
+ }
+
+ data = vaesdq_u8(data, vld1q_u8((byte *)m_expandedKey[1]));
+ data = veorq_u8(data, vld1q_u8((byte *)m_expandedKey[0]));
+
+ if (CBCMode)
+ data = veorq_u8(data, vld1q_u8(iv));
+
+ vst1q_u8(iv, vld1q_u8(input));
+ vst1q_u8(outBuffer, data);
+
input += 16;
outBuffer += 16;
+ numBlocks--;
}
memcpy(m_initVector,iv,16);
-
- return 16*numBlocks;
}
+#endif
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -197,8 +508,8 @@ void Rijndael::keyEncToDec()
for(int r = 1; r < m_uRounds; r++)
{
byte n_expandedKey[4][4];
- for (int i=0;i<4;i++)
- for (int j=0;j<4;j++)
+ for (int i = 0; i < 4; i++)
+ for (int j = 0; j < 4; j++)
{
byte *w=m_expandedKey[r][j];
n_expandedKey[j][i]=U1[w[0]][i]^U2[w[1]][i]^U3[w[2]][i]^U4[w[3]][i];
@@ -208,91 +519,89 @@ void Rijndael::keyEncToDec()
}
-void Rijndael::decrypt(const byte a[16], byte b[16])
+static byte gmul(byte a, byte b) // Galois field "peasant's algorithm" multiplication.
{
- int r;
- byte temp[4][4];
-
- Xor128((byte*)temp,(byte*)a,(byte*)m_expandedKey[m_uRounds]);
-
- Xor128(b, T5[temp[0][0]],T6[temp[3][1]],T7[temp[2][2]],T8[temp[1][3]]);
- Xor128(b+4, T5[temp[1][0]],T6[temp[0][1]],T7[temp[3][2]],T8[temp[2][3]]);
- Xor128(b+8, T5[temp[2][0]],T6[temp[1][1]],T7[temp[0][2]],T8[temp[3][3]]);
- Xor128(b+12,T5[temp[3][0]],T6[temp[2][1]],T7[temp[1][2]],T8[temp[0][3]]);
-
- for(r = m_uRounds-1; r > 1; r--)
+ const byte poly=0x1b; // Lower byte of AES 0x11b irreducible polynomial.
+ byte result = 0;
+ while (b>0)
{
- Xor128((byte*)temp,(byte*)b,(byte*)m_expandedKey[r]);
- Xor128(b, T5[temp[0][0]],T6[temp[3][1]],T7[temp[2][2]],T8[temp[1][3]]);
- Xor128(b+4, T5[temp[1][0]],T6[temp[0][1]],T7[temp[3][2]],T8[temp[2][3]]);
- Xor128(b+8, T5[temp[2][0]],T6[temp[1][1]],T7[temp[0][2]],T8[temp[3][3]]);
- Xor128(b+12,T5[temp[3][0]],T6[temp[2][1]],T7[temp[1][2]],T8[temp[0][3]]);
+ if ((b & 1) != 0)
+ result ^= a;
+ a = (a & 0x80) ? (a<<1)^poly : a<<1;
+ b >>= 1;
}
-
- Xor128((byte*)temp,(byte*)b,(byte*)m_expandedKey[1]);
- b[ 0] = S5[temp[0][0]];
- b[ 1] = S5[temp[3][1]];
- b[ 2] = S5[temp[2][2]];
- b[ 3] = S5[temp[1][3]];
- b[ 4] = S5[temp[1][0]];
- b[ 5] = S5[temp[0][1]];
- b[ 6] = S5[temp[3][2]];
- b[ 7] = S5[temp[2][3]];
- b[ 8] = S5[temp[2][0]];
- b[ 9] = S5[temp[1][1]];
- b[10] = S5[temp[0][2]];
- b[11] = S5[temp[3][3]];
- b[12] = S5[temp[3][0]];
- b[13] = S5[temp[2][1]];
- b[14] = S5[temp[1][2]];
- b[15] = S5[temp[0][3]];
- Xor128((byte*)b,(byte*)b,(byte*)m_expandedKey[0]);
+ return result;
}
-#define ff_poly 0x011b
-#define ff_hi 0x80
-#define FFinv(x) ((x) ? pow[255 - log[x]]: 0)
+// 2021-09-24: changed to slower and simpler code without interim tables.
+// It is still fast enough for our purpose.
+void Rijndael::GenerateTables()
+{
+ for (int I=0;I<256;I++)
+ S5[S[I]]=I;
+
+ for (int I=0;I<256;I++)
+ {
+ byte s=S[I];
+ T1[I][1]=T1[I][2]=T2[I][2]=T2[I][3]=T3[I][0]=T3[I][3]=T4[I][0]=T4[I][1]=s;
+ T1[I][0]=T2[I][1]=T3[I][2]=T4[I][3]=gmul(s,2);
+ T1[I][3]=T2[I][0]=T3[I][1]=T4[I][2]=gmul(s,3);
+
+ byte b=S5[I];
+ U1[b][3]=U2[b][0]=U3[b][1]=U4[b][2]=T5[I][3]=T6[I][0]=T7[I][1]=T8[I][2]=gmul(b,0xb);
+ U1[b][1]=U2[b][2]=U3[b][3]=U4[b][0]=T5[I][1]=T6[I][2]=T7[I][3]=T8[I][0]=gmul(b,0x9);
+ U1[b][2]=U2[b][3]=U3[b][0]=U4[b][1]=T5[I][2]=T6[I][3]=T7[I][0]=T8[I][1]=gmul(b,0xd);
+ U1[b][0]=U2[b][1]=U3[b][2]=U4[b][3]=T5[I][0]=T6[I][1]=T7[I][2]=T8[I][3]=gmul(b,0xe);
+ }
+}
-#define FFmul02(x) (x ? pow[log[x] + 0x19] : 0)
-#define FFmul03(x) (x ? pow[log[x] + 0x01] : 0)
-#define FFmul09(x) (x ? pow[log[x] + 0xc7] : 0)
-#define FFmul0b(x) (x ? pow[log[x] + 0x68] : 0)
-#define FFmul0d(x) (x ? pow[log[x] + 0xee] : 0)
-#define FFmul0e(x) (x ? pow[log[x] + 0xdf] : 0)
-#define fwd_affine(x) \
- (w = (uint)x, w ^= (w<<1)^(w<<2)^(w<<3)^(w<<4), (byte)(0x63^(w^(w>>8))))
-#define inv_affine(x) \
- (w = (uint)x, w = (w<<1)^(w<<3)^(w<<6), (byte)(0x05^(w^(w>>8))))
+#if 0
+static void TestRijndael();
+struct TestRij {TestRij() {TestRijndael();exit(0);}} GlobalTestRij;
-void Rijndael::GenerateTables()
+// Test CBC encryption according to NIST 800-38A.
+void TestRijndael()
{
- unsigned char pow[512],log[256];
- int i = 0, w = 1;
- do
- {
- pow[i] = (byte)w;
- pow[i + 255] = (byte)w;
- log[w] = (byte)i++;
- w ^= (w << 1) ^ (w & ff_hi ? ff_poly : 0);
- } while (w != 1);
-
- for (int i = 0,w = 1; i < sizeof(rcon)/sizeof(rcon[0]); i++)
+ byte IV[16]={0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f};
+ byte PT[64]={
+ 0x6b,0xc1,0xbe,0xe2,0x2e,0x40,0x9f,0x96,0xe9,0x3d,0x7e,0x11,0x73,0x93,0x17,0x2a,
+ 0xae,0x2d,0x8a,0x57,0x1e,0x03,0xac,0x9c,0x9e,0xb7,0x6f,0xac,0x45,0xaf,0x8e,0x51,
+ 0x30,0xc8,0x1c,0x46,0xa3,0x5c,0xe4,0x11,0xe5,0xfb,0xc1,0x19,0x1a,0x0a,0x52,0xef,
+ 0xf6,0x9f,0x24,0x45,0xdf,0x4f,0x9b,0x17,0xad,0x2b,0x41,0x7b,0xe6,0x6c,0x37,0x10,
+ };
+
+ byte Key128[16]={0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6,0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c};
+ byte Chk128[16]={0x3f,0xf1,0xca,0xa1,0x68,0x1f,0xac,0x09,0x12,0x0e,0xca,0x30,0x75,0x86,0xe1,0xa7};
+ byte Key192[24]={0x8e,0x73,0xb0,0xf7,0xda,0x0e,0x64,0x52,0xc8,0x10,0xf3,0x2b,0x80,0x90,0x79,0xe5,0x62,0xf8,0xea,0xd2,0x52,0x2c,0x6b,0x7b};
+ byte Chk192[16]={0x08,0xb0,0xe2,0x79,0x88,0x59,0x88,0x81,0xd9,0x20,0xa9,0xe6,0x4f,0x56,0x15,0xcd};
+ byte Key256[32]={0x60,0x3d,0xeb,0x10,0x15,0xca,0x71,0xbe,0x2b,0x73,0xae,0xf0,0x85,0x7d,0x77,0x81,0x1f,0x35,0x2c,0x07,0x3b,0x61,0x08,0xd7,0x2d,0x98,0x10,0xa3,0x09,0x14,0xdf,0xf4};
+ byte Chk256[16]={0xb2,0xeb,0x05,0xe2,0xc3,0x9b,0xe9,0xfc,0xda,0x6c,0x19,0x07,0x8c,0x6a,0x9d,0x1b};
+ byte *Key[3]={Key128,Key192,Key256};
+ byte *Chk[3]={Chk128,Chk192,Chk256};
+
+ Rijndael rij; // Declare outside of loop to test re-initialization.
+ for (uint L=0;L<3;L++)
{
- rcon[i] = w;
- w = (w << 1) ^ (w & ff_hi ? ff_poly : 0);
- }
- for(int i = 0; i < 256; ++i)
- {
- unsigned char b=S[i]=fwd_affine(FFinv((byte)i));
- T1[i][1]=T1[i][2]=T2[i][2]=T2[i][3]=T3[i][0]=T3[i][3]=T4[i][0]=T4[i][1]=b;
- T1[i][0]=T2[i][1]=T3[i][2]=T4[i][3]=FFmul02(b);
- T1[i][3]=T2[i][0]=T3[i][1]=T4[i][2]=FFmul03(b);
- S5[i] = b = FFinv(inv_affine((byte)i));
- U1[b][3]=U2[b][0]=U3[b][1]=U4[b][2]=T5[i][3]=T6[i][0]=T7[i][1]=T8[i][2]=FFmul0b(b);
- U1[b][1]=U2[b][2]=U3[b][3]=U4[b][0]=T5[i][1]=T6[i][2]=T7[i][3]=T8[i][0]=FFmul09(b);
- U1[b][2]=U2[b][3]=U3[b][0]=U4[b][1]=T5[i][2]=T6[i][3]=T7[i][0]=T8[i][1]=FFmul0d(b);
- U1[b][0]=U2[b][1]=U3[b][2]=U4[b][3]=T5[i][0]=T6[i][1]=T7[i][2]=T8[i][3]=FFmul0e(b);
+ byte Out[16];
+ wchar Str[sizeof(Out)*2+1];
+
+ uint KeyLength=128+L*64;
+ rij.Init(true,Key[L],KeyLength,IV);
+ for (uint I=0;I<sizeof(PT);I+=16)
+ rij.blockEncrypt(PT+I,16,Out);
+ BinToHex(Chk[L],16,NULL,Str,ASIZE(Str));
+ mprintf(L"\nAES-%d expected: %s",KeyLength,Str);
+ BinToHex(Out,sizeof(Out),NULL,Str,ASIZE(Str));
+ mprintf(L"\nAES-%d result: %s",KeyLength,Str);
+ if (memcmp(Out,Chk[L],16)==0)
+ mprintf(L" OK");
+ else
+ {
+ mprintf(L" FAILED");
+ getchar();
+ }
}
}
+#endif
diff --git a/unrar/unrar/rijndael.hpp b/unrar/unrar/rijndael.hpp
index 6a8edde..96e1d0d 100644
--- a/unrar/unrar/rijndael.hpp
+++ b/unrar/unrar/rijndael.hpp
@@ -2,11 +2,7 @@
#define _RIJNDAEL_H_
/**************************************************************************
- * This code is based on Szymon Stefanek AES implementation: *
- * http://www.esat.kuleuven.ac.be/~rijmen/rijndael/rijndael-cpplib.tar.gz *
- * *
- * Dynamic tables generation is based on the Brian Gladman's work: *
- * http://fp.gladman.plus.com/cryptography_technology/rijndael *
+ * This code is based on Szymon Stefanek public domain AES implementation *
**************************************************************************/
#define _MAX_KEY_COLUMNS (256/32)
@@ -14,24 +10,41 @@
#define MAX_IV_SIZE 16
class Rijndael
-{
- public:
- enum Direction { Encrypt , Decrypt };
+{
private:
+#ifdef USE_SSE
+ void blockEncryptSSE(const byte *input,size_t numBlocks,byte *outBuffer);
+ void blockDecryptSSE(const byte *input, size_t numBlocks, byte *outBuffer);
+
+ bool AES_NI;
+#endif
+#ifdef USE_NEON
+ // Set "crypto" attribute as replacement of -march=armv8-a+crypto switch.
+ __attribute__((target("crypto")))
+ void blockEncryptNeon(const byte *input,size_t numBlocks,byte *outBuffer);
+ __attribute__((target("crypto")))
+ void blockDecryptNeon(const byte *input, size_t numBlocks, byte *outBuffer);
+
+ bool AES_Neon;
+#endif
+
void keySched(byte key[_MAX_KEY_COLUMNS][4]);
void keyEncToDec();
- void encrypt(const byte a[16], byte b[16]);
- void decrypt(const byte a[16], byte b[16]);
void GenerateTables();
- Direction m_direction;
+ // RAR always uses CBC, but we may need to turn it off when calling
+ // this code from other archive formats with CTR and other modes.
+ bool CBCMode;
+
+ int m_uRounds;
byte m_initVector[MAX_IV_SIZE];
byte m_expandedKey[_MAX_ROUNDS+1][4][4];
public:
Rijndael();
- void init(Direction dir,const byte *key,byte *initVector);
- int blockEncrypt(const byte *input, int inputLen, byte *outBuffer);
- int blockDecrypt(const byte *input, int inputLen, byte *outBuffer);
+ void Init(bool Encrypt,const byte *key,uint keyLen,const byte *initVector);
+ void blockEncrypt(const byte *input, size_t inputLen, byte *outBuffer);
+ void blockDecrypt(const byte *input, size_t inputLen, byte *outBuffer);
+ void SetCBCMode(bool Mode) {CBCMode=Mode;}
};
-
+
#endif // _RIJNDAEL_H_
diff --git a/unrar/unrar/rs.cpp b/unrar/unrar/rs.cpp
index 9eed7a2..10ccc6d 100644
--- a/unrar/unrar/rs.cpp
+++ b/unrar/unrar/rs.cpp
@@ -2,53 +2,65 @@
#define Clean(D,S) {for (int I=0;I<(S);I++) (D)[I]=0;}
-RSCoder::RSCoder(int ParSize)
+void RSCoder::Init(int ParSize)
{
- RSCoder::ParSize=ParSize;
+ RSCoder::ParSize=ParSize; // Store the number of recovery volumes.
FirstBlockDone=false;
gfInit();
pnInit();
}
+// Initialize logarithms and exponents Galois field tables.
void RSCoder::gfInit()
{
for (int I=0,J=1;I<MAXPAR;I++)
{
gfLog[J]=I;
gfExp[I]=J;
- if ((J<<=1)&256)
- J^=285;
+ J<<=1;
+ if (J > MAXPAR)
+ J^=0x11D; // 0x11D field-generator polynomial (x^8+x^4+x^3+x^2+1).
}
- for (int I=MAXPAR;I<MAXPOL;I++)
+ for (int I=MAXPAR;I<MAXPOL;I++) // Avoid gfExp overflow check.
gfExp[I]=gfExp[I-MAXPAR];
}
+// Multiplication over Galois field.
inline int RSCoder::gfMult(int a,int b)
{
return(a==0 || b == 0 ? 0:gfExp[gfLog[a]+gfLog[b]]);
}
+// Create the generator polynomial g(x).
+// g(x)=(x-a)(x-a^2)(x-a^3)..(x-a^N)
void RSCoder::pnInit()
{
- int p1[MAXPAR+1],p2[MAXPAR+1];
+ int p2[MAXPAR+1]; // Currently calculated part of g(x).
Clean(p2,ParSize);
- p2[0]=1;
+ p2[0]=1; // Set p2 polynomial to 1.
+
for (int I=1;I<=ParSize;I++)
{
+ int p1[MAXPAR+1]; // We use p1 as current (x+a^i) expression.
Clean(p1,ParSize);
p1[0]=gfExp[I];
- p1[1]=1;
+ p1[1]=1; // Set p1 polynomial to x+a^i.
+
+ // Multiply the already calucated part of g(x) to next (x+a^i).
pnMult(p1,p2,GXPol);
+
+ // p2=g(x).
for (int J=0;J<ParSize;J++)
p2[J]=GXPol[J];
}
}
+// Multiply polynomial 'p1' to 'p2' and store the result in 'r'.
void RSCoder::pnMult(int *p1,int *p2,int *r)
{
Clean(r,ParSize);
@@ -61,12 +73,14 @@ void RSCoder::pnMult(int *p1,int *p2,int *r)
void RSCoder::Encode(byte *Data,int DataSize,byte *DestData)
{
- int ShiftReg[MAXPAR+1];
+ int ShiftReg[MAXPAR+1]; // Linear Feedback Shift Register.
Clean(ShiftReg,ParSize+1);
for (int I=0;I<DataSize;I++)
{
int D=Data[I]^ShiftReg[ParSize-1];
+
+ // Use g(x) to define feedback taps.
for (int J=ParSize-1;J>0;J--)
ShiftReg[J]=ShiftReg[J-1]^gfMult(GXPol[J],D);
ShiftReg[0]=gfMult(GXPol[0],D);
@@ -78,66 +92,69 @@ void RSCoder::Encode(byte *Data,int DataSize,byte *DestData)
bool RSCoder::Decode(byte *Data,int DataSize,int *EraLoc,int EraSize)
{
- int SynData[MAXPOL];
+ int SynData[MAXPOL]; // Syndrome data.
+
bool AllZeroes=true;
for (int I=0;I<ParSize;I++)
{
- int Sum=Data[0],J=1,Exp=gfExp[I+1];
- for (;J+8<=DataSize;J+=8)
- {
- Sum=Data[J]^gfMult(Exp,Sum);
- Sum=Data[J+1]^gfMult(Exp,Sum);
- Sum=Data[J+2]^gfMult(Exp,Sum);
- Sum=Data[J+3]^gfMult(Exp,Sum);
- Sum=Data[J+4]^gfMult(Exp,Sum);
- Sum=Data[J+5]^gfMult(Exp,Sum);
- Sum=Data[J+6]^gfMult(Exp,Sum);
- Sum=Data[J+7]^gfMult(Exp,Sum);
- }
- for (;J<DataSize;J++)
- Sum=Data[J]^gfMult(Exp,Sum);
+ int Sum=0;
+ for (int J=0;J<DataSize;J++)
+ Sum=Data[J]^gfMult(gfExp[I+1],Sum);
if ((SynData[I]=Sum)!=0)
AllZeroes=false;
}
+
+ // If all syndrome numbers are zero, message does not have errors.
if (AllZeroes)
return(true);
- if (!FirstBlockDone)
+ if (!FirstBlockDone) // Do things which we need to do once for all data.
{
FirstBlockDone=true;
- Clean(PolB,ParSize+1);
- PolB[0]=1;
+
+ // Calculate the error locator polynomial.
+ Clean(ELPol,ParSize+1);
+ ELPol[0]=1;
+
for (int EraPos=0;EraPos<EraSize;EraPos++)
for (int I=ParSize,M=gfExp[DataSize-EraLoc[EraPos]-1];I>0;I--)
- PolB[I]^=gfMult(M,PolB[I-1]);
+ ELPol[I]^=gfMult(M,ELPol[I-1]);
ErrCount=0;
+
+ // Find roots of error locator polynomial.
for (int Root=MAXPAR-DataSize;Root<MAXPAR+1;Root++)
{
int Sum=0;
for (int B=0;B<ParSize+1;B++)
- Sum^=gfMult(gfExp[(B*Root)%MAXPAR],PolB[B]);
- if (Sum==0)
+ Sum^=gfMult(gfExp[(B*Root)%MAXPAR],ELPol[B]);
+ if (Sum==0) // Root found.
{
- Dn[ErrCount]=0;
+ ErrorLocs[ErrCount]=MAXPAR-Root; // Location of error.
+
+ // Calculate the denominator for every error location.
+ Dnm[ErrCount]=0;
for (int I=1;I<ParSize+1;I+=2)
- Dn[ErrCount]^= gfMult(PolB[I],gfExp[Root*(I-1)%MAXPAR]);
- ErrorLocs[ErrCount++]=MAXPAR-Root;
+ Dnm[ErrCount]^= gfMult(ELPol[I],gfExp[Root*(I-1)%MAXPAR]);
+
+ ErrCount++;
}
}
}
- int PolD[MAXPOL];
- pnMult(PolB,SynData,PolD);
+ int EEPol[MAXPOL]; // Error Evaluator Polynomial.
+ pnMult(ELPol,SynData,EEPol);
+ // If errors are present and their number is correctable.
if ((ErrCount<=ParSize) && ErrCount>0)
for (int I=0;I<ErrCount;I++)
{
int Loc=ErrorLocs[I],DLoc=MAXPAR-Loc,N=0;
for (int J=0;J<ParSize;J++)
- N^=gfMult(PolD[J],gfExp[DLoc*J%MAXPAR]);
+ N^=gfMult(EEPol[J],gfExp[DLoc*J%MAXPAR]);
int DataPos=DataSize-Loc-1;
+ // Perform bounds check and correct the data error.
if (DataPos>=0 && DataPos<DataSize)
- Data[DataPos]^=gfMult(N,gfExp[MAXPAR-gfLog[Dn[I]]]);
+ Data[DataPos]^=gfMult(N,gfExp[MAXPAR-gfLog[Dnm[I]]]);
}
- return(ErrCount<=ParSize);
+ return(ErrCount<=ParSize); // Return true if success.
}
diff --git a/unrar/unrar/rs.hpp b/unrar/unrar/rs.hpp
index 2f099f0..6ac8094 100644
--- a/unrar/unrar/rs.hpp
+++ b/unrar/unrar/rs.hpp
@@ -1,8 +1,8 @@
#ifndef _RAR_RS_
#define _RAR_RS_
-#define MAXPAR 255
-#define MAXPOL 512
+#define MAXPAR 255 // Maximum parity data size.
+#define MAXPOL 512 // Maximum polynomial degree.
class RSCoder
{
@@ -12,19 +12,19 @@ class RSCoder
void pnInit();
void pnMult(int *p1,int *p2,int *r);
- int gfExp[MAXPOL];
- int gfLog[MAXPAR+1];
+ int gfExp[MAXPOL]; // Galois field exponents.
+ int gfLog[MAXPAR+1]; // Galois field logarithms.
- int GXPol[MAXPOL*2];
+ int GXPol[MAXPOL*2]; // Generator polynomial g(x).
int ErrorLocs[MAXPAR+1],ErrCount;
- int Dn[MAXPAR+1];
+ int Dnm[MAXPAR+1];
- int ParSize;
- int PolB[MAXPOL];
+ int ParSize; // Parity bytes size and so the number of recovery volumes.
+ int ELPol[MAXPOL]; // Error locator polynomial.
bool FirstBlockDone;
public:
- RSCoder(int ParSize);
+ void Init(int ParSize);
void Encode(byte *Data,int DataSize,byte *DestData);
bool Decode(byte *Data,int DataSize,int *EraLoc,int EraSize);
};
diff --git a/unrar/unrar/rs16.cpp b/unrar/unrar/rs16.cpp
new file mode 100644
index 0000000..f5c7cca
--- /dev/null
+++ b/unrar/unrar/rs16.cpp
@@ -0,0 +1,421 @@
+#include "rar.hpp"
+
+// We used "Screaming Fast Galois Field Arithmetic Using Intel SIMD
+// Instructions" paper by James S. Plank, Kevin M. Greenan
+// and Ethan L. Miller for fast SSE based multiplication.
+// Also we are grateful to Artem Drobanov and Bulat Ziganshin
+// for samples and ideas allowed to make Reed-Solomon codec more efficient.
+
+RSCoder16::RSCoder16()
+{
+ Decoding=false;
+ ND=NR=NE=0;
+ ValidFlags=NULL;
+ MX=NULL;
+ DataLog=NULL;
+ DataLogSize=0;
+
+ gfInit();
+}
+
+
+RSCoder16::~RSCoder16()
+{
+ delete[] gfExp;
+ delete[] gfLog;
+ delete[] DataLog;
+ delete[] MX;
+ delete[] ValidFlags;
+}
+
+
+// Initialize logarithms and exponents Galois field tables.
+void RSCoder16::gfInit()
+{
+ gfExp=new uint[4*gfSize+1];
+ gfLog=new uint[gfSize+1];
+
+ for (uint L=0,E=1;L<gfSize;L++)
+ {
+ gfLog[E]=L;
+ gfExp[L]=E;
+ gfExp[L+gfSize]=E; // Duplicate the table to avoid gfExp overflow check.
+ E<<=1;
+ if (E>gfSize)
+ E^=0x1100B; // Irreducible field-generator polynomial.
+ }
+
+ // log(0)+log(x) must be outside of usual log table, so we can set it
+ // to 0 and avoid check for 0 in multiplication parameters.
+ gfLog[0]= 2*gfSize;
+ for (uint I=2*gfSize;I<=4*gfSize;I++) // Results for log(0)+log(x).
+ gfExp[I]=0;
+}
+
+
+uint RSCoder16::gfAdd(uint a,uint b) // Addition in Galois field.
+{
+ return a^b;
+}
+
+
+uint RSCoder16::gfMul(uint a,uint b) // Multiplication in Galois field.
+{
+ return gfExp[gfLog[a]+gfLog[b]];
+}
+
+
+uint RSCoder16::gfInv(uint a) // Inverse element in Galois field.
+{
+ return a==0 ? 0:gfExp[gfSize-gfLog[a]];
+}
+
+
+bool RSCoder16::Init(uint DataCount, uint RecCount, bool *ValidityFlags)
+{
+ ND = DataCount;
+ NR = RecCount;
+ NE = 0;
+
+ Decoding=ValidityFlags!=NULL;
+ if (Decoding)
+ {
+ delete[] ValidFlags;
+ ValidFlags=new bool[ND + NR];
+
+ for (uint I = 0; I < ND + NR; I++)
+ ValidFlags[I]=ValidityFlags[I];
+ for (uint I = 0; I < ND; I++)
+ if (!ValidFlags[I])
+ NE++;
+ uint ValidECC=0;
+ for (uint I = ND; I < ND + NR; I++)
+ if (ValidFlags[I])
+ ValidECC++;
+ if (NE > ValidECC || NE == 0 || ValidECC == 0)
+ return false;
+ }
+
+ // 2021.09.01 - we allowed RR and REV >100%, so no more NR > ND check.
+ if (ND + NR > gfSize || /*NR > ND ||*/ ND == 0 || NR == 0)
+ return false;
+
+ delete[] MX;
+ if (Decoding)
+ {
+ MX=new uint[NE * ND];
+ MakeDecoderMatrix();
+ InvertDecoderMatrix();
+ }
+ else
+ {
+ MX=new uint[NR * ND];
+ MakeEncoderMatrix();
+ }
+ return true;
+}
+
+
+void RSCoder16::MakeEncoderMatrix()
+{
+ // Create Cauchy encoder generator matrix. Skip trivial "1" diagonal rows,
+ // which would just copy source data to destination.
+ for (uint I = 0; I < NR; I++)
+ for (uint J = 0; J < ND; J++)
+ MX[I * ND + J] = gfInv( gfAdd( (I+ND), J) );
+}
+
+
+void RSCoder16::MakeDecoderMatrix()
+{
+ // Create Cauchy decoder matrix. Skip trivial rows matching valid data
+ // units and containing "1" on main diagonal. Such rows would just copy
+ // source data to destination and they have no real value for us.
+ // Include rows only for broken data units and replace them by first
+ // available valid recovery code rows.
+ for (uint Flag=0, R=ND, Dest=0; Flag < ND; Flag++)
+ if (!ValidFlags[Flag]) // For every broken data unit.
+ {
+ while (!ValidFlags[R]) // Find a valid recovery unit.
+ R++;
+ for (uint J = 0; J < ND; J++) // And place its row to matrix.
+ MX[Dest*ND + J] = gfInv( gfAdd(R,J) );
+ Dest++;
+ R++;
+ }
+}
+
+
+// Apply Gauss–Jordan elimination to find inverse of decoder matrix.
+// We have the square NDxND matrix, but we do not store its trivial
+// diagonal "1" rows matching valid data, so we work with NExND matrix.
+// Our original Cauchy matrix does not contain 0, so we skip search
+// for non-zero pivot.
+void RSCoder16::InvertDecoderMatrix()
+{
+ uint *MI=new uint[NE * ND]; // We'll create inverse matrix here.
+ memset(MI, 0, ND * NE * sizeof(*MI)); // Initialize to identity matrix.
+ for (uint Kr = 0, Kf = 0; Kr < NE; Kr++, Kf++)
+ {
+ while (ValidFlags[Kf]) // Skip trivial rows.
+ Kf++;
+ MI[Kr * ND + Kf] = 1; // Set diagonal 1.
+ }
+
+ // Kr is the number of row in our actual reduced NE x ND matrix,
+ // which does not contain trivial diagonal 1 rows.
+ // Kf is the number of row in full ND x ND matrix with all trivial rows
+ // included.
+ for (uint Kr = 0, Kf = 0; Kf < ND; Kr++, Kf++) // Select pivot row.
+ {
+ while (ValidFlags[Kf] && Kf < ND)
+ {
+ // Here we process trivial diagonal 1 rows matching valid data units.
+ // Their processing can be simplified comparing to usual rows.
+ // In full version of elimination we would set MX[I * ND + Kf] to zero
+ // after MI[..]^=, but we do not need it for matrix inversion.
+ for (uint I = 0; I < NE; I++)
+ MI[I * ND + Kf] ^= MX[I * ND + Kf];
+ Kf++;
+ }
+
+ if (Kf == ND)
+ break;
+
+ uint *MXk = MX + Kr * ND; // k-th row of main matrix.
+ uint *MIk = MI + Kr * ND; // k-th row of inversion matrix.
+
+ uint PInv = gfInv( MXk[Kf] ); // Pivot inverse.
+ // Divide the pivot row by pivot, so pivot cell contains 1.
+ for (uint I = 0; I < ND; I++)
+ {
+ MXk[I] = gfMul( MXk[I], PInv );
+ MIk[I] = gfMul( MIk[I], PInv );
+ }
+
+ for (uint I = 0; I < NE; I++)
+ if (I != Kr) // For all rows except containing the pivot cell.
+ {
+ // Apply Gaussian elimination Mij -= Mkj * Mik / pivot.
+ // Since pivot is already 1, it is reduced to Mij -= Mkj * Mik.
+ uint *MXi = MX + I * ND; // i-th row of main matrix.
+ uint *MIi = MI + I * ND; // i-th row of inversion matrix.
+ uint Mik = MXi[Kf]; // Cell in pivot position.
+ for (uint J = 0; J < ND; J++)
+ {
+ MXi[J] ^= gfMul(MXk[J] , Mik);
+ MIi[J] ^= gfMul(MIk[J] , Mik);
+ }
+ }
+ }
+
+ // Copy data to main matrix.
+ for (uint I = 0; I < NE * ND; I++)
+ MX[I] = MI[I];
+
+ delete[] MI;
+}
+
+
+#if 0
+// Multiply matrix to data vector. When encoding, it contains data in Data
+// and stores error correction codes in Out. When decoding it contains
+// broken data followed by ECC in Data and stores recovered data to Out.
+// We do not use this function now, everything is moved to UpdateECC.
+void RSCoder16::Process(const uint *Data, uint *Out)
+{
+ uint ProcData[gfSize];
+
+ for (uint I = 0; I < ND; I++)
+ ProcData[I]=Data[I];
+
+ if (Decoding)
+ {
+ // Replace broken data units with first available valid recovery codes.
+ // 'Data' array must contain recovery codes after data.
+ for (uint I=0, R=ND, Dest=0; I < ND; I++)
+ if (!ValidFlags[I]) // For every broken data unit.
+ {
+ while (!ValidFlags[R]) // Find a valid recovery unit.
+ R++;
+ ProcData[I]=Data[R];
+ R++;
+ }
+ }
+
+ uint H=Decoding ? NE : NR;
+ for (uint I = 0; I < H; I++)
+ {
+ uint R = 0; // Result of matrix row multiplication to data.
+
+ uint *MXi=MX + I * ND;
+ for (uint J = 0; J < ND; J++)
+ R ^= gfMul(MXi[J], ProcData[J]);
+
+ Out[I] = R;
+ }
+}
+#endif
+
+
+// We update ECC in blocks by applying every data block to all ECC blocks.
+// This function applies one data block to one ECC block.
+void RSCoder16::UpdateECC(uint DataNum, uint ECCNum, const byte *Data, byte *ECC, size_t BlockSize)
+{
+ if (DataNum==0) // Init ECC data.
+ memset(ECC, 0, BlockSize);
+
+ bool DirectAccess;
+#ifdef LITTLE_ENDIAN
+ // We can access data and ECC directly if we have little endian 16 bit uint.
+ DirectAccess=sizeof(ushort)==2;
+#else
+ DirectAccess=false;
+#endif
+
+#ifdef USE_SSE
+ if (DirectAccess && SSE_UpdateECC(DataNum,ECCNum,Data,ECC,BlockSize))
+ return;
+#endif
+
+ if (ECCNum==0)
+ {
+ if (DataLogSize!=BlockSize)
+ {
+ delete[] DataLog;
+ DataLog=new uint[BlockSize];
+ DataLogSize=BlockSize;
+
+ }
+ if (DirectAccess)
+ for (size_t I=0; I<BlockSize; I+=2)
+ DataLog[I] = gfLog[ *(ushort*)(Data+I) ];
+ else
+ for (size_t I=0; I<BlockSize; I+=2)
+ {
+ uint D=Data[I]+Data[I+1]*256;
+ DataLog[I] = gfLog[ D ];
+ }
+ }
+
+ uint ML = gfLog[ MX[ECCNum * ND + DataNum] ];
+
+ if (DirectAccess)
+ for (size_t I=0; I<BlockSize; I+=2)
+ *(ushort*)(ECC+I) ^= gfExp[ ML + DataLog[I] ];
+ else
+ for (size_t I=0; I<BlockSize; I+=2)
+ {
+ uint R=gfExp[ ML + DataLog[I] ];
+ ECC[I]^=byte(R);
+ ECC[I+1]^=byte(R/256);
+ }
+}
+
+
+#ifdef USE_SSE
+// Data and ECC addresses must be properly aligned for SSE.
+// AVX2 did not provide a noticeable speed gain on i7-6700K here.
+bool RSCoder16::SSE_UpdateECC(uint DataNum, uint ECCNum, const byte *Data, byte *ECC, size_t BlockSize)
+{
+ // Check data alignment and SSSE3 support.
+ if ((size_t(Data) & (SSE_ALIGNMENT-1))!=0 || (size_t(ECC) & (SSE_ALIGNMENT-1))!=0 ||
+ _SSE_Version<SSE_SSSE3)
+ return false;
+
+ uint M=MX[ECCNum * ND + DataNum];
+
+ // Prepare tables containing products of M and 4, 8, 12, 16 bit length
+ // numbers, which have 4 high bits in 0..15 range and other bits set to 0.
+ // Store high and low bytes of resulting 16 bit product in separate tables.
+ __m128i T0L,T1L,T2L,T3L; // Low byte tables.
+ __m128i T0H,T1H,T2H,T3H; // High byte tables.
+
+ for (uint I=0;I<16;I++)
+ {
+ ((byte *)&T0L)[I]=gfMul(I,M);
+ ((byte *)&T0H)[I]=gfMul(I,M)>>8;
+ ((byte *)&T1L)[I]=gfMul(I<<4,M);
+ ((byte *)&T1H)[I]=gfMul(I<<4,M)>>8;
+ ((byte *)&T2L)[I]=gfMul(I<<8,M);
+ ((byte *)&T2H)[I]=gfMul(I<<8,M)>>8;
+ ((byte *)&T3L)[I]=gfMul(I<<12,M);
+ ((byte *)&T3H)[I]=gfMul(I<<12,M)>>8;
+ }
+
+ size_t Pos=0;
+
+ __m128i LowByteMask=_mm_set1_epi16(0xff); // 00ff00ff...00ff
+ __m128i Low4Mask=_mm_set1_epi8(0xf); // 0f0f0f0f...0f0f
+ __m128i High4Mask=_mm_slli_epi16(Low4Mask,4); // f0f0f0f0...f0f0
+
+ for (; Pos+2*sizeof(__m128i)<=BlockSize; Pos+=2*sizeof(__m128i))
+ {
+ // We process two 128 bit chunks of source data at once.
+ __m128i *D=(__m128i *)(Data+Pos);
+
+ // Place high bytes of both chunks to one variable and low bytes to
+ // another, so we can use the table lookup multiplication for 16 values
+ // 4 bit length each at once.
+ __m128i HighBytes0=_mm_srli_epi16(D[0],8);
+ __m128i LowBytes0=_mm_and_si128(D[0],LowByteMask);
+ __m128i HighBytes1=_mm_srli_epi16(D[1],8);
+ __m128i LowBytes1=_mm_and_si128(D[1],LowByteMask);
+ __m128i HighBytes=_mm_packus_epi16(HighBytes0,HighBytes1);
+ __m128i LowBytes=_mm_packus_epi16(LowBytes0,LowBytes1);
+
+ // Multiply bits 0..3 of low bytes. Store low and high product bytes
+ // separately in cumulative sum variables.
+ __m128i LowBytesLow4=_mm_and_si128(LowBytes,Low4Mask);
+ __m128i LowBytesMultSum=_mm_shuffle_epi8(T0L,LowBytesLow4);
+ __m128i HighBytesMultSum=_mm_shuffle_epi8(T0H,LowBytesLow4);
+
+ // Multiply bits 4..7 of low bytes. Store low and high product bytes separately.
+ __m128i LowBytesHigh4=_mm_and_si128(LowBytes,High4Mask);
+ LowBytesHigh4=_mm_srli_epi16(LowBytesHigh4,4);
+ __m128i LowBytesHigh4MultLow=_mm_shuffle_epi8(T1L,LowBytesHigh4);
+ __m128i LowBytesHigh4MultHigh=_mm_shuffle_epi8(T1H,LowBytesHigh4);
+
+ // Add new product to existing sum, low and high bytes separately.
+ LowBytesMultSum=_mm_xor_si128(LowBytesMultSum,LowBytesHigh4MultLow);
+ HighBytesMultSum=_mm_xor_si128(HighBytesMultSum,LowBytesHigh4MultHigh);
+
+ // Multiply bits 0..3 of high bytes. Store low and high product bytes separately.
+ __m128i HighBytesLow4=_mm_and_si128(HighBytes,Low4Mask);
+ __m128i HighBytesLow4MultLow=_mm_shuffle_epi8(T2L,HighBytesLow4);
+ __m128i HighBytesLow4MultHigh=_mm_shuffle_epi8(T2H,HighBytesLow4);
+
+ // Add new product to existing sum, low and high bytes separately.
+ LowBytesMultSum=_mm_xor_si128(LowBytesMultSum,HighBytesLow4MultLow);
+ HighBytesMultSum=_mm_xor_si128(HighBytesMultSum,HighBytesLow4MultHigh);
+
+ // Multiply bits 4..7 of high bytes. Store low and high product bytes separately.
+ __m128i HighBytesHigh4=_mm_and_si128(HighBytes,High4Mask);
+ HighBytesHigh4=_mm_srli_epi16(HighBytesHigh4,4);
+ __m128i HighBytesHigh4MultLow=_mm_shuffle_epi8(T3L,HighBytesHigh4);
+ __m128i HighBytesHigh4MultHigh=_mm_shuffle_epi8(T3H,HighBytesHigh4);
+
+ // Add new product to existing sum, low and high bytes separately.
+ LowBytesMultSum=_mm_xor_si128(LowBytesMultSum,HighBytesHigh4MultLow);
+ HighBytesMultSum=_mm_xor_si128(HighBytesMultSum,HighBytesHigh4MultHigh);
+
+ // Combine separate low and high cumulative sum bytes to 16-bit words.
+ __m128i HighBytesHigh4Mult0=_mm_unpacklo_epi8(LowBytesMultSum,HighBytesMultSum);
+ __m128i HighBytesHigh4Mult1=_mm_unpackhi_epi8(LowBytesMultSum,HighBytesMultSum);
+
+ // Add result to ECC.
+ __m128i *StoreECC=(__m128i *)(ECC+Pos);
+
+ StoreECC[0]=_mm_xor_si128(StoreECC[0],HighBytesHigh4Mult0);
+ StoreECC[1]=_mm_xor_si128(StoreECC[1],HighBytesHigh4Mult1);
+ }
+
+ // If we have non 128 bit aligned data in the end of block, process them
+ // in a usual way. We cannot do the same in the beginning of block,
+ // because Data and ECC can have different alignment offsets.
+ for (; Pos<BlockSize; Pos+=2)
+ *(ushort*)(ECC+Pos) ^= gfMul( M, *(ushort*)(Data+Pos) );
+
+ return true;
+}
+#endif
diff --git a/unrar/unrar/rs16.hpp b/unrar/unrar/rs16.hpp
new file mode 100644
index 0000000..b67a7ca
--- /dev/null
+++ b/unrar/unrar/rs16.hpp
@@ -0,0 +1,44 @@
+#ifndef _RAR_RS16_
+#define _RAR_RS16_
+
+class RSCoder16
+{
+ private:
+ static const uint gfSize=65535; // Galois field size.
+ void gfInit(); // Galois field inititalization.
+ inline uint gfAdd(uint a,uint b); // Addition in Galois field.
+ inline uint gfMul(uint a,uint b); // Multiplication in Galois field.
+ inline uint gfInv(uint a); // Inverse element in Galois field.
+ uint *gfExp; // Galois field exponents.
+ uint *gfLog; // Galois field logarithms.
+
+ void MakeEncoderMatrix();
+ void MakeDecoderMatrix();
+ void InvertDecoderMatrix();
+
+#ifdef USE_SSE
+ bool SSE_UpdateECC(uint DataNum, uint ECCNum, const byte *Data, byte *ECC, size_t BlockSize);
+#endif
+
+ bool Decoding; // If we are decoding or encoding data.
+ uint ND; // Number of data units.
+ uint NR; // Number of Reed-Solomon code units.
+ uint NE; // Number of erasures.
+ bool *ValidFlags; // Validity flags for data and ECC units.
+ uint *MX; // Cauchy based coding or decoding matrix.
+
+ uint *DataLog; // Buffer to store data logarithms for UpdateECC.
+ size_t DataLogSize;
+
+ public:
+ RSCoder16();
+ ~RSCoder16();
+
+ bool Init(uint DataCount, uint RecCount, bool *ValidityFlags);
+#if 0 // We use only UpdateECC now.
+ void Process(const uint *Data, uint *Out);
+#endif
+ void UpdateECC(uint DataNum, uint ECCNum, const byte *Data, byte *ECC, size_t BlockSize);
+};
+
+#endif
diff --git a/unrar/unrar/savepos.cpp b/unrar/unrar/savepos.cpp
deleted file mode 100644
index e46c4e6..0000000
--- a/unrar/unrar/savepos.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-#include "rar.hpp"
-
-SaveFilePos::SaveFilePos(File &SaveFile)
-{
- SaveFilePos::SaveFile=&SaveFile;
- SavePos=SaveFile.Tell();
- CloseCount=SaveFile.CloseCount;
-}
-
-
-SaveFilePos::~SaveFilePos()
-{
- if (CloseCount==SaveFile->CloseCount)
- SaveFile->Seek(SavePos,SEEK_SET);
-}
diff --git a/unrar/unrar/savepos.hpp b/unrar/unrar/savepos.hpp
deleted file mode 100644
index 303550a..0000000
--- a/unrar/unrar/savepos.hpp
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef _RAR_SAVEPOS_
-#define _RAR_SAVEPOS_
-
-class SaveFilePos
-{
- private:
- File *SaveFile;
- Int64 SavePos;
- uint CloseCount;
- public:
- SaveFilePos(File &SaveFile);
- ~SaveFilePos();
-};
-
-#endif
diff --git a/unrar/unrar/scantree.cpp b/unrar/unrar/scantree.cpp
index cfd2ef7..dbaf1e4 100644
--- a/unrar/unrar/scantree.cpp
+++ b/unrar/unrar/scantree.cpp
@@ -1,6 +1,6 @@
#include "rar.hpp"
-ScanTree::ScanTree(StringList *FileMasks,int Recurse,bool GetLinks,int GetDirs)
+ScanTree::ScanTree(StringList *FileMasks,RECURSE_MODE Recurse,bool GetLinks,SCAN_DIRS GetDirs)
{
ScanTree::FileMasks=FileMasks;
ScanTree::Recurse=Recurse;
@@ -8,16 +8,17 @@ ScanTree::ScanTree(StringList *FileMasks,int Recurse,bool GetLinks,int GetDirs)
ScanTree::GetDirs=GetDirs;
ScanEntireDisk=false;
+ FolderWildcards=false;
SetAllMaskDepth=0;
*CurMask=0;
- *CurMaskW=0;
memset(FindStack,0,sizeof(FindStack));
Depth=0;
Errors=0;
- FastFindFile=false;
*ErrArcName=0;
Cmd=NULL;
+ ErrDirList=NULL;
+ ErrDirSpecPathLength=NULL;
}
@@ -29,17 +30,30 @@ ScanTree::~ScanTree()
}
-int ScanTree::GetNext(FindData *FindData)
+SCAN_CODE ScanTree::GetNext(FindData *FD)
{
if (Depth<0)
- return(SCAN_DONE);
+ return SCAN_DONE;
- int FindCode;
+#ifndef SILENT
+ uint LoopCount=0;
+#endif
+
+ SCAN_CODE FindCode;
while (1)
{
- if ((*CurMask==0 || FastFindFile && Depth==0) && !PrepareMasks())
- return(SCAN_DONE);
- FindCode=FindProc(FindData);
+ if (*CurMask==0 && !GetNextMask())
+ return SCAN_DONE;
+
+#ifndef SILENT
+ // Let's return some ticks to system or WinRAR can become irresponsible
+ // while scanning files in command like "winrar a -r arc c:\file.ext".
+ // Also we reset system sleep timer here.
+ if ((++LoopCount & 0x3ff)==0)
+ Wait();
+#endif
+
+ FindCode=FindProc(FD);
if (FindCode==SCAN_ERROR)
{
Errors++;
@@ -47,258 +61,445 @@ int ScanTree::GetNext(FindData *FindData)
}
if (FindCode==SCAN_NEXT)
continue;
- if (FindCode==SCAN_SUCCESS && FindData->IsDir && GetDirs==SCAN_SKIPDIRS)
+ if (FindCode==SCAN_SUCCESS && FD->IsDir && GetDirs==SCAN_SKIPDIRS)
continue;
- if (FindCode==SCAN_DONE && PrepareMasks())
+ if (FindCode==SCAN_DONE && GetNextMask())
continue;
+ if (FilterList.ItemsCount()>0 && FindCode==SCAN_SUCCESS)
+ if (!CommandData::CheckArgs(&FilterList,FD->IsDir,FD->Name,false,MATCH_WILDSUBPATH))
+ continue;
break;
}
- return(FindCode);
+ return FindCode;
}
-bool ScanTree::PrepareMasks()
+// For masks like dir1\dir2*\*.ext in non-recursive mode.
+bool ScanTree::ExpandFolderMask()
{
- ScanEntireDisk=false;
- if (!FileMasks->GetString(CurMask,CurMaskW,sizeof(CurMask)))
- return(false);
- CurMask[ASIZE(CurMask)-1]=0;
- CurMaskW[ASIZE(CurMaskW)-1]=0;
-#ifdef _WIN_32
- UnixSlashToDos(CurMask);
-#endif
+ bool WildcardFound=false;
+ uint SlashPos=0;
+ for (int I=0;CurMask[I]!=0;I++)
+ {
+ if (CurMask[I]=='?' || CurMask[I]=='*')
+ WildcardFound=true;
+ if (WildcardFound && IsPathDiv(CurMask[I]))
+ {
+ // First path separator position after folder wildcard mask.
+ // In case of dir1\dir2*\dir3\name.ext mask it may point not to file
+ // name, so we cannot use PointToName() here.
+ SlashPos=I;
+ break;
+ }
+ }
- // We wish to scan entire disk if mask like c:\ is specified
- // regardless of recursion mode. Use c:\*.* mask when need to scan only
- // the root directory.
- ScanEntireDisk=IsDiskLetter(CurMask) && IsPathDiv(CurMask[2]) && CurMask[3]==0;
+ wchar Mask[NM];
+ wcsncpyz(Mask,CurMask,ASIZE(Mask));
+ Mask[SlashPos]=0;
+
+ // Prepare the list of all folders matching the wildcard mask.
+ ExpandedFolderList.Reset();
+ FindFile Find;
+ Find.SetMask(Mask);
+ FindData FD;
+ while (Find.Next(&FD))
+ if (FD.IsDir)
+ {
+ wcsncatz(FD.Name,CurMask+SlashPos,ASIZE(FD.Name));
- char *Name=PointToName(CurMask);
- if (*Name==0)
- strcat(CurMask,MASKALL);
- if (Name[0]=='.' && (Name[1]==0 || Name[1]=='.' && Name[2]==0))
+ // Treat dir*\* or dir*\*.* as dir, so empty 'dir' is also matched
+ // by such mask. Skipping empty dir with dir*\*.* confused some users.
+ wchar *LastMask=PointToName(FD.Name);
+ if (wcscmp(LastMask,L"*")==0 || wcscmp(LastMask,L"*.*")==0)
+ RemoveNameFromPath(FD.Name);
+
+ ExpandedFolderList.AddString(FD.Name);
+ }
+ if (ExpandedFolderList.ItemsCount()==0)
+ return false;
+ // Return the first matching folder name now.
+ ExpandedFolderList.GetString(CurMask,ASIZE(CurMask));
+ return true;
+}
+
+
+// For masks like dir1\dir2*\file.ext this function sets 'dir1' recursive mask
+// and '*\dir2*\file.ext' filter. Masks without folder wildcards are
+// returned as is.
+bool ScanTree::GetFilteredMask()
+{
+ // If we have some matching folders left for non-recursive folder wildcard
+ // mask, we return it here.
+ if (ExpandedFolderList.ItemsCount()>0 && ExpandedFolderList.GetString(CurMask,ASIZE(CurMask)))
+ return true;
+
+ FolderWildcards=false;
+ FilterList.Reset();
+ if (!FileMasks->GetString(CurMask,ASIZE(CurMask)))
+ return false;
+
+ // Check if folder wildcards present.
+ bool WildcardFound=false;
+ uint FolderWildcardCount=0;
+ uint SlashPos=0;
+ uint StartPos=0;
+#ifdef _WIN_ALL // Not treat the special NTFS \\?\d: path prefix as a wildcard.
+ if (CurMask[0]=='\\' && CurMask[1]=='\\' && CurMask[2]=='?' && CurMask[3]=='\\')
+ StartPos=4;
+#endif
+ for (uint I=StartPos;CurMask[I]!=0;I++)
{
- AddEndSlash(CurMask);
- strcat(CurMask,MASKALL);
+ if (CurMask[I]=='?' || CurMask[I]=='*')
+ WildcardFound=true;
+ if (IsPathDiv(CurMask[I]) || IsDriveDiv(CurMask[I]))
+ {
+ if (WildcardFound)
+ {
+ // Calculate a number of folder wildcards in current mask.
+ FolderWildcardCount++;
+ WildcardFound=false;
+ }
+ if (FolderWildcardCount==0)
+ SlashPos=I; // Slash position before first folder wildcard mask.
+ }
}
- SpecPathLength=Name-CurMask;
-// if (SpecPathLength>1)
-// SpecPathLength--;
+ if (FolderWildcardCount==0)
+ return true;
+ FolderWildcards=true; // Global folder wildcards flag.
+
+ // If we have only one folder wildcard component and -r is missing or -r-
+ // is specified, prepare matching folders in non-recursive mode.
+ // We assume -r for masks like dir1*\dir2*\file*, because it is complicated
+ // to fast find them using OS file find API call.
+ if ((Recurse==RECURSE_NONE || Recurse==RECURSE_DISABLE) && FolderWildcardCount==1)
+ return ExpandFolderMask();
+
+ wchar Filter[NM];
+ // Convert path\dir*\ to *\dir filter to search for 'dir' in all 'path' subfolders.
+ wcsncpyz(Filter,L"*",ASIZE(Filter));
+ AddEndSlash(Filter,ASIZE(Filter));
+ // SlashPos might point or not point to path separator for masks like 'dir*', '\dir*' or 'd:dir*'
+ wchar *WildName=IsPathDiv(CurMask[SlashPos]) || IsDriveDiv(CurMask[SlashPos]) ? CurMask+SlashPos+1 : CurMask+SlashPos;
+ wcsncatz(Filter,WildName,ASIZE(Filter));
+
+ // Treat dir*\* or dir*\*.* as dir\, so empty 'dir' is also matched
+ // by such mask. Skipping empty dir with dir*\*.* confused some users.
+ wchar *LastMask=PointToName(Filter);
+ if (wcscmp(LastMask,L"*")==0 || wcscmp(LastMask,L"*.*")==0)
+ *LastMask=0;
+
+ FilterList.AddString(Filter);
+
+ bool RelativeDrive=IsDriveDiv(CurMask[SlashPos]);
+ if (RelativeDrive)
+ SlashPos++; // Use "d:" instead of "d" for d:* mask.
+
+ CurMask[SlashPos]=0;
+
+ if (!RelativeDrive) // Keep d: mask as is, not convert to d:\*
+ {
+ // We need to append "\*" both for -ep1 to work correctly and to
+ // convert d:\* masks previously truncated to d: back to original form.
+ AddEndSlash(CurMask,ASIZE(CurMask));
+ wcsncatz(CurMask,MASKALL,ASIZE(CurMask));
+ }
+ return true;
+}
+
- bool WideName=(*CurMaskW!=0);
+bool ScanTree::GetNextMask()
+{
+ if (!GetFilteredMask())
+ return false;
+#ifdef _WIN_ALL
+ UnixSlashToDos(CurMask,CurMask,ASIZE(CurMask));
+#endif
- if (WideName)
+ // We prefer to scan entire disk if mask like \\server\share\ or c:\
+ // is specified regardless of recursion mode. Use \\server\share\*.*
+ // or c:\*.* mask to scan only the root directory.
+ if (CurMask[0]=='\\' && CurMask[1]=='\\')
{
- wchar *NameW=PointToName(CurMaskW);
- if (*NameW==0)
- strcatw(CurMaskW,MASKALLW);
- if (NameW[0]=='.' && (NameW[1]==0 || NameW[1]=='.' && NameW[2]==0))
+ const wchar *Slash=wcschr(CurMask+2,'\\');
+ if (Slash!=NULL)
{
- AddEndSlash(CurMaskW);
- strcatw(CurMaskW,MASKALLW);
+ Slash=wcschr(Slash+1,'\\');
+ ScanEntireDisk=Slash!=NULL && *(Slash+1)==0;
}
- SpecPathLengthW=NameW-CurMaskW;
}
else
+ ScanEntireDisk=IsDriveLetter(CurMask) && IsPathDiv(CurMask[2]) && CurMask[3]==0;
+
+
+ wchar *Name=PointToName(CurMask);
+ if (*Name==0)
+ wcsncatz(CurMask,MASKALL,ASIZE(CurMask));
+ if (Name[0]=='.' && (Name[1]==0 || Name[1]=='.' && Name[2]==0))
{
- wchar WideMask[NM];
- CharToWide(CurMask,WideMask);
- SpecPathLengthW=PointToName(WideMask)-WideMask;
+ AddEndSlash(CurMask,ASIZE(CurMask));
+ wcsncatz(CurMask,MASKALL,ASIZE(CurMask));
}
+ SpecPathLength=Name-CurMask;
Depth=0;
- strcpy(OrigCurMask,CurMask);
- strcpyw(OrigCurMaskW,CurMaskW);
+ wcsncpyz(OrigCurMask,CurMask,ASIZE(OrigCurMask));
- return(true);
+ return true;
}
-int ScanTree::FindProc(FindData *FindData)
+SCAN_CODE ScanTree::FindProc(FindData *FD)
{
if (*CurMask==0)
- return(SCAN_NEXT);
- FastFindFile=false;
- if (FindStack[Depth]==NULL)
+ return SCAN_NEXT;
+ bool FastFindFile=false;
+
+ if (FindStack[Depth]==NULL) // No FindFile object for this depth yet.
{
- bool Wildcards=IsWildcard(CurMask,CurMaskW);
- bool FindCode=!Wildcards && FindFile::FastFind(CurMask,CurMaskW,FindData,GetLinks);
- bool IsDir=FindCode && FindData->IsDir;
+ bool Wildcards=IsWildcard(CurMask);
+
+ // If we have a file name without wildcards, we can try to use
+ // FastFind to optimize speed. For example, in Unix it results in
+ // stat call instead of opendir/readdir/closedir.
+ bool FindCode=!Wildcards && FindFile::FastFind(CurMask,FD,GetLinks);
+
+ // Link check is important for NTFS, where links can have "Directory"
+ // attribute, but we do not want to recurse to them in "get links" mode.
+ bool IsDir=FindCode && FD->IsDir && (!GetLinks || !FD->IsLink);
+
+ // SearchAll means that we'll use "*" mask for search, so we'll find
+ // subdirectories and will be able to recurse into them.
+ // We do not use "*" for directories at any level or for files
+ // at top level in recursion mode. We always comrpess the entire directory
+ // if folder wildcard is specified.
bool SearchAll=!IsDir && (Depth>0 || Recurse==RECURSE_ALWAYS ||
- Wildcards && Recurse==RECURSE_WILDCARDS || ScanEntireDisk);
+ FolderWildcards && Recurse!=RECURSE_DISABLE ||
+ Wildcards && Recurse==RECURSE_WILDCARDS ||
+ ScanEntireDisk && Recurse!=RECURSE_DISABLE);
if (Depth==0)
SearchAllInRoot=SearchAll;
if (SearchAll || Wildcards)
{
+ // Create the new FindFile object for wildcard based search.
FindStack[Depth]=new FindFile;
- char SearchMask[NM];
- strcpy(SearchMask,CurMask);
+
+ wchar SearchMask[NM];
+ wcsncpyz(SearchMask,CurMask,ASIZE(SearchMask));
if (SearchAll)
- strcpy(PointToName(SearchMask),MASKALL);
+ SetName(SearchMask,MASKALL,ASIZE(SearchMask));
FindStack[Depth]->SetMask(SearchMask);
- if (*CurMaskW)
- {
- wchar SearchMaskW[NM];
- strcpyw(SearchMaskW,CurMaskW);
- if (SearchAll)
- strcpyw(PointToName(SearchMaskW),MASKALLW);
- FindStack[Depth]->SetMaskW(SearchMaskW);
- }
}
else
{
- FastFindFile=true;
- if (!FindCode)
+ // Either we failed to fast find or we found a file or we found
+ // a directory in RECURSE_DISABLE mode, so we do not need to scan it.
+ // We can return here and do not need to process further.
+ // We need to process further only if we fast found a directory.
+ if (!FindCode || !IsDir || Recurse==RECURSE_DISABLE)
{
- if (Cmd!=NULL && Cmd->ExclCheck(CurMask,true))
- return(SCAN_NEXT);
- ErrHandler.OpenErrorMsg(ErrArcName,CurMask);
- return(FindData->Error ? SCAN_ERROR:SCAN_NEXT);
+ // Return SCAN_SUCCESS if we found a file.
+ SCAN_CODE RetCode=SCAN_SUCCESS;
+
+ if (!FindCode)
+ {
+ // Return SCAN_ERROR if problem is more serious than just
+ // "file not found".
+ RetCode=FD->Error ? SCAN_ERROR:SCAN_NEXT;
+
+ // If we failed to find an object, but our current mask is excluded,
+ // we skip this object and avoid indicating an error.
+ if (Cmd!=NULL && Cmd->ExclCheck(CurMask,false,true,true))
+ RetCode=SCAN_NEXT;
+ else
+ {
+ ErrHandler.OpenErrorMsg(ErrArcName,CurMask);
+ // User asked to return RARX_NOFILES and not RARX_OPEN here.
+ ErrHandler.SetErrorCode(RARX_NOFILES);
+ }
+ }
+
+ // If we searched only for one file or directory in "fast find"
+ // (without a wildcard) mode, let's set masks to zero,
+ // so calling function will know that current mask is used
+ // and next one must be read from mask list for next call.
+ // It is not necessary for directories, because even in "fast find"
+ // mode, directory recursing will quit by (Depth < 0) condition,
+ // which returns SCAN_DONE to calling function.
+ *CurMask=0;
+
+ return RetCode;
}
+
+ // We found a directory using only FindFile::FastFind function.
+ FastFindFile=true;
}
}
- if (!FastFindFile && !FindStack[Depth]->Next(FindData,GetLinks))
+ if (!FastFindFile && !FindStack[Depth]->Next(FD,GetLinks))
{
- bool Error=FindData->Error;
-
-#ifdef _WIN_32
- if (Error)
- {
- // Do not display an error if we cannot scan contents of reparse
- // point. Vista contains a lot of reparse (or junction) points,
- // which are not accessible.
- if ((FindData->FileAttr & FILE_ATTRIBUTE_REPARSE_POINT)!=0)
- Error=false;
-
- // Do not display an error if we cannot scan contents of
- // "System Volume Information" folder. Normally it is not accessible.
- if (strstr(CurMask,"System Volume Information\\")!=NULL)
- Error=false;
- }
-#endif
-
- if (Cmd!=NULL && Cmd->ExclCheck(CurMask,true))
- Error=false;
+ // We cannot find anything more in directory either because of
+ // some error or just as result of all directory entries already read.
-#ifndef SILENT
+ bool Error=FD->Error;
if (Error)
- {
- Log(NULL,St(MScanError),CurMask);
- }
-#endif
+ ScanError(Error);
- char DirName[NM];
- wchar DirNameW[NM];
+ wchar DirName[NM];
*DirName=0;
- *DirNameW=0;
+ // Going to at least one directory level higher.
delete FindStack[Depth];
FindStack[Depth--]=NULL;
while (Depth>=0 && FindStack[Depth]==NULL)
Depth--;
if (Depth < 0)
{
+ // Directories scanned both in normal and FastFindFile mode,
+ // finally exit from scan here, by (Depth < 0) condition.
+
if (Error)
Errors++;
- return(SCAN_DONE);
+ return SCAN_DONE;
}
- char *Slash=strrchrd(CurMask,CPATHDIVIDER);
+
+ wchar *Slash=wcsrchr(CurMask,CPATHDIVIDER);
if (Slash!=NULL)
{
- char Mask[NM];
- strcpy(Mask,Slash);
+ wchar Mask[NM];
+ wcsncpyz(Mask,Slash,ASIZE(Mask));
if (Depth<SetAllMaskDepth)
- strcpy(Mask+1,PointToName(OrigCurMask));
+ wcsncpyz(Mask+1,PointToName(OrigCurMask),ASIZE(Mask)-1);
*Slash=0;
- strcpy(DirName,CurMask);
- char *PrevSlash=strrchrd(CurMask,CPATHDIVIDER);
+ wcsncpyz(DirName,CurMask,ASIZE(DirName));
+ wchar *PrevSlash=wcsrchr(CurMask,CPATHDIVIDER);
if (PrevSlash==NULL)
- strcpy(CurMask,Mask+1);
+ wcsncpyz(CurMask,Mask+1,ASIZE(CurMask));
else
- strcpy(PrevSlash,Mask);
- }
-
- if (*CurMaskW!=0)
- {
- wchar *Slash=strrchrw(CurMaskW,CPATHDIVIDER);
- if (Slash!=NULL)
{
- wchar Mask[NM];
- strcpyw(Mask,Slash);
- if (Depth<SetAllMaskDepth)
- strcpyw(Mask+1,PointToName(OrigCurMaskW));
- *Slash=0;
- strcpyw(DirNameW,CurMaskW);
- wchar *PrevSlash=strrchrw(CurMaskW,CPATHDIVIDER);
- if (PrevSlash==NULL)
- strcpyw(CurMaskW,Mask+1);
- else
- strcpyw(PrevSlash,Mask);
+ *PrevSlash=0;
+ wcsncatz(CurMask,Mask,ASIZE(CurMask));
}
-#ifndef _WIN_CE
- if (LowAscii(CurMaskW))
- *CurMaskW=0;
-#endif
}
if (GetDirs==SCAN_GETDIRSTWICE &&
- FindFile::FastFind(DirName,DirNameW,FindData,GetLinks) && FindData->IsDir)
+ FindFile::FastFind(DirName,FD,GetLinks) && FD->IsDir)
{
- FindData->Flags|=FDDF_SECONDDIR;
- return(Error ? SCAN_ERROR:SCAN_SUCCESS);
+ FD->Flags|=FDDF_SECONDDIR;
+ return Error ? SCAN_ERROR:SCAN_SUCCESS;
}
- return(Error ? SCAN_ERROR:SCAN_NEXT);
+ return Error ? SCAN_ERROR:SCAN_NEXT;
}
- if (FindData->IsDir)
+ // Link check is required for NTFS links, not for Unix.
+ if (FD->IsDir && (!GetLinks || !FD->IsLink))
{
+ // If we found the directory in top (Depth==0) directory
+ // and if we are not in "fast find" (directory name only as argument)
+ // or in recurse (SearchAll was set when opening the top directory) mode,
+ // we do not recurse into this directory. We either return it by itself
+ // or skip it.
if (!FastFindFile && Depth==0 && !SearchAllInRoot)
- return(GetDirs==SCAN_GETCURDIRS ? SCAN_SUCCESS:SCAN_NEXT);
-
-// if (GetDirs==SCAN_GETCURDIRS && Depth==0 && !SearchAllInRoot)
-// return(SCAN_SUCCESS);
+ return GetDirs==SCAN_GETCURDIRS ? SCAN_SUCCESS:SCAN_NEXT;
- char Mask[NM];
- bool MaskAll=FastFindFile;
+ // Let's check if directory name is excluded, so we do not waste
+ // time searching in directory, which will be excluded anyway.
+ if (Cmd!=NULL && (Cmd->ExclCheck(FD->Name,true,false,false) ||
+ Cmd->ExclDirByAttr(FD->FileAttr)))
+ {
+ // If we are here in "fast find" mode, it means that entire directory
+ // specified in command line is excluded. Then we need to return
+ // SCAN_DONE to go to next mask and avoid the infinite loop
+ // in GetNext() function. Such loop would be possible in case of
+ // SCAN_NEXT code and "rar a arc dir -xdir" command.
-// bool MaskAll=CmpName(CurMask,FindData->Name,MATCH_NAMES);
+ return FastFindFile ? SCAN_DONE:SCAN_NEXT;
+ }
+
+ wchar Mask[NM];
- strcpy(Mask,MaskAll ? MASKALL:PointToName(CurMask));
- strcpy(CurMask,FindData->Name);
+ wcsncpyz(Mask,FastFindFile ? MASKALL:PointToName(CurMask),ASIZE(Mask));
+ wcsncpyz(CurMask,FD->Name,ASIZE(CurMask));
- if (strlen(CurMask)+strlen(Mask)+1>=NM || Depth>=MAXSCANDEPTH-1)
+ if (wcslen(CurMask)+wcslen(Mask)+1>=NM || Depth>=MAXSCANDEPTH-1)
{
-#ifndef SILENT
- Log(NULL,"\n%s%c%s",CurMask,CPATHDIVIDER,Mask);
- Log(NULL,St(MPathTooLong));
-#endif
- return(SCAN_ERROR);
+ uiMsg(UIERROR_PATHTOOLONG,CurMask,SPATHDIVIDER,Mask);
+ return SCAN_ERROR;
}
- AddEndSlash(CurMask);
- strcat(CurMask,Mask);
+ AddEndSlash(CurMask,ASIZE(CurMask));
+ wcsncatz(CurMask,Mask,ASIZE(CurMask));
- if (*CurMaskW && *FindData->NameW==0)
- CharToWide(FindData->Name,FindData->NameW);
- if (*FindData->NameW!=0)
- {
- wchar Mask[NM];
- if (FastFindFile)
- strcpyw(Mask,MASKALLW);
- else
- if (*CurMaskW)
- strcpyw(Mask,PointToName(CurMaskW));
- else
- CharToWide(PointToName(CurMask),Mask);
- strcpyw(CurMaskW,FindData->NameW);
- AddEndSlash(CurMaskW);
- strcatw(CurMaskW,Mask);
- }
Depth++;
- if (MaskAll)
+
+ // We need to use OrigCurMask for depths less than SetAllMaskDepth
+ // and "*" for depths equal or larger than SetAllMaskDepth.
+ // It is important when "fast finding" directories at Depth > 0.
+ // For example, if current directory is RootFolder and we compress
+ // the following directories structure:
+ // RootFolder
+ // +--Folder1
+ // | +--Folder2
+ // | +--Folder3
+ // +--Folder4
+ // with 'rar a -r arcname Folder2' command, rar could add not only
+ // Folder1\Folder2 contents, but also Folder1\Folder3 if we were using
+ // "*" mask at all levels. We need to use "*" mask inside of Folder2,
+ // but return to "Folder2" mask when completing scanning Folder2.
+ // We can rewrite SearchAll expression above to avoid fast finding
+ // directories at Depth > 0, but then 'rar a -r arcname Folder2'
+ // will add the empty Folder2 and do not add its contents.
+
+ if (FastFindFile)
SetAllMaskDepth=Depth;
}
- if (!FastFindFile && !CmpName(CurMask,FindData->Name,MATCH_NAMES))
- return(SCAN_NEXT);
- return(SCAN_SUCCESS);
+ if (!FastFindFile && !CmpName(CurMask,FD->Name,MATCH_NAMES))
+ return SCAN_NEXT;
+
+ return SCAN_SUCCESS;
+}
+
+
+void ScanTree::ScanError(bool &Error)
+{
+#ifdef _WIN_ALL
+ if (Error)
+ {
+ // Get attributes of parent folder and do not display an error
+ // if it is reparse point. We cannot scan contents of standard
+ // Windows reparse points like "C:\Documents and Settings"
+ // and we do not want to issue numerous useless errors for them.
+ // We cannot just check FD->FileAttr here, it can be undefined
+ // if we process "folder\*" mask or if we process "folder" mask,
+ // but "folder" is inaccessible.
+ wchar *Slash=PointToName(CurMask);
+ if (Slash>CurMask)
+ {
+ *(Slash-1)=0;
+ DWORD Attr=GetFileAttributes(CurMask);
+ *(Slash-1)=CPATHDIVIDER;
+ if (Attr!=0xffffffff && (Attr & FILE_ATTRIBUTE_REPARSE_POINT)!=0)
+ Error=false;
+ }
+
+ // Do not display an error if we cannot scan contents of
+ // "System Volume Information" folder. Normally it is not accessible.
+ if (wcsstr(CurMask,L"System Volume Information\\")!=NULL)
+ Error=false;
+ }
+#endif
+
+ if (Error && Cmd!=NULL && Cmd->ExclCheck(CurMask,false,true,true))
+ Error=false;
+
+ if (Error)
+ {
+ if (ErrDirList!=NULL)
+ ErrDirList->AddString(CurMask);
+ if (ErrDirSpecPathLength!=NULL)
+ ErrDirSpecPathLength->Push((uint)SpecPathLength);
+ wchar FullName[NM];
+ // This conversion works for wildcard masks too.
+ ConvertNameToFull(CurMask,FullName,ASIZE(FullName));
+ uiMsg(UIERROR_DIRSCAN,FullName);
+ ErrHandler.SysErrMsg();
+ }
}
diff --git a/unrar/unrar/scantree.hpp b/unrar/unrar/scantree.hpp
index 34d52fe..7ebe69a 100644
--- a/unrar/unrar/scantree.hpp
+++ b/unrar/unrar/scantree.hpp
@@ -1,9 +1,15 @@
#ifndef _RAR_SCANTREE_
#define _RAR_SCANTREE_
-enum { RECURSE_NONE=0,RECURSE_ALWAYS,RECURSE_WILDCARDS };
-enum { SCAN_SKIPDIRS=0,SCAN_GETDIRS,SCAN_GETDIRSTWICE,SCAN_GETCURDIRS };
-enum { SCAN_SUCCESS,SCAN_DONE,SCAN_ERROR,SCAN_NEXT };
+enum SCAN_DIRS
+{
+ SCAN_SKIPDIRS, // Skip directories, but recurse for files if recursion mode is enabled.
+ SCAN_GETDIRS, // Get subdirectories in recurse mode.
+ SCAN_GETDIRSTWICE, // Get the directory name both before and after the list of files it contains.
+ SCAN_GETCURDIRS // Get subdirectories in current directory even in RECURSE_NONE mode.
+};
+
+enum SCAN_CODE { SCAN_SUCCESS,SCAN_DONE,SCAN_ERROR,SCAN_NEXT };
#define MAXSCANDEPTH (NM/2)
@@ -12,8 +18,11 @@ class CommandData;
class ScanTree
{
private:
- bool PrepareMasks();
- int FindProc(FindData *FindData);
+ bool ExpandFolderMask();
+ bool GetFilteredMask();
+ bool GetNextMask();
+ SCAN_CODE FindProc(FindData *FD);
+ void ScanError(bool &Error);
FindFile *FindStack[MAXSCANDEPTH];
int Depth;
@@ -21,35 +30,49 @@ class ScanTree
int SetAllMaskDepth;
StringList *FileMasks;
- int Recurse;
+ RECURSE_MODE Recurse;
bool GetLinks;
- int GetDirs;
+ SCAN_DIRS GetDirs;
int Errors;
- // set when processing paths like c:\ (root directory without wildcards)
+ // Set when processing paths like c:\ (root directory without wildcards).
bool ScanEntireDisk;
- char CurMask[NM];
- wchar CurMaskW[NM];
- char OrigCurMask[NM];
- wchar OrigCurMaskW[NM];
+ wchar CurMask[NM];
+ wchar OrigCurMask[NM];
+
+ // Store all folder masks generated from folder wildcard mask in non-recursive mode.
+ StringList ExpandedFolderList;
+
+ // Store a filter string for folder wildcard in recursive mode.
+ StringList FilterList;
+
+ // Save the list of unreadable dirs here.
+ StringList *ErrDirList;
+ Array<uint> *ErrDirSpecPathLength;
+
+ // Set if processing a folder wildcard mask.
+ bool FolderWildcards;
+
bool SearchAllInRoot;
- bool FastFindFile;
- int SpecPathLength;
- int SpecPathLengthW;
+ size_t SpecPathLength;
- char ErrArcName[NM];
+ wchar ErrArcName[NM];
CommandData *Cmd;
public:
- ScanTree(StringList *FileMasks,int Recurse,bool GetLinks,int GetDirs);
+ ScanTree(StringList *FileMasks,RECURSE_MODE Recurse,bool GetLinks,SCAN_DIRS GetDirs);
~ScanTree();
- int GetNext(FindData *FindData);
- int GetSpecPathLength() {return(SpecPathLength);};
- int GetSpecPathLengthW() {return(SpecPathLengthW);};
- int GetErrors() {return(Errors);};
- void SetErrArcName(const char *Name) {strcpy(ErrArcName,Name);}
+ SCAN_CODE GetNext(FindData *FindData);
+ size_t GetSpecPathLength() {return SpecPathLength;}
+ int GetErrors() {return Errors;};
+ void SetErrArcName(const wchar *Name) {wcsncpyz(ErrArcName,Name,ASIZE(ErrArcName));}
void SetCommandData(CommandData *Cmd) {ScanTree::Cmd=Cmd;}
+ void SetErrDirList(StringList *List,Array<uint> *Lengths)
+ {
+ ErrDirList=List;
+ ErrDirSpecPathLength=Lengths;
+ }
};
#endif
diff --git a/unrar/unrar/secpassword.cpp b/unrar/unrar/secpassword.cpp
new file mode 100644
index 0000000..08da549
--- /dev/null
+++ b/unrar/unrar/secpassword.cpp
@@ -0,0 +1,218 @@
+#include "rar.hpp"
+
+#if defined(_WIN_ALL)
+typedef BOOL (WINAPI *CRYPTPROTECTMEMORY)(LPVOID pData,DWORD cbData,DWORD dwFlags);
+typedef BOOL (WINAPI *CRYPTUNPROTECTMEMORY)(LPVOID pData,DWORD cbData,DWORD dwFlags);
+
+#ifndef CRYPTPROTECTMEMORY_BLOCK_SIZE
+#define CRYPTPROTECTMEMORY_BLOCK_SIZE 16
+#define CRYPTPROTECTMEMORY_SAME_PROCESS 0x00
+#define CRYPTPROTECTMEMORY_CROSS_PROCESS 0x01
+#endif
+
+class CryptLoader
+{
+ private:
+ HMODULE hCrypt;
+ bool LoadCalled;
+ public:
+ CryptLoader()
+ {
+ hCrypt=NULL;
+ pCryptProtectMemory=NULL;
+ pCryptUnprotectMemory=NULL;
+ LoadCalled=false;
+ }
+ ~CryptLoader()
+ {
+ if (hCrypt!=NULL)
+ FreeLibrary(hCrypt);
+ hCrypt=NULL;
+ pCryptProtectMemory=NULL;
+ pCryptUnprotectMemory=NULL;
+ };
+ void Load()
+ {
+ if (!LoadCalled)
+ {
+ hCrypt = LoadSysLibrary(L"Crypt32.dll");
+ if (hCrypt != NULL)
+ {
+ // Available since Vista.
+ pCryptProtectMemory = (CRYPTPROTECTMEMORY)GetProcAddress(hCrypt, "CryptProtectMemory");
+ pCryptUnprotectMemory = (CRYPTUNPROTECTMEMORY)GetProcAddress(hCrypt, "CryptUnprotectMemory");
+ }
+ LoadCalled=true;
+ }
+ }
+
+ CRYPTPROTECTMEMORY pCryptProtectMemory;
+ CRYPTUNPROTECTMEMORY pCryptUnprotectMemory;
+};
+
+// We need to call FreeLibrary when RAR is exiting.
+static CryptLoader GlobalCryptLoader;
+#endif
+
+SecPassword::SecPassword()
+{
+ Set(L"");
+}
+
+
+SecPassword::~SecPassword()
+{
+ Clean();
+}
+
+
+void SecPassword::Clean()
+{
+ PasswordSet=false;
+ if (Password.size()>0)
+ cleandata(&Password[0],Password.size()*sizeof(Password[0]));
+}
+
+
+// When we call memset in end of function to clean local variables
+// for security reason, compiler optimizer can remove such call.
+// So we use our own function for this purpose.
+void cleandata(void *data,size_t size)
+{
+ if (data==NULL || size==0)
+ return;
+#if defined(_WIN_ALL) && defined(_MSC_VER)
+ SecureZeroMemory(data,size);
+#else
+ // 'volatile' is required. Otherwise optimizers can remove this function
+ // if cleaning local variables, which are not used after that.
+ volatile byte *d = (volatile byte *)data;
+ for (size_t i=0;i<size;i++)
+ d[i]=0;
+#endif
+}
+
+
+// We got a complain from user that it is possible to create WinRAR dump
+// with "Create dump file" command in Windows Task Manager and then easily
+// locate Unicode password string in the dump. It is unsecure if several
+// people share the same computer and somebody left WinRAR copy with entered
+// password. So we decided to obfuscate the password to make it more difficult
+// to find it in dump.
+void SecPassword::Process(const wchar *Src,size_t SrcSize,wchar *Dst,size_t DstSize,bool Encode)
+{
+ // Source string can be shorter than destination as in case when we process
+ // -p<pwd> parameter, so we need to take into account both sizes.
+ memcpy(Dst,Src,Min(SrcSize,DstSize)*sizeof(*Dst));
+ SecHideData(Dst,DstSize*sizeof(*Dst),Encode,false);
+}
+
+
+void SecPassword::Get(wchar *Psw,size_t MaxSize)
+{
+ if (PasswordSet)
+ {
+ Process(&Password[0],Password.size(),Psw,MaxSize,false);
+ Psw[MaxSize-1]=0;
+ }
+ else
+ *Psw=0;
+}
+
+
+
+
+void SecPassword::Set(const wchar *Psw)
+{
+ // Eliminate any traces of previously stored password for security reason
+ // in case it was longer than new one.
+ Clean();
+
+ if (*Psw!=0)
+ {
+ PasswordSet=true;
+ Process(Psw,wcslen(Psw)+1,&Password[0],Password.size(),true);
+ }
+}
+
+
+size_t SecPassword::Length()
+{
+ wchar Plain[MAXPASSWORD];
+ Get(Plain,ASIZE(Plain));
+ size_t Length=wcslen(Plain);
+ cleandata(Plain,sizeof(Plain));
+ return Length;
+}
+
+
+bool SecPassword::operator == (SecPassword &psw)
+{
+ // We cannot compare encoded data directly, because there is no guarantee
+ // than encryption function will always produce the same result for same
+ // data (salt?) and because we do not clean the rest of password buffer
+ // after trailing zero before encoding password. So we decode first.
+ wchar Plain1[MAXPASSWORD],Plain2[MAXPASSWORD];
+ Get(Plain1,ASIZE(Plain1));
+ psw.Get(Plain2,ASIZE(Plain2));
+ bool Result=wcscmp(Plain1,Plain2)==0;
+ cleandata(Plain1,sizeof(Plain1));
+ cleandata(Plain2,sizeof(Plain2));
+ return Result;
+}
+
+
+// Set CrossProcess to true if we need to pass a password to another process.
+// We use CrossProcess when transferring parameters to UAC elevated WinRAR
+// and Windows GUI SFX modules.
+void SecHideData(void *Data,size_t DataSize,bool Encode,bool CrossProcess)
+{
+ // CryptProtectMemory is not available in UWP and CryptProtectData
+ // increases data size not allowing in place conversion.
+#if defined(_WIN_ALL)
+ // Try to utilize the secure Crypt[Un]ProtectMemory if possible.
+ if (GlobalCryptLoader.pCryptProtectMemory==NULL)
+ GlobalCryptLoader.Load();
+ size_t Aligned=DataSize-DataSize%CRYPTPROTECTMEMORY_BLOCK_SIZE;
+ DWORD Flags=CrossProcess ? CRYPTPROTECTMEMORY_CROSS_PROCESS : CRYPTPROTECTMEMORY_SAME_PROCESS;
+ if (Encode)
+ {
+ if (GlobalCryptLoader.pCryptProtectMemory!=NULL)
+ {
+ if (!GlobalCryptLoader.pCryptProtectMemory(Data,DWORD(Aligned),Flags))
+ {
+ ErrHandler.GeneralErrMsg(L"CryptProtectMemory failed");
+ ErrHandler.SysErrMsg();
+ ErrHandler.Exit(RARX_FATAL);
+ }
+ return;
+ }
+ }
+ else
+ {
+ if (GlobalCryptLoader.pCryptUnprotectMemory!=NULL)
+ {
+ if (!GlobalCryptLoader.pCryptUnprotectMemory(Data,DWORD(Aligned),Flags))
+ {
+ ErrHandler.GeneralErrMsg(L"CryptUnprotectMemory failed");
+ ErrHandler.SysErrMsg();
+ ErrHandler.Exit(RARX_FATAL);
+ }
+ return;
+ }
+ }
+#endif
+
+ // CryptProtectMemory is not available, so only slightly obfuscate data.
+ uint Key;
+#ifdef _WIN_ALL
+ Key=GetCurrentProcessId();
+#elif defined(_UNIX)
+ Key=getpid();
+#else
+ Key=0; // Just an arbitrary value.
+#endif
+
+ for (size_t I=0;I<DataSize;I++)
+ *((byte *)Data+I)^=Key+I+75;
+}
diff --git a/unrar/unrar/secpassword.hpp b/unrar/unrar/secpassword.hpp
new file mode 100644
index 0000000..5284bce
--- /dev/null
+++ b/unrar/unrar/secpassword.hpp
@@ -0,0 +1,28 @@
+#ifndef _RAR_SECURE_PASSWORD_
+#define _RAR_SECURE_PASSWORD_
+
+// Store a password securely (if data encryption is provided by OS)
+// or obfuscated to make search for password in memory dump less trivial.
+class SecPassword
+{
+ private:
+ void Process(const wchar *Src,size_t SrcSize,wchar *Dst,size_t DstSize,bool Encode);
+
+ std::vector<wchar> Password = std::vector<wchar>(MAXPASSWORD);
+ bool PasswordSet;
+ public:
+ SecPassword();
+ ~SecPassword();
+ void Clean();
+ void Get(wchar *Psw,size_t MaxSize);
+ void Set(const wchar *Psw);
+ bool IsSet() {return PasswordSet;}
+ size_t Length();
+ bool operator == (SecPassword &psw);
+};
+
+
+void cleandata(void *data,size_t size);
+void SecHideData(void *Data,size_t DataSize,bool Encode,bool CrossProcess);
+
+#endif
diff --git a/unrar/unrar/sha1.cpp b/unrar/unrar/sha1.cpp
index 686ff94..562aadd 100644
--- a/unrar/unrar/sha1.cpp
+++ b/unrar/unrar/sha1.cpp
@@ -4,228 +4,201 @@
SHA-1 in C
By Steve Reid <steve@edmweb.com>
100% Public Domain
-
-Test Vectors (from FIPS PUB 180-1)
-"abc"
- A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
-"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
- 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
-A million repetitions of "a"
- 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
*/
-#if !defined(LITTLE_ENDIAN) && !defined(BIG_ENDIAN)
- #if defined(_M_IX86) || defined(_M_I86) || defined(__alpha)
- #define LITTLE_ENDIAN
- #else
- #error "LITTLE_ENDIAN or BIG_ENDIAN must be defined"
- #endif
+#ifndef SFX_MODULE
+#define SHA1_UNROLL
#endif
-/* #define SHA1HANDSOFF * Copies data before messing with it. */
-
-#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
-
/* blk0() and blk() perform the initial expand. */
/* I got the idea of expanding during the round function from SSLeay */
#ifdef LITTLE_ENDIAN
-#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
- |(rol(block->l[i],8)&0x00FF00FF))
+#define blk0(i) (block->l[i] = ByteSwap32(block->l[i]))
#else
#define blk0(i) block->l[i]
#endif
-#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
+#define blk(i) (block->l[i&15] = rotl32(block->l[(i+13)&15]^block->l[(i+8)&15] \
^block->l[(i+2)&15]^block->l[i&15],1))
/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
-#define R0(v,w,x,y,z,i) {z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);}
-#define R1(v,w,x,y,z,i) {z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);}
-#define R2(v,w,x,y,z,i) {z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);}
-#define R3(v,w,x,y,z,i) {z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);}
-#define R4(v,w,x,y,z,i) {z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);}
-
+#define R0(v,w,x,y,z,i) {z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rotl32(v,5);w=rotl32(w,30);}
+#define R1(v,w,x,y,z,i) {z+=((w&(x^y))^y)+blk(i)+0x5A827999+rotl32(v,5);w=rotl32(w,30);}
+#define R2(v,w,x,y,z,i) {z+=(w^x^y)+blk(i)+0x6ED9EBA1+rotl32(v,5);w=rotl32(w,30);}
+#define R3(v,w,x,y,z,i) {z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rotl32(v,5);w=rotl32(w,30);}
+#define R4(v,w,x,y,z,i) {z+=(w^x^y)+blk(i)+0xCA62C1D6+rotl32(v,5);w=rotl32(w,30);}
/* Hash a single 512-bit block. This is the core of the algorithm. */
-
-void SHA1Transform(uint32 state[5], unsigned char buffer[64], bool handsoff)
+void SHA1Transform(uint32 state[5], uint32 workspace[16], const byte buffer[64], bool inplace)
{
uint32 a, b, c, d, e;
- typedef union {
+
+ union CHAR64LONG16
+ {
unsigned char c[64];
uint32 l[16];
-} CHAR64LONG16;
-CHAR64LONG16* block;
-static unsigned char workspace[64];
- if (handsoff)
- {
- block = (CHAR64LONG16*)workspace;
- memcpy(block, buffer, 64);
- }
- else
- block = (CHAR64LONG16*)buffer;
-#ifdef SFX_MODULE
- static int pos[80][5];
- static bool pinit=false;
- if (!pinit)
- {
- for (int I=0,P=0;I<80;I++,P=(P ? P-1:4))
- {
- pos[I][0]=P;
- pos[I][1]=(P+1)%5;
- pos[I][2]=(P+2)%5;
- pos[I][3]=(P+3)%5;
- pos[I][4]=(P+4)%5;
- }
- pinit=true;
- }
- uint32 s[5];
- for (int I=0;I<sizeof(s)/sizeof(s[0]);I++)
- s[I]=state[I];
-
- for (int I=0;I<16;I++)
- R0(s[pos[I][0]],s[pos[I][1]],s[pos[I][2]],s[pos[I][3]],s[pos[I][4]],I);
- for (int I=16;I<20;I++)
- R1(s[pos[I][0]],s[pos[I][1]],s[pos[I][2]],s[pos[I][3]],s[pos[I][4]],I);
- for (int I=20;I<40;I++)
- R2(s[pos[I][0]],s[pos[I][1]],s[pos[I][2]],s[pos[I][3]],s[pos[I][4]],I);
- for (int I=40;I<60;I++)
- R3(s[pos[I][0]],s[pos[I][1]],s[pos[I][2]],s[pos[I][3]],s[pos[I][4]],I);
- for (int I=60;I<80;I++)
- R4(s[pos[I][0]],s[pos[I][1]],s[pos[I][2]],s[pos[I][3]],s[pos[I][4]],I);
-
- for (int I=0;I<sizeof(s)/sizeof(s[0]);I++)
- state[I]+=s[I];
+ } *block;
+
+ if (inplace)
+ block = (CHAR64LONG16*)buffer;
+ else
+ {
+ block = (CHAR64LONG16*)workspace;
+ memcpy(block, buffer, 64);
+ }
+
+ /* Copy context->state[] to working vars */
+ a = state[0];
+ b = state[1];
+ c = state[2];
+ d = state[3];
+ e = state[4];
+
+#ifdef SHA1_UNROLL
+ /* 4 rounds of 20 operations each. Loop unrolled. */
+ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+ R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+ R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+ R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+ R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+ R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+ R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+ R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+ R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+ R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+ R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+ R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+ R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+ R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+ R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+ R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+ R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+ R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+ R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+ R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
#else
- /* Copy context->state[] to working vars */
- a = state[0];
- b = state[1];
- c = state[2];
- d = state[3];
- e = state[4];
- /* 4 rounds of 20 operations each. Loop unrolled. */
- R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
- R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
- R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
- R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
- R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
- R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
- R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
- R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
- R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
- R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
- R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
- R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
- R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
- R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
- R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
- R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
- R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
- R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
- R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
- R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
- /* Add the working vars back into context.state[] */
- state[0] += a;
- state[1] += b;
- state[2] += c;
- state[3] += d;
- state[4] += e;
-
- /* Wipe variables */
- a = b = c = d = e = 0;
- memset(&a,0,sizeof(a));
+ for (uint I=0;;I+=5)
+ {
+ R0(a,b,c,d,e, I+0); if (I==15) break;
+ R0(e,a,b,c,d, I+1); R0(d,e,a,b,c, I+2);
+ R0(c,d,e,a,b, I+3); R0(b,c,d,e,a, I+4);
+ }
+ R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+ for (uint I=20;I<=35;I+=5)
+ {
+ R2(a,b,c,d,e,I+0); R2(e,a,b,c,d,I+1); R2(d,e,a,b,c,I+2);
+ R2(c,d,e,a,b,I+3); R2(b,c,d,e,a,I+4);
+ }
+ for (uint I=40;I<=55;I+=5)
+ {
+ R3(a,b,c,d,e,I+0); R3(e,a,b,c,d,I+1); R3(d,e,a,b,c,I+2);
+ R3(c,d,e,a,b,I+3); R3(b,c,d,e,a,I+4);
+ }
+ for (uint I=60;I<=75;I+=5)
+ {
+ R4(a,b,c,d,e,I+0); R4(e,a,b,c,d,I+1); R4(d,e,a,b,c,I+2);
+ R4(c,d,e,a,b,I+3); R4(b,c,d,e,a,I+4);
+ }
#endif
+ /* Add the working vars back into context.state[] */
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
}
/* Initialize new context */
-
-void hash_initial(hash_context* context)
+void sha1_init(sha1_context* context)
{
- /* SHA1 initialization constants */
- context->state[0] = 0x67452301;
- context->state[1] = 0xEFCDAB89;
- context->state[2] = 0x98BADCFE;
- context->state[3] = 0x10325476;
- context->state[4] = 0xC3D2E1F0;
- context->count[0] = context->count[1] = 0;
+ context->count = 0;
+ /* SHA1 initialization constants */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xEFCDAB89;
+ context->state[2] = 0x98BADCFE;
+ context->state[3] = 0x10325476;
+ context->state[4] = 0xC3D2E1F0;
}
/* Run your data through this. */
-void hash_process( hash_context * context, unsigned char * data, unsigned len,
- bool handsoff )
+void sha1_process( sha1_context * context, const unsigned char * data, size_t len)
{
-unsigned int i, j;
-uint blen = ((uint)len)<<3;
-
- j = (context->count[0] >> 3) & 63;
- if ((context->count[0] += blen) < blen ) context->count[1]++;
- context->count[1] += (len >> 29);
- if ((j + len) > 63) {
- memcpy(&context->buffer[j], data, (i = 64-j));
- SHA1Transform(context->state, context->buffer, handsoff);
- for ( ; i + 63 < len; i += 64) {
-#ifdef ALLOW_NOT_ALIGNED_INT
- SHA1Transform(context->state, &data[i], handsoff);
-#else
- unsigned char buffer[64];
- memcpy(buffer,data+i,sizeof(buffer));
- SHA1Transform(context->state, buffer, handsoff);
- memcpy(data+i,buffer,sizeof(buffer));
-#endif
-#ifdef BIG_ENDIAN
- if (!handsoff)
- {
- unsigned char *d=data+i;
- for (int k=0;k<64;k+=4)
- {
- byte b0=d[k],b1=d[k+1];
- d[k]=d[k+3];
- d[k+1]=d[k+2];
- d[k+2]=b1;
- d[k+3]=b0;
- }
- }
-#endif
- }
- j = 0;
+ size_t i, j = (size_t)(context->count & 63);
+ context->count += len;
+
+ if ((j + len) > 63)
+ {
+ memcpy(context->buffer+j, data, (i = 64-j));
+ uint32 workspace[16];
+ SHA1Transform(context->state, workspace, context->buffer, true);
+ for ( ; i + 63 < len; i += 64)
+ SHA1Transform(context->state, workspace, data+i, false);
+ j = 0;
+ }
+ else
+ i = 0;
+ if (len > i)
+ memcpy(context->buffer+j, data+i, len - i);
+}
+
+
+void sha1_process_rar29(sha1_context *context, const unsigned char *data, size_t len)
+{
+ size_t i, j = (size_t)(context->count & 63);
+ context->count += len;
+
+ if ((j + len) > 63)
+ {
+ memcpy(context->buffer+j, data, (i = 64-j));
+ uint32 workspace[16];
+ SHA1Transform(context->state, workspace, context->buffer, true);
+ for ( ; i + 63 < len; i += 64)
+ {
+ SHA1Transform(context->state, workspace, data+i, false);
+ for (uint k = 0; k < 16; k++)
+ RawPut4(workspace[k],(void*)(data+i+k*4));
}
- else i = 0;
- if (len > i)
- memcpy(&context->buffer[j], &data[i], len - i);
+ j = 0;
+ }
+ else
+ i = 0;
+ if (len > i)
+ memcpy(context->buffer+j, data+i, len - i);
}
/* Add padding and return the message digest. */
-
-void hash_final( hash_context* context, uint32 digest[5], bool handsoff)
+void sha1_done( sha1_context* context, uint32 digest[5])
{
-uint i, j;
-unsigned char finalcount[8];
-
- for (i = 0; i < 8; i++) {
- finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
- >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
- }
- unsigned char ch=(unsigned char)'\200';
- hash_process(context, &ch, 1, handsoff);
- while ((context->count[0] & 504) != 448) {
- ch=0;
- hash_process(context, &ch, 1, handsoff);
- }
- hash_process(context, finalcount, 8, handsoff); /* Should cause a SHA1Transform() */
- for (i = 0; i < 5; i++) {
- digest[i] = context->state[i] & 0xffffffff;
+ uint32 workspace[16];
+ uint64 BitLength = context->count * 8;
+ uint BufPos = (uint)context->count & 0x3f;
+ context->buffer[BufPos++] = 0x80; // Padding the message with "1" bit.
+
+ if (BufPos!=56) // We need 56 bytes block followed by 8 byte length.
+ {
+ if (BufPos>56)
+ {
+ while (BufPos<64)
+ context->buffer[BufPos++] = 0;
+ BufPos=0;
}
- /* Wipe variables */
- memset(&i,0,sizeof(i));
- memset(&j,0,sizeof(j));
- memset(context->buffer, 0, 64);
- memset(context->state, 0, 20);
- memset(context->count, 0, 8);
- memset(&finalcount, 0, 8);
- if (handsoff)
- SHA1Transform(context->state, context->buffer, true);
+ if (BufPos==0)
+ SHA1Transform(context->state, workspace, context->buffer, true);
+ memset(context->buffer+BufPos,0,56-BufPos);
+ }
+
+ RawPutBE4((uint32)(BitLength>>32), context->buffer + 56);
+ RawPutBE4((uint32)(BitLength), context->buffer + 60);
+
+ SHA1Transform(context->state, workspace, context->buffer, true);
+
+ for (uint i = 0; i < 5; i++)
+ digest[i] = context->state[i];
+
+ /* Wipe variables */
+ sha1_init(context);
}
diff --git a/unrar/unrar/sha1.hpp b/unrar/unrar/sha1.hpp
index 155c62a..7c0b7fb 100644
--- a/unrar/unrar/sha1.hpp
+++ b/unrar/unrar/sha1.hpp
@@ -1,17 +1,15 @@
#ifndef _RAR_SHA1_
#define _RAR_SHA1_
-#define HW 5
-
typedef struct {
uint32 state[5];
- uint32 count[2];
+ uint64 count;
unsigned char buffer[64];
-} hash_context;
+} sha1_context;
-void hash_initial( hash_context * c );
-void hash_process( hash_context * c, unsigned char * data, unsigned len,
- bool handsoff);
-void hash_final( hash_context * c, uint32[HW], bool handsoff);
+void sha1_init( sha1_context * c );
+void sha1_process(sha1_context * c, const byte *data, size_t len);
+void sha1_process_rar29(sha1_context *context, const unsigned char *data, size_t len);
+void sha1_done( sha1_context * c, uint32 digest[5] );
#endif
diff --git a/unrar/unrar/sha256.cpp b/unrar/unrar/sha256.cpp
new file mode 100644
index 0000000..f90d2c0
--- /dev/null
+++ b/unrar/unrar/sha256.cpp
@@ -0,0 +1,148 @@
+#include "rar.hpp"
+#include "sha256.hpp"
+
+static const uint32 K[64] =
+{
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+ 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+ 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+ 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+ 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+ 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+ 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+};
+
+// SHA-256 functions. We could optimize Ch and Maj a little,
+// but with no visible speed benefit.
+#define Ch(x, y, z) ((x & y) ^ (~x & z))
+#define Maj(x, y, z) ((x & y) ^ (x & z) ^ (y & z))
+
+// Sigma functions.
+#define Sg0(x) (rotr32(x, 2) ^ rotr32(x,13) ^ rotr32(x, 22))
+#define Sg1(x) (rotr32(x, 6) ^ rotr32(x,11) ^ rotr32(x, 25))
+#define sg0(x) (rotr32(x, 7) ^ rotr32(x,18) ^ (x >> 3))
+#define sg1(x) (rotr32(x,17) ^ rotr32(x,19) ^ (x >> 10))
+
+void sha256_init(sha256_context *ctx)
+{
+ ctx->H[0] = 0x6a09e667; // Set the initial hash value.
+ ctx->H[1] = 0xbb67ae85;
+ ctx->H[2] = 0x3c6ef372;
+ ctx->H[3] = 0xa54ff53a;
+ ctx->H[4] = 0x510e527f;
+ ctx->H[5] = 0x9b05688c;
+ ctx->H[6] = 0x1f83d9ab;
+ ctx->H[7] = 0x5be0cd19;
+ ctx->Count = 0; // Processed data counter.
+}
+
+
+static void sha256_transform(sha256_context *ctx)
+{
+ uint32 W[64]; // Words of message schedule.
+ uint32 v[8]; // FIPS a, b, c, d, e, f, g, h working variables.
+
+ // Prepare message schedule.
+ for (uint I = 0; I < 16; I++)
+ W[I] = RawGetBE4(ctx->Buffer + I * 4);
+ for (uint I = 16; I < 64; I++)
+ W[I] = sg1(W[I-2]) + W[I-7] + sg0(W[I-15]) + W[I-16];
+
+ uint32 *H=ctx->H;
+ v[0]=H[0]; v[1]=H[1]; v[2]=H[2]; v[3]=H[3];
+ v[4]=H[4]; v[5]=H[5]; v[6]=H[6]; v[7]=H[7];
+
+ for (uint I = 0; I < 64; I++)
+ {
+ uint T1 = v[7] + Sg1(v[4]) + Ch(v[4], v[5], v[6]) + K[I] + W[I];
+
+ // It is possible to eliminate variable copying if we unroll loop
+ // and rename variables every time. But my test did not show any speed
+ // gain on i7 for such full or partial unrolling.
+ v[7] = v[6];
+ v[6] = v[5];
+ v[5] = v[4];
+ v[4] = v[3] + T1;
+
+ // It works a little faster when moved here from beginning of loop.
+ uint T2 = Sg0(v[0]) + Maj(v[0], v[1], v[2]);
+
+ v[3] = v[2];
+ v[2] = v[1];
+ v[1] = v[0];
+ v[0] = T1 + T2;
+ }
+
+ H[0]+=v[0]; H[1]+=v[1]; H[2]+=v[2]; H[3]+=v[3];
+ H[4]+=v[4]; H[5]+=v[5]; H[6]+=v[6]; H[7]+=v[7];
+}
+
+
+void sha256_process(sha256_context *ctx, const void *Data, size_t Size)
+{
+ const byte *Src=(const byte *)Data;
+ size_t BufPos = (uint)ctx->Count & 0x3f;
+ ctx->Count+=Size;
+ while (Size > 0)
+ {
+ size_t BufSpace=sizeof(ctx->Buffer)-BufPos;
+ size_t CopySize=Size>BufSpace ? BufSpace:Size;
+
+ memcpy(ctx->Buffer+BufPos,Src,CopySize);
+
+ Src+=CopySize;
+ BufPos+=CopySize;
+ Size-=CopySize;
+ if (BufPos == 64)
+ {
+ BufPos = 0;
+ sha256_transform(ctx);
+ }
+ }
+}
+
+
+void sha256_done(sha256_context *ctx, byte *Digest)
+{
+ uint64 BitLength = ctx->Count * 8;
+ uint BufPos = (uint)ctx->Count & 0x3f;
+ ctx->Buffer[BufPos++] = 0x80; // Padding the message with "1" bit.
+
+ if (BufPos!=56) // We need 56 bytes block followed by 8 byte length.
+ {
+ if (BufPos>56)
+ {
+ while (BufPos<64)
+ ctx->Buffer[BufPos++] = 0;
+ BufPos=0;
+ }
+ if (BufPos==0)
+ sha256_transform(ctx);
+ memset(ctx->Buffer+BufPos,0,56-BufPos);
+ }
+
+ RawPutBE4((uint32)(BitLength>>32), ctx->Buffer + 56);
+ RawPutBE4((uint32)(BitLength), ctx->Buffer + 60);
+
+ sha256_transform(ctx);
+
+ RawPutBE4(ctx->H[0], Digest + 0);
+ RawPutBE4(ctx->H[1], Digest + 4);
+ RawPutBE4(ctx->H[2], Digest + 8);
+ RawPutBE4(ctx->H[3], Digest + 12);
+ RawPutBE4(ctx->H[4], Digest + 16);
+ RawPutBE4(ctx->H[5], Digest + 20);
+ RawPutBE4(ctx->H[6], Digest + 24);
+ RawPutBE4(ctx->H[7], Digest + 28);
+
+ sha256_init(ctx);
+}
diff --git a/unrar/unrar/sha256.hpp b/unrar/unrar/sha256.hpp
new file mode 100644
index 0000000..b6837e7
--- /dev/null
+++ b/unrar/unrar/sha256.hpp
@@ -0,0 +1,17 @@
+#ifndef _RAR_SHA256_
+#define _RAR_SHA256_
+
+#define SHA256_DIGEST_SIZE 32
+
+typedef struct
+{
+ uint32 H[8];
+ uint64 Count;
+ byte Buffer[64];
+} sha256_context;
+
+void sha256_init(sha256_context *ctx);
+void sha256_process(sha256_context *ctx, const void *Data, size_t Size);
+void sha256_done(sha256_context *ctx, byte *Digest);
+
+#endif
diff --git a/unrar/unrar/smallfn.cpp b/unrar/unrar/smallfn.cpp
index 51997c5..81259d0 100644
--- a/unrar/unrar/smallfn.cpp
+++ b/unrar/unrar/smallfn.cpp
@@ -1,23 +1,19 @@
#include "rar.hpp"
-int ToPercent(Int64 N1,Int64 N2)
+int ToPercent(int64 N1,int64 N2)
{
if (N2<N1)
- return(100);
- return(ToPercentUnlim(N1,N2));
+ return 100;
+ return ToPercentUnlim(N1,N2);
}
-/* allows percent larger than 100 */
-int ToPercentUnlim(Int64 N1,Int64 N2)
+
+// Allows the percent larger than 100.
+int ToPercentUnlim(int64 N1,int64 N2)
{
if (N2==0)
- return(0);
- return(int64to32(N1*100/N2));
+ return 0;
+ return (int)(N1*100/N2);
}
-void RARInitData()
-{
- InitCRC();
- ErrHandler.Clean();
-}
diff --git a/unrar/unrar/smallfn.hpp b/unrar/unrar/smallfn.hpp
index 83266f0..f53daa8 100644
--- a/unrar/unrar/smallfn.hpp
+++ b/unrar/unrar/smallfn.hpp
@@ -1,8 +1,8 @@
#ifndef _RAR_SMALLFN_
#define _RAR_SMALLFN_
-int ToPercent(Int64 N1,Int64 N2);
-int ToPercentUnlim(Int64 N1,Int64 N2);
+int ToPercent(int64 N1,int64 N2);
+int ToPercentUnlim(int64 N1,int64 N2);
void RARInitData();
#endif
diff --git a/unrar/unrar/strfn.cpp b/unrar/unrar/strfn.cpp
index 52b7a5f..7617f7a 100644
--- a/unrar/unrar/strfn.cpp
+++ b/unrar/unrar/strfn.cpp
@@ -2,200 +2,469 @@
const char *NullToEmpty(const char *Str)
{
- return(Str==NULL ? "":Str);
+ return Str==NULL ? "":Str;
}
const wchar *NullToEmpty(const wchar *Str)
{
- return(Str==NULL ? L"":Str);
+ return Str==NULL ? L"":Str;
}
-char *IntNameToExt(const char *Name)
+void IntToExt(const char *Src,char *Dest,size_t DestSize)
{
- static char OutName[NM];
- IntToExt(Name,OutName);
- return(OutName);
-}
-
-
-void ExtToInt(const char *Src,char *Dest)
-{
-#if defined(_WIN_32)
- CharToOem(Src,Dest);
+#ifdef _WIN_ALL
+ // OemToCharBuff does not stop at 0, so let's check source length.
+ size_t SrcLength=strlen(Src)+1;
+ if (DestSize>SrcLength)
+ DestSize=SrcLength;
+ OemToCharBuffA(Src,Dest,(DWORD)DestSize);
+ Dest[DestSize-1]=0;
#else
if (Dest!=Src)
- strcpy(Dest,Src);
+ strncpyz(Dest,Src,DestSize);
#endif
}
-void IntToExt(const char *Src,char *Dest)
+// Convert archived names and comments to Unicode.
+// Allows user to select a code page in GUI.
+void ArcCharToWide(const char *Src,wchar *Dest,size_t DestSize,ACTW_ENCODING Encoding)
{
-#if defined(_WIN_32)
- OemToChar(Src,Dest);
-#else
- if (Dest!=Src)
- strcpy(Dest,Src);
+#if defined(_WIN_ALL) // Console Windows RAR.
+ if (Encoding==ACTW_UTF8)
+ UtfToWide(Src,Dest,DestSize);
+ else
+ {
+ Array<char> NameA;
+ if (Encoding==ACTW_OEM)
+ {
+ NameA.Alloc(DestSize+1);
+ IntToExt(Src,&NameA[0],NameA.Size());
+ Src=&NameA[0];
+ }
+ CharToWide(Src,Dest,DestSize);
+ }
+#else // RAR for Unix.
+ if (Encoding==ACTW_UTF8)
+ UtfToWide(Src,Dest,DestSize);
+ else
+ CharToWide(Src,Dest,DestSize);
#endif
+ // Ensure that we return a zero terminate string for security reason.
+ // While [Jni]CharToWide might already do it, be protected in case of future
+ // changes in these functions.
+ if (DestSize>0)
+ Dest[DestSize-1]=0;
}
-char* strlower(char *Str)
+
+
+int stricomp(const char *s1,const char *s2)
{
-#ifdef _WIN_32
- CharLower((LPTSTR)Str);
+#ifdef _WIN_ALL
+ return CompareStringA(LOCALE_USER_DEFAULT,NORM_IGNORECASE|SORT_STRINGSORT,s1,-1,s2,-1)-2;
#else
- for (char *ChPtr=Str;*ChPtr;ChPtr++)
- *ChPtr=(char)loctolower(*ChPtr);
+ while (toupper(*s1)==toupper(*s2))
+ {
+ if (*s1==0)
+ return 0;
+ s1++;
+ s2++;
+ }
+ return s1 < s2 ? -1 : 1;
#endif
- return(Str);
}
-char* strupper(char *Str)
+int strnicomp(const char *s1,const char *s2,size_t n)
{
-#ifdef _WIN_32
- CharUpper((LPTSTR)Str);
+#ifdef _WIN_ALL
+ // If we specify 'n' exceeding the actual string length, CompareString goes
+ // beyond the trailing zero and compares garbage. So we need to limit 'n'
+ // to real string length.
+ // It is important to use strnlen (or memchr(...,0)) instead of strlen,
+ // because data can be not zero terminated.
+ size_t l1=Min(strnlen(s1,n),n);
+ size_t l2=Min(strnlen(s2,n),n);
+ return CompareStringA(LOCALE_USER_DEFAULT,NORM_IGNORECASE|SORT_STRINGSORT,s1,(int)l1,s2,(int)l2)-2;
#else
- for (char *ChPtr=Str;*ChPtr;ChPtr++)
- *ChPtr=(char)loctoupper(*ChPtr);
+ if (n==0)
+ return 0;
+ while (toupper(*s1)==toupper(*s2))
+ {
+ if (*s1==0 || --n==0)
+ return 0;
+ s1++;
+ s2++;
+ }
+ return s1 < s2 ? -1 : 1;
#endif
- return(Str);
}
-int stricomp(const char *Str1,const char *Str2)
+wchar* RemoveEOL(wchar *Str)
{
- char S1[NM*2],S2[NM*2];
- strncpyz(S1,Str1,ASIZE(S1));
- strncpyz(S2,Str2,ASIZE(S2));
- return(strcmp(strupper(S1),strupper(S2)));
+ for (int I=(int)wcslen(Str)-1;I>=0 && (Str[I]=='\r' || Str[I]=='\n' || Str[I]==' ' || Str[I]=='\t');I--)
+ Str[I]=0;
+ return Str;
}
-int strnicomp(const char *Str1,const char *Str2,int N)
+wchar* RemoveLF(wchar *Str)
{
- char S1[NM*2],S2[NM*2];
- strncpyz(S1,Str1,ASIZE(S1));
- strncpyz(S2,Str2,ASIZE(S2));
- return(strncmp(strupper(S1),strupper(S2),N));
+ for (int I=(int)wcslen(Str)-1;I>=0 && (Str[I]=='\r' || Str[I]=='\n');I--)
+ Str[I]=0;
+ return Str;
}
-char* RemoveEOL(char *Str)
+#if defined(SFX_MODULE)
+// char version of etoupperw. Used in console SFX module only.
+// Fast toupper for English only input and output. Additionally to speed,
+// it also avoids Turkish small i to big I with dot conversion problem.
+// We do not define 'c' as 'int' to avoid necessity to cast all
+// signed chars passed to this function to unsigned char.
+unsigned char etoupper(unsigned char c)
{
- for (int I=(int)strlen(Str)-1;I>=0 && (Str[I]=='\r' || Str[I]=='\n' || Str[I]==' ' || Str[I]=='\t');I--)
- Str[I]=0;
- return(Str);
+ return c>='a' && c<='z' ? c-'a'+'A' : c;
}
+#endif
-char* RemoveLF(char *Str)
+// Fast toupper for English only input and output. Additionally to speed,
+// it also avoids Turkish small i to big I with dot conversion problem.
+// We do not define 'c' as 'int' to avoid necessity to cast all
+// signed wchars passed to this function to unsigned char.
+wchar etoupperw(wchar c)
{
- for (int I=(int)strlen(Str)-1;I>=0 && (Str[I]=='\r' || Str[I]=='\n');I--)
- Str[I]=0;
- return(Str);
+ return c>='a' && c<='z' ? c-'a'+'A' : c;
}
-unsigned int loctolower(byte ch)
+// We do not want to cast every signed char to unsigned when passing to
+// isdigit, so we implement the replacement. Shall work for Unicode too.
+// If chars are signed, conversion from char to int could generate negative
+// values, resulting in undefined behavior in standard isdigit.
+bool IsDigit(int ch)
{
-#ifdef _WIN_32
- // convert to LPARAM first to avoid a warning in 64 bit mode
- return((int)(LPARAM)CharLower((LPTSTR)ch));
-#else
- return(tolower(ch));
-#endif
+ return ch>='0' && ch<='9';
}
-unsigned int loctoupper(byte ch)
+// We do not want to cast every signed char to unsigned when passing to
+// isspace, so we implement the replacement. Shall work for Unicode too.
+// If chars are signed, conversion from char to int could generate negative
+// values, resulting in undefined behavior in standard isspace.
+bool IsSpace(int ch)
{
-#ifdef _WIN_32
- // convert to LPARAM first to avoid a warning in 64 bit mode
- return((int)(LPARAM)CharUpper((LPTSTR)ch));
-#else
- return(toupper(ch));
-#endif
+ return ch==' ' || ch=='\t';
}
-// toupper with English only results. Avoiding Turkish i -> I conversion
-// problem
-int etoupper(int ch)
+// We do not want to cast every signed char to unsigned when passing to
+// isalpha, so we implement the replacement. Shall work for Unicode too.
+// If chars are signed, conversion from char to int could generate negative
+// values, resulting in undefined behavior in standard function.
+bool IsAlpha(int ch)
{
- if (ch=='i')
- return('I');
- return(toupper(ch));
+ return ch>='A' && ch<='Z' || ch>='a' && ch<='z';
}
+void BinToHex(const byte *Bin,size_t BinSize,char *HexA,wchar *HexW,size_t HexSize)
+{
+ uint A=0,W=0; // ASCII and Unicode hex output positions.
+ for (uint I=0;I<BinSize;I++)
+ {
+ uint High=Bin[I] >> 4;
+ uint Low=Bin[I] & 0xf;
+ uint HighHex=High>9 ? 'a'+High-10:'0'+High;
+ uint LowHex=Low>9 ? 'a'+Low-10:'0'+Low;
+ if (HexA!=NULL && A<HexSize-2) // Need space for 2 chars and final zero.
+ {
+ HexA[A++]=(char)HighHex;
+ HexA[A++]=(char)LowHex;
+ }
+ if (HexW!=NULL && W<HexSize-2) // Need space for 2 chars and final zero.
+ {
+ HexW[W++]=HighHex;
+ HexW[W++]=LowHex;
+ }
+ }
+ if (HexA!=NULL && HexSize>0)
+ HexA[A]=0;
+ if (HexW!=NULL && HexSize>0)
+ HexW[W]=0;
+}
-bool LowAscii(const char *Str)
+#ifndef SFX_MODULE
+uint GetDigits(uint Number)
{
- for (int I=0;Str[I]!=0;I++)
- if ((byte)Str[I]<32 || (byte)Str[I]>127)
- return(false);
- return(true);
+ uint Digits=1;
+ while (Number>=10)
+ {
+ Number/=10;
+ Digits++;
+ }
+ return Digits;
}
+#endif
-bool LowAscii(const wchar *Str)
+bool LowAscii(const char *Str)
{
- for (int I=0;Str[I]!=0;I++)
- if (Str[I]<32 || Str[I]>127)
- return(false);
- return(true);
+ for (size_t I=0;Str[I]!=0;I++)
+ if (/*(byte)Str[I]<32 || */(byte)Str[I]>127)
+ return false;
+ return true;
}
+bool LowAscii(const wchar *Str)
+{
+ for (size_t I=0;Str[I]!=0;I++)
+ {
+ // We convert wchar_t to uint just in case if some compiler
+ // uses signed wchar_t.
+ if (/*(uint)Str[I]<32 || */(uint)Str[I]>127)
+ return false;
+ }
+ return true;
+}
-int stricompc(const char *Str1,const char *Str2)
+int wcsicompc(const wchar *s1,const wchar *s2) // For path comparison.
{
#if defined(_UNIX)
- return(strcmp(Str1,Str2));
+ return wcscmp(s1,s2);
#else
- return(stricomp(Str1,Str2));
+ return wcsicomp(s1,s2);
#endif
}
-#ifndef SFX_MODULE
-int stricompcw(const wchar *Str1,const wchar *Str2)
+int wcsnicompc(const wchar *s1,const wchar *s2,size_t n)
{
#if defined(_UNIX)
- return(strcmpw(Str1,Str2));
+ return wcsncmp(s1,s2,n);
#else
- return(stricmpw(Str1,Str2));
+ return wcsnicomp(s1,s2,n);
#endif
}
-#endif
-// safe strncpy: copies maxlen-1 max and always returns zero terminated dest
-char* strncpyz(char *dest, const char *src, size_t maxlen)
+// Safe copy: copies maxlen-1 max and for maxlen>0 returns zero terminated dest.
+void strncpyz(char *dest, const char *src, size_t maxlen)
{
if (maxlen>0)
{
- strncpy(dest,src,maxlen-1);
- dest[maxlen-1]=0;
+ while (--maxlen>0 && *src!=0)
+ *dest++=*src++;
+ *dest=0;
}
- return(dest);
}
-// safe strncpyw: copies maxlen-1 max and always returns zero terminated dest
-wchar* strncpyzw(wchar *dest, const wchar *src, size_t maxlen)
+
+// Safe copy: copies maxlen-1 max and for maxlen>0 returns zero terminated dest.
+void wcsncpyz(wchar *dest, const wchar *src, size_t maxlen)
{
if (maxlen>0)
{
- strncpyw(dest,src,maxlen-1);
- dest[maxlen-1]=0;
+ while (--maxlen>0 && *src!=0)
+ *dest++=*src++;
+ *dest=0;
+ }
+}
+
+
+// Safe append: resulting dest length cannot exceed maxlen and dest
+// is always zero terminated. 'maxlen' parameter defines the entire
+// dest buffer size and is not compatible with wcsncat.
+void strncatz(char* dest, const char* src, size_t maxlen)
+{
+ size_t length = strlen(dest);
+ if (maxlen > length)
+ strncpyz(dest + length, src, maxlen - length);
+}
+
+
+// Safe append: resulting dest length cannot exceed maxlen and dest
+// is always zero terminated. 'maxlen' parameter defines the entire
+// dest buffer size and is not compatible with wcsncat.
+void wcsncatz(wchar* dest, const wchar* src, size_t maxlen)
+{
+ size_t length = wcslen(dest);
+ if (maxlen > length)
+ wcsncpyz(dest + length, src, maxlen - length);
+}
+
+
+void itoa(int64 n,char *Str,size_t MaxSize)
+{
+ char NumStr[50];
+ size_t Pos=0;
+
+ int Neg=n < 0 ? 1 : 0;
+ if (Neg)
+ n=-n;
+
+ do
+ {
+ if (Pos+1>=MaxSize-Neg)
+ break;
+ NumStr[Pos++]=char(n%10)+'0';
+ n=n/10;
+ } while (n!=0);
+
+ if (Neg)
+ NumStr[Pos++]='-';
+
+ for (size_t I=0;I<Pos;I++)
+ Str[I]=NumStr[Pos-I-1];
+ Str[Pos]=0;
+}
+
+
+void itoa(int64 n,wchar *Str,size_t MaxSize)
+{
+ wchar NumStr[50];
+ size_t Pos=0;
+
+ int Neg=n < 0 ? 1 : 0;
+ if (Neg)
+ n=-n;
+
+ do
+ {
+ if (Pos+1>=MaxSize-Neg)
+ break;
+ NumStr[Pos++]=wchar(n%10)+'0';
+ n=n/10;
+ } while (n!=0);
+
+ if (Neg)
+ NumStr[Pos++]='-';
+
+ for (size_t I=0;I<Pos;I++)
+ Str[I]=NumStr[Pos-I-1];
+ Str[Pos]=0;
+}
+
+
+// Convert the number to string using thousand separators.
+void fmtitoa(int64 n,wchar *Str,size_t MaxSize)
+{
+ static wchar ThSep=0; // Thousands separator.
+#ifdef _WIN_ALL
+ wchar Info[10];
+ if (!ThSep!=0 && GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_STHOUSAND,Info,ASIZE(Info))>0)
+ ThSep=*Info;
+#elif defined(_UNIX)
+ ThSep=*localeconv()->thousands_sep;
+#endif
+ if (ThSep==0) // If failed to detect the actual separator value.
+ ThSep=' ';
+ wchar RawText[30]; // 20 characters are enough for largest unsigned 64 bit int.
+ itoa(n,RawText,ASIZE(RawText));
+ uint S=0,D=0,L=wcslen(RawText)%3;
+ while (RawText[S]!=0 && D+1<MaxSize)
+ {
+ if (S!=0 && (S+3-L)%3==0)
+ Str[D++]=ThSep;
+ Str[D++]=RawText[S++];
+ }
+ Str[D]=0;
+}
+
+
+const wchar* GetWide(const char *Src)
+{
+ const size_t MaxLength=NM;
+ static wchar StrTable[4][MaxLength];
+ static uint StrNum=0;
+ if (++StrNum >= ASIZE(StrTable))
+ StrNum=0;
+ wchar *Str=StrTable[StrNum];
+ CharToWide(Src,Str,MaxLength);
+ Str[MaxLength-1]=0;
+ return Str;
+}
+
+
+// Parse string containing parameters separated with spaces.
+// Support quote marks. Param can be NULL to return the pointer to next
+// parameter, which can be used to estimate the buffer size for Param.
+const wchar* GetCmdParam(const wchar *CmdLine,wchar *Param,size_t MaxSize)
+{
+ while (IsSpace(*CmdLine))
+ CmdLine++;
+ if (*CmdLine==0)
+ return NULL;
+
+ size_t ParamSize=0;
+ bool Quote=false;
+ while (*CmdLine!=0 && (Quote || !IsSpace(*CmdLine)))
+ {
+ if (*CmdLine=='\"')
+ {
+ if (CmdLine[1]=='\"')
+ {
+ // Insert the quote character instead of two adjoining quote characters.
+ if (Param!=NULL && ParamSize<MaxSize-1)
+ Param[ParamSize++]='\"';
+ CmdLine++;
+ }
+ else
+ Quote=!Quote;
+ }
+ else
+ if (Param!=NULL && ParamSize<MaxSize-1)
+ Param[ParamSize++]=*CmdLine;
+ CmdLine++;
}
- return(dest);
+ if (Param!=NULL)
+ Param[ParamSize]=0;
+ return CmdLine;
}
+
+
+#ifndef RARDLL
+// For compatibility with existing translations we use %s to print Unicode
+// strings in format strings and convert them to %ls here. %s could work
+// without such conversion in Windows, but not in Unix wprintf.
+void PrintfPrepareFmt(const wchar *Org,wchar *Cvt,size_t MaxSize)
+{
+ uint Src=0,Dest=0;
+ while (Org[Src]!=0 && Dest<MaxSize-1)
+ {
+ if (Org[Src]=='%' && (Src==0 || Org[Src-1]!='%'))
+ {
+ uint SPos=Src+1;
+ // Skipping a possible width specifier like %-50s.
+ while (IsDigit(Org[SPos]) || Org[SPos]=='-')
+ SPos++;
+ if (Org[SPos]=='s' && Dest<MaxSize-(SPos-Src+1))
+ {
+ while (Src<SPos)
+ Cvt[Dest++]=Org[Src++];
+ Cvt[Dest++]='l';
+ }
+ }
+#ifdef _WIN_ALL
+ // Convert \n to \r\n in Windows. Important when writing to log,
+ // so other tools like Notebook can view resulting log properly.
+ if (Org[Src]=='\n' && (Src==0 || Org[Src-1]!='\r'))
+ Cvt[Dest++]='\r';
+#endif
+
+ Cvt[Dest++]=Org[Src++];
+ }
+ Cvt[Dest]=0;
+}
+#endif
diff --git a/unrar/unrar/strfn.hpp b/unrar/unrar/strfn.hpp
index a167667..2a21fea 100644
--- a/unrar/unrar/strfn.hpp
+++ b/unrar/unrar/strfn.hpp
@@ -1,34 +1,52 @@
#ifndef _RAR_STRFN_
#define _RAR_STRFN_
-const char *NullToEmpty(const char *Str);
-const wchar *NullToEmpty(const wchar *Str);
-char *IntNameToExt(const char *Name);
-void ExtToInt(const char *Src,char *Dest);
-void IntToExt(const char *Src,char *Dest);
-char* strlower(char *Str);
-char* strupper(char *Str);
-int stricomp(const char *Str1,const char *Str2);
-int strnicomp(const char *Str1,const char *Str2,int N);
-char* RemoveEOL(char *Str);
-char* RemoveLF(char *Str);
-unsigned int loctolower(byte ch);
-unsigned int loctoupper(byte ch);
+const char* NullToEmpty(const char *Str);
+const wchar* NullToEmpty(const wchar *Str);
+void IntToExt(const char *Src,char *Dest,size_t DestSize);
-char* strncpyz(char *dest, const char *src, size_t maxlen);
-wchar* strncpyzw(wchar *dest, const wchar *src, size_t maxlen);
+enum ACTW_ENCODING { ACTW_DEFAULT, ACTW_OEM, ACTW_UTF8};
+void ArcCharToWide(const char *Src,wchar *Dest,size_t DestSize,ACTW_ENCODING Encoding);
-int etoupper(int ch);
+int stricomp(const char *s1,const char *s2);
+int strnicomp(const char *s1,const char *s2,size_t n);
+wchar* RemoveEOL(wchar *Str);
+wchar* RemoveLF(wchar *Str);
+void strncpyz(char *dest, const char *src, size_t maxlen);
+void wcsncpyz(wchar *dest, const wchar *src, size_t maxlen);
+void strncatz(char* dest, const char* src, size_t maxlen);
+void wcsncatz(wchar* dest, const wchar* src, size_t maxlen);
+
+#if defined(SFX_MODULE)
+unsigned char etoupper(unsigned char c);
+#endif
+wchar etoupperw(wchar c);
+
+bool IsDigit(int ch);
+bool IsSpace(int ch);
+bool IsAlpha(int ch);
+
+void BinToHex(const byte *Bin,size_t BinSize,char *Hex,wchar *HexW,size_t HexSize);
+
+#ifndef SFX_MODULE
+uint GetDigits(uint Number);
+#endif
bool LowAscii(const char *Str);
bool LowAscii(const wchar *Str);
+int wcsicompc(const wchar *s1,const wchar *s2);
+int wcsnicompc(const wchar *s1,const wchar *s2,size_t n);
-int stricompc(const char *Str1,const char *Str2);
-#ifndef SFX_MODULE
-int stricompcw(const wchar *Str1,const wchar *Str2);
+void itoa(int64 n,char *Str,size_t MaxSize);
+void itoa(int64 n,wchar *Str,size_t MaxSize);
+void fmtitoa(int64 n,wchar *Str,size_t MaxSize);
+const wchar* GetWide(const char *Src);
+const wchar* GetCmdParam(const wchar *CmdLine,wchar *Param,size_t MaxSize);
+#ifndef RARDLL
+void PrintfPrepareFmt(const wchar *Org,wchar *Cvt,size_t MaxSize);
#endif
#endif
diff --git a/unrar/unrar/strlist.cpp b/unrar/unrar/strlist.cpp
index 7e7e50c..50d69c7 100644
--- a/unrar/unrar/strlist.cpp
+++ b/unrar/unrar/strlist.cpp
@@ -6,159 +6,123 @@ StringList::StringList()
}
-StringList::~StringList()
-{
-}
-
-
void StringList::Reset()
{
Rewind();
StringData.Reset();
- StringDataW.Reset();
- PosDataW.Reset();
StringsCount=0;
SavePosNumber=0;
}
-unsigned int StringList::AddString(const char *Str)
+void StringList::AddStringA(const char *Str)
{
- return(AddString(Str,NULL));
+ Array<wchar> StrW(strlen(Str));
+ CharToWide(Str,&StrW[0],StrW.Size());
+ AddString(&StrW[0]);
}
-unsigned int StringList::AddString(const char *Str,const wchar *StrW)
+void StringList::AddString(const wchar *Str)
{
- int PrevSize=StringData.Size();
- StringData.Add((int)(strlen(Str)+1));
- strcpy(&StringData[PrevSize],Str);
- if (StrW!=NULL && *StrW!=0)
- {
- int PrevPos=PosDataW.Size();
- PosDataW.Add(1);
- PosDataW[PrevPos]=PrevSize;
+ if (Str==NULL)
+ Str=L"";
+
+ size_t PrevSize=StringData.Size();
+ StringData.Add(wcslen(Str)+1);
+ wcscpy(&StringData[PrevSize],Str);
- int PrevSizeW=StringDataW.Size();
- StringDataW.Add(strlenw(StrW)+1);
- strcpyw(&StringDataW[PrevSizeW],StrW);
- }
StringsCount++;
- return(PrevSize);
}
-bool StringList::GetString(char *Str,int MaxLength)
+bool StringList::GetStringA(char *Str,size_t MaxLength)
{
- return(GetString(Str,NULL,MaxLength));
+ Array<wchar> StrW(MaxLength);
+ if (!GetString(&StrW[0],StrW.Size()))
+ return false;
+ WideToChar(&StrW[0],Str,MaxLength);
+ return true;
}
-bool StringList::GetString(char *Str,wchar *StrW,int MaxLength)
+bool StringList::GetString(wchar *Str,size_t MaxLength)
{
- char *StrPtr;
- wchar *StrPtrW;
- if (Str==NULL || !GetString(&StrPtr,&StrPtrW))
- return(false);
- strncpy(Str,StrPtr,MaxLength);
- if (StrW!=NULL)
- strncpyw(StrW,NullToEmpty(StrPtrW),MaxLength);
- return(true);
+ wchar *StrPtr;
+ if (!GetString(&StrPtr))
+ return false;
+ wcsncpyz(Str,StrPtr,MaxLength);
+ return true;
}
#ifndef SFX_MODULE
-bool StringList::GetString(char *Str,wchar *StrW,int MaxLength,int StringNum)
+bool StringList::GetString(wchar *Str,size_t MaxLength,int StringNum)
{
SavePosition();
Rewind();
bool RetCode=true;
while (StringNum-- >=0)
- if (!GetString(Str,StrW,MaxLength))
+ if (!GetString(Str,MaxLength))
{
RetCode=false;
break;
}
RestorePosition();
- return(RetCode);
+ return RetCode;
}
#endif
-char* StringList::GetString()
+wchar* StringList::GetString()
{
- char *Str;
- GetString(&Str,NULL);
- return(Str);
+ wchar *Str;
+ GetString(&Str);
+ return Str;
}
-
-bool StringList::GetString(char **Str,wchar **StrW)
+bool StringList::GetString(wchar **Str)
{
- if (CurPos>=StringData.Size())
+ if (CurPos>=StringData.Size()) // No more strings left unprocessed.
{
- *Str=NULL;
- return(false);
+ if (Str!=NULL)
+ *Str=NULL;
+ return false;
}
- *Str=&StringData[CurPos];
- if (PosDataItem<PosDataW.Size() && PosDataW[PosDataItem]==CurPos)
- {
- PosDataItem++;
- if (StrW!=NULL)
- *StrW=&StringDataW[CurPosW];
- CurPosW+=strlenw(&StringDataW[CurPosW])+1;
- }
- else
- if (StrW!=NULL)
- *StrW=NULL;
- CurPos+=strlen(*Str)+1;
- return(true);
-}
+ wchar *CurStr=&StringData[CurPos];
+ CurPos+=wcslen(CurStr)+1;
+ if (Str!=NULL)
+ *Str=CurStr;
-char* StringList::GetString(unsigned int StringPos)
-{
- if (StringPos>=StringData.Size())
- return(NULL);
- return(&StringData[StringPos]);
+ return true;
}
void StringList::Rewind()
{
CurPos=0;
- CurPosW=0;
- PosDataItem=0;
-}
-
-
-int StringList::GetBufferSize()
-{
- return(StringData.Size()+StringDataW.Size());
}
#ifndef SFX_MODULE
-bool StringList::Search(char *Str,wchar *StrW,bool CaseSensitive)
+bool StringList::Search(const wchar *Str,bool CaseSensitive)
{
SavePosition();
Rewind();
bool Found=false;
- char *CurStr;
- wchar *CurStrW;
- while (GetString(&CurStr,&CurStrW))
+ wchar *CurStr;
+ while (GetString(&CurStr))
{
- if ((CaseSensitive ? strcmp(Str,CurStr):stricomp(Str,CurStr))!=0)
- continue;
- if (StrW!=NULL && CurStrW!=NULL)
- if ((CaseSensitive ? strcmpw(StrW,CurStrW):stricmpw(StrW,CurStrW))!=0)
+ if (Str!=NULL && CurStr!=NULL)
+ if ((CaseSensitive ? wcscmp(Str,CurStr):wcsicomp(Str,CurStr))!=0)
continue;
Found=true;
break;
}
RestorePosition();
- return(Found);
+ return Found;
}
#endif
@@ -166,11 +130,9 @@ bool StringList::Search(char *Str,wchar *StrW,bool CaseSensitive)
#ifndef SFX_MODULE
void StringList::SavePosition()
{
- if (SavePosNumber<sizeof(SaveCurPos)/sizeof(SaveCurPos[0]))
+ if (SavePosNumber<ASIZE(SaveCurPos))
{
SaveCurPos[SavePosNumber]=CurPos;
- SaveCurPosW[SavePosNumber]=CurPosW;
- SavePosDataItem[SavePosNumber]=PosDataItem;
SavePosNumber++;
}
}
@@ -184,8 +146,6 @@ void StringList::RestorePosition()
{
SavePosNumber--;
CurPos=SaveCurPos[SavePosNumber];
- CurPosW=SaveCurPosW[SavePosNumber];
- PosDataItem=SavePosDataItem[SavePosNumber];
}
}
#endif
diff --git a/unrar/unrar/strlist.hpp b/unrar/unrar/strlist.hpp
index c4d3b01..16a2cbb 100644
--- a/unrar/unrar/strlist.hpp
+++ b/unrar/unrar/strlist.hpp
@@ -4,34 +4,26 @@
class StringList
{
private:
- Array<char> StringData;
- unsigned int CurPos;
+ Array<wchar> StringData;
+ size_t CurPos;
- Array<wchar> StringDataW;
- unsigned int CurPosW;
+ size_t StringsCount;
- Array<int> PosDataW;
- uint PosDataItem;
-
- uint StringsCount;
-
- uint SaveCurPos[16],SaveCurPosW[16],SavePosDataItem[16],SavePosNumber;
+ size_t SaveCurPos[16],SavePosNumber;
public:
StringList();
- ~StringList();
void Reset();
- unsigned int AddString(const char *Str);
- unsigned int AddString(const char *Str,const wchar *StrW);
- bool GetString(char *Str,int MaxLength);
- bool GetString(char *Str,wchar *StrW,int MaxLength);
- bool GetString(char *Str,wchar *StrW,int MaxLength,int StringNum);
- char* GetString();
- bool GetString(char **Str,wchar **StrW);
- char* GetString(unsigned int StringPos);
+ void AddStringA(const char *Str);
+ void AddString(const wchar *Str);
+ bool GetStringA(char *Str,size_t MaxLength);
+ bool GetString(wchar *Str,size_t MaxLength);
+ bool GetString(wchar *Str,size_t MaxLength,int StringNum);
+ wchar* GetString();
+ bool GetString(wchar **Str);
void Rewind();
- unsigned int ItemsCount() {return(StringsCount);};
- int GetBufferSize();
- bool Search(char *Str,wchar *StrW,bool CaseSensitive);
+ size_t ItemsCount() {return StringsCount;};
+ size_t GetCharCount() {return StringData.Size();}
+ bool Search(const wchar *Str,bool CaseSensitive);
void SavePosition();
void RestorePosition();
};
diff --git a/unrar/unrar/suballoc.cpp b/unrar/unrar/suballoc.cpp
index 0d42de5..07d3285 100644
--- a/unrar/unrar/suballoc.cpp
+++ b/unrar/unrar/suballoc.cpp
@@ -5,6 +5,9 @@
* Contents: memory allocation routines *
****************************************************************************/
+static const uint UNIT_SIZE=Max(sizeof(RARPPM_CONTEXT),sizeof(RARPPM_MEM_BLK));
+static const uint FIXED_UNIT_SIZE=12;
+
SubAllocator::SubAllocator()
{
Clean();
@@ -34,17 +37,18 @@ inline void* SubAllocator::RemoveNode(int indx)
inline uint SubAllocator::U2B(int NU)
{
- return /*8*NU+4*NU*/UNIT_SIZE*NU;
+ // We calculate the size of units in bytes based on real UNIT_SIZE.
+ // In original implementation it was 8*NU+4*NU.
+ return UNIT_SIZE*NU;
}
-/*
- calculate RAR_MEM_BLK + Items address. Real RAR_MEM_BLK size must be
- equal to UNIT_SIZE, so we cannot just add Items to RAR_MEM_BLK address
-*/
-inline RAR_MEM_BLK* SubAllocator::MBPtr(RAR_MEM_BLK *BasePtr,int Items)
+
+// Calculate RARPPM_MEM_BLK+Items address. Real RARPPM_MEM_BLK size must be
+// equal to UNIT_SIZE, so we cannot just add Items to RARPPM_MEM_BLK address.
+inline RARPPM_MEM_BLK* SubAllocator::MBPtr(RARPPM_MEM_BLK *BasePtr,int Items)
{
- return((RAR_MEM_BLK*)( ((byte *)(BasePtr))+U2B(Items) ));
+ return((RARPPM_MEM_BLK*)( ((byte *)(BasePtr))+U2B(Items) ));
}
@@ -62,14 +66,12 @@ inline void SubAllocator::SplitBlock(void* pv,int OldIndx,int NewIndx)
}
-
-
void SubAllocator::StopSubAllocator()
{
if ( SubAllocatorSize )
{
SubAllocatorSize=0;
- rarfree(HeapStart);
+ free(HeapStart);
}
}
@@ -78,20 +80,26 @@ bool SubAllocator::StartSubAllocator(int SASize)
{
uint t=SASize << 20;
if (SubAllocatorSize == t)
- return TRUE;
+ return true;
StopSubAllocator();
- uint AllocSize=t/FIXED_UNIT_SIZE*UNIT_SIZE+UNIT_SIZE;
-#ifdef STRICT_ALIGNMENT_REQUIRED
- AllocSize+=UNIT_SIZE;
-#endif
- if ((HeapStart=(byte *)rarmalloc(AllocSize)) == NULL)
+
+ // Original algorithm expects FIXED_UNIT_SIZE, but actual structure size
+ // can be larger. So let's recalculate the allocated size and add two more
+ // units: one as reserve for HeapEnd overflow checks and another
+ // to provide the space to correctly align UnitsStart.
+ uint AllocSize=t/FIXED_UNIT_SIZE*UNIT_SIZE+2*UNIT_SIZE;
+ if ((HeapStart=(byte *)malloc(AllocSize)) == NULL)
{
ErrHandler.MemoryError();
- return FALSE;
+ return false;
}
+
+ // HeapEnd did not present in original algorithm. We added it to control
+ // invalid memory access attempts when processing corrupt archived data.
HeapEnd=HeapStart+AllocSize-UNIT_SIZE;
+
SubAllocatorSize=t;
- return TRUE;
+ return true;
}
@@ -100,17 +108,46 @@ void SubAllocator::InitSubAllocator()
int i, k;
memset(FreeList,0,sizeof(FreeList));
pText=HeapStart;
+
+ // Original algorithm operates with 12 byte FIXED_UNIT_SIZE, but actual
+ // size of RARPPM_MEM_BLK and RARPPM_CONTEXT structures can exceed this value
+ // because of alignment and larger pointer fields size.
+ // So we define UNIT_SIZE for this larger size and adjust memory
+ // pointers accordingly.
+
+ // Size2 is (HiUnit-LoUnit) memory area size to allocate as originally
+ // supposed by compression algorithm. It is 7/8 of total allocated size.
uint Size2=FIXED_UNIT_SIZE*(SubAllocatorSize/8/FIXED_UNIT_SIZE*7);
+
+ // RealSize2 is the real adjusted size of (HiUnit-LoUnit) memory taking
+ // into account that our UNIT_SIZE can be larger than FIXED_UNIT_SIZE.
uint RealSize2=Size2/FIXED_UNIT_SIZE*UNIT_SIZE;
+
+ // Size1 is the size of memory area from HeapStart to FakeUnitsStart
+ // as originally supposed by compression algorithm. This area can contain
+ // different data types, both single symbols and structures.
uint Size1=SubAllocatorSize-Size2;
- uint RealSize1=Size1/FIXED_UNIT_SIZE*UNIT_SIZE+Size1%FIXED_UNIT_SIZE;
-#ifdef STRICT_ALIGNMENT_REQUIRED
- if (Size1%FIXED_UNIT_SIZE!=0)
- RealSize1+=UNIT_SIZE-Size1%FIXED_UNIT_SIZE;
-#endif
- HiUnit=HeapStart+SubAllocatorSize;
+
+ // Real size of this area. We correct it according to UNIT_SIZE vs
+ // FIXED_UNIT_SIZE difference. Also we add one more UNIT_SIZE
+ // to compensate a possible reminder from Size1/FIXED_UNIT_SIZE,
+ // which would be lost otherwise. We add UNIT_SIZE instead of
+ // this Size1%FIXED_UNIT_SIZE reminder, because it allows to align
+ // UnitsStart easily and adding more than reminder is ok for algorithm.
+ uint RealSize1=Size1/FIXED_UNIT_SIZE*UNIT_SIZE+UNIT_SIZE;
+
+ // RealSize1 must be divided by UNIT_SIZE without a reminder, so UnitsStart
+ // is aligned to UNIT_SIZE. It is important for those architectures,
+ // where a proper memory alignment is mandatory. Since we produce RealSize1
+ // multiplying by UNIT_SIZE, this condition is always true. So LoUnit,
+ // UnitsStart, HeapStart are properly aligned,
LoUnit=UnitsStart=HeapStart+RealSize1;
+
+ // When we reach FakeUnitsStart, we restart the model. It is where
+ // the original algorithm expected to see UnitsStart. Real UnitsStart
+ // can have a larger value.
FakeUnitsStart=HeapStart+Size1;
+
HiUnit=LoUnit+RealSize2;
for (i=0,k=1;i < N1 ;i++,k += 1)
Indx2Units[i]=k;
@@ -130,14 +167,14 @@ void SubAllocator::InitSubAllocator()
inline void SubAllocator::GlueFreeBlocks()
{
- RAR_MEM_BLK s0, * p, * p1;
+ RARPPM_MEM_BLK s0, * p, * p1;
int i, k, sz;
if (LoUnit != HiUnit)
*LoUnit=0;
for (i=0, s0.next=s0.prev=&s0;i < N_INDEXES;i++)
while ( FreeList[i].next )
{
- p=(RAR_MEM_BLK*)RemoveNode(i);
+ p=(RARPPM_MEM_BLK*)RemoveNode(i);
p->insertAt(&s0);
p->Stamp=0xFFFF;
p->NU=Indx2Units[i];
@@ -178,13 +215,13 @@ void* SubAllocator::AllocUnitsRare(int indx)
GlueCount--;
i=U2B(Indx2Units[indx]);
int j=FIXED_UNIT_SIZE*Indx2Units[indx];
- if (FakeUnitsStart-pText > j)
+ if (FakeUnitsStart - pText > j)
{
- FakeUnitsStart-=j;
+ FakeUnitsStart -= j;
UnitsStart -= i;
- return(UnitsStart);
+ return UnitsStart;
}
- return(NULL);
+ return NULL;
}
} while ( !FreeList[i].next );
void* RetVal=RemoveNode(i);
diff --git a/unrar/unrar/suballoc.hpp b/unrar/unrar/suballoc.hpp
index 474ee59..2a1d132 100644
--- a/unrar/unrar/suballoc.hpp
+++ b/unrar/unrar/suballoc.hpp
@@ -7,24 +7,21 @@
#if !defined(_SUBALLOC_H_)
#define _SUBALLOC_H_
-const int N1=4, N2=4, N3=4, N4=(128+3-1*N1-2*N2-3*N3)/4;
-const int N_INDEXES=N1+N2+N3+N4;
-
-#if defined(__GNUC__) && !defined(STRICT_ALIGNMENT_REQUIRED)
-#define _PACK_ATTR __attribute__ ((packed))
+#if defined(__GNUC__) && defined(ALLOW_MISALIGNED)
+#define RARPPM_PACK_ATTR __attribute__ ((packed))
#else
-#define _PACK_ATTR
+#define RARPPM_PACK_ATTR
#endif /* defined(__GNUC__) */
-#ifndef STRICT_ALIGNMENT_REQUIRED
+#ifdef ALLOW_MISALIGNED
#pragma pack(1)
#endif
-struct RAR_MEM_BLK
+struct RARPPM_MEM_BLK
{
ushort Stamp, NU;
- RAR_MEM_BLK* next, * prev;
- void insertAt(RAR_MEM_BLK* p)
+ RARPPM_MEM_BLK* next, * prev;
+ void insertAt(RARPPM_MEM_BLK* p)
{
next=(prev=p)->next;
p->next=next->prev=this;
@@ -34,9 +31,9 @@ struct RAR_MEM_BLK
prev->next=next;
next->prev=prev;
}
-} _PACK_ATTR;
+} RARPPM_PACK_ATTR;
-#ifndef STRICT_ALIGNMENT_REQUIRED
+#ifdef ALLOW_MISALIGNED
#ifdef _AIX
#pragma pack(pop)
#else
@@ -45,22 +42,24 @@ struct RAR_MEM_BLK
#endif
-struct RAR_NODE
-{
- RAR_NODE* next;
-};
-
class SubAllocator
{
private:
+ static const int N1=4, N2=4, N3=4, N4=(128+3-1*N1-2*N2-3*N3)/4;
+ static const int N_INDEXES=N1+N2+N3+N4;
+
+ struct RAR_NODE
+ {
+ RAR_NODE* next;
+ };
+
inline void InsertNode(void* p,int indx);
inline void* RemoveNode(int indx);
inline uint U2B(int NU);
inline void SplitBlock(void* pv,int OldIndx,int NewIndx);
- uint GetUsedMemory();
inline void GlueFreeBlocks();
void* AllocUnitsRare(int indx);
- inline RAR_MEM_BLK* MBPtr(RAR_MEM_BLK *BasePtr,int Items);
+ inline RARPPM_MEM_BLK* MBPtr(RARPPM_MEM_BLK *BasePtr,int Items);
long SubAllocatorSize;
byte Indx2Units[N_INDEXES], Units2Indx[128], GlueCount;
@@ -78,7 +77,7 @@ class SubAllocator
inline void* ExpandUnits(void* ptr,int OldNU);
inline void* ShrinkUnits(void* ptr,int OldNU,int NewNU);
inline void FreeUnits(void* ptr,int OldNU);
- long GetAllocatedMemory() {return(SubAllocatorSize);};
+ long GetAllocatedMemory() {return(SubAllocatorSize);}
byte *pText, *UnitsStart,*HeapEnd,*FakeUnitsStart;
};
diff --git a/unrar/unrar/system.cpp b/unrar/unrar/system.cpp
index d5de22e..9e53622 100644
--- a/unrar/unrar/system.cpp
+++ b/unrar/unrar/system.cpp
@@ -1,20 +1,17 @@
#include "rar.hpp"
-#ifndef _WIN_CE
static int SleepTime=0;
void InitSystemOptions(int SleepTime)
{
::SleepTime=SleepTime;
}
-#endif
-
-#if !defined(SFX_MODULE) && !defined(_WIN_CE)
+#if !defined(SFX_MODULE)
void SetPriority(int Priority)
{
-#ifdef _WIN_32
+#ifdef _WIN_ALL
uint PriorityClass;
int PriorityLevel;
if (Priority<1 || Priority>15)
@@ -24,6 +21,10 @@ void SetPriority(int Priority)
{
PriorityClass=IDLE_PRIORITY_CLASS;
PriorityLevel=THREAD_PRIORITY_IDLE;
+
+// Background mode for Vista, can be slow for many small files.
+// if (WinNT()>=WNT_VISTA)
+// SetPriorityClass(GetCurrentProcess(),PROCESS_MODE_BACKGROUND_BEGIN);
}
else
if (Priority<7)
@@ -57,29 +58,51 @@ void SetPriority(int Priority)
SetPriorityClass(GetCurrentProcess(),PriorityClass);
SetThreadPriority(GetCurrentThread(),PriorityLevel);
-// background mode for Vista, too slow for real life use
-// if (WinNT()>=6 && Priority==1)
-// SetPriorityClass(GetCurrentProcess(),PROCESS_MODE_BACKGROUND_BEGIN);
+#ifdef RAR_SMP
+ ThreadPool::SetPriority(PriorityLevel);
+#endif
#endif
}
#endif
+// Monotonic clock. Like clock(), returns time passed in CLOCKS_PER_SEC items.
+// In Android 5+ and Unix usual clock() returns time spent by all threads
+// together, so we cannot use it to measure time intervals anymore.
+clock_t MonoClock()
+{
+ return clock();
+}
+
+
+
void Wait()
{
-#if defined(_WIN_32) && !defined(_WIN_CE) && !defined(SFX_MODULE)
+ if (ErrHandler.UserBreak)
+ ErrHandler.Exit(RARX_USERBREAK);
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
if (SleepTime!=0)
- Sleep(SleepTime);
+ {
+ static clock_t LastTime=MonoClock();
+ if (MonoClock()-LastTime>10*CLOCKS_PER_SEC/1000)
+ {
+ Sleep(SleepTime);
+ LastTime=MonoClock();
+ }
+ }
+#endif
+#if defined(_WIN_ALL)
+ // Reset system sleep timer to prevent system going sleep.
+ SetThreadExecutionState(ES_SYSTEM_REQUIRED);
#endif
}
-#if defined(_WIN_32) && !defined(_WIN_CE) && !defined(SFX_MODULE) && !defined(SHELL_EXT)
-
-void Shutdown()
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
+void Shutdown(POWER_MODE Mode)
{
HANDLE hToken;
TOKEN_PRIVILEGES tkp;
@@ -91,6 +114,102 @@ void Shutdown()
AdjustTokenPrivileges(hToken,FALSE,&tkp,0,(PTOKEN_PRIVILEGES)NULL,0);
}
- ExitWindowsEx(EWX_SHUTDOWN|EWX_FORCE|EWX_POWEROFF,SHTDN_REASON_FLAG_PLANNED);
+ if (Mode==POWERMODE_OFF)
+ ExitWindowsEx(EWX_SHUTDOWN|EWX_FORCE,SHTDN_REASON_FLAG_PLANNED);
+ if (Mode==POWERMODE_SLEEP)
+ SetSuspendState(FALSE,FALSE,FALSE);
+ if (Mode==POWERMODE_HIBERNATE)
+ SetSuspendState(TRUE,FALSE,FALSE);
+ if (Mode==POWERMODE_RESTART)
+ ExitWindowsEx(EWX_REBOOT|EWX_FORCE,SHTDN_REASON_FLAG_PLANNED);
+}
+
+
+bool ShutdownCheckAnother(bool Open)
+{
+ const wchar *EventName=L"rar -ioff";
+ static HANDLE hEvent=NULL;
+ bool Result=false; // Return false if no other RAR -ioff are running.
+ if (Open) // Create or open the event.
+ hEvent=CreateEvent(NULL,FALSE,FALSE,EventName);
+ else
+ {
+ if (hEvent!=NULL)
+ CloseHandle(hEvent); // Close our event.
+ // Check if other copies still own the event. While race conditions
+ // are possible, they are improbable and their harm is minimal.
+ hEvent=CreateEvent(NULL,FALSE,FALSE,EventName);
+ Result=GetLastError()==ERROR_ALREADY_EXISTS;
+ if (hEvent!=NULL)
+ CloseHandle(hEvent);
+ }
+ return Result;
+}
+#endif
+
+
+
+
+#if defined(_WIN_ALL)
+// Load library from Windows System32 folder. Use this function to prevent
+// loading a malicious code from current folder or same folder as exe.
+HMODULE WINAPI LoadSysLibrary(const wchar *Name)
+{
+ wchar SysDir[NM];
+ if (GetSystemDirectory(SysDir,ASIZE(SysDir))==0)
+ return NULL;
+ MakeName(SysDir,Name,SysDir,ASIZE(SysDir));
+ return LoadLibrary(SysDir);
+}
+
+
+bool IsUserAdmin()
+{
+ SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
+ PSID AdministratorsGroup;
+ BOOL b = AllocateAndInitializeSid(&NtAuthority,2,SECURITY_BUILTIN_DOMAIN_RID,
+ DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdministratorsGroup);
+ if (b)
+ {
+ if (!CheckTokenMembership( NULL, AdministratorsGroup, &b))
+ b = FALSE;
+ FreeSid(AdministratorsGroup);
+ }
+ return b!=FALSE;
+}
+
+#endif
+
+
+#ifdef USE_SSE
+SSE_VERSION _SSE_Version=GetSSEVersion();
+
+SSE_VERSION GetSSEVersion()
+{
+ int CPUInfo[4];
+ __cpuid(CPUInfo, 0);
+
+ // Maximum supported cpuid function.
+ uint MaxSupported=CPUInfo[0];
+
+ if (MaxSupported>=7)
+ {
+ __cpuid(CPUInfo, 7);
+ if ((CPUInfo[1] & 0x20)!=0)
+ return SSE_AVX2;
+ }
+ if (MaxSupported>=1)
+ {
+ __cpuid(CPUInfo, 1);
+ if ((CPUInfo[2] & 0x80000)!=0)
+ return SSE_SSE41;
+ if ((CPUInfo[2] & 0x200)!=0)
+ return SSE_SSSE3;
+ if ((CPUInfo[3] & 0x4000000)!=0)
+ return SSE_SSE2;
+ if ((CPUInfo[3] & 0x2000000)!=0)
+ return SSE_SSE;
+ }
+ return SSE_NONE;
}
#endif
diff --git a/unrar/unrar/system.hpp b/unrar/unrar/system.hpp
index d98e5a2..a56d6b7 100644
--- a/unrar/unrar/system.hpp
+++ b/unrar/unrar/system.hpp
@@ -1,7 +1,7 @@
#ifndef _RAR_SYSTEM_
#define _RAR_SYSTEM_
-#ifdef _WIN_32
+#ifdef _WIN_ALL
#ifndef BELOW_NORMAL_PRIORITY_CLASS
#define BELOW_NORMAL_PRIORITY_CLASS 0x00004000
#define ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
@@ -19,8 +19,22 @@
void InitSystemOptions(int SleepTime);
void SetPriority(int Priority);
+clock_t MonoClock();
void Wait();
-bool EmailFile(char *FileName,char *MailTo);
-void Shutdown();
+bool EmailFile(const wchar *FileName,const wchar *MailToW);
+void Shutdown(POWER_MODE Mode);
+bool ShutdownCheckAnother(bool Open);
+
+#ifdef _WIN_ALL
+HMODULE WINAPI LoadSysLibrary(const wchar *Name);
+bool IsUserAdmin();
+#endif
+
+
+#ifdef USE_SSE
+enum SSE_VERSION {SSE_NONE,SSE_SSE,SSE_SSE2,SSE_SSSE3,SSE_SSE41,SSE_AVX2};
+SSE_VERSION GetSSEVersion();
+extern SSE_VERSION _SSE_Version;
+#endif
#endif
diff --git a/unrar/unrar/threadmisc.cpp b/unrar/unrar/threadmisc.cpp
new file mode 100644
index 0000000..7a6ec78
--- /dev/null
+++ b/unrar/unrar/threadmisc.cpp
@@ -0,0 +1,153 @@
+static inline bool CriticalSectionCreate(CRITSECT_HANDLE *CritSection)
+{
+#ifdef _WIN_ALL
+ InitializeCriticalSection(CritSection);
+ return true;
+#elif defined(_UNIX)
+ return pthread_mutex_init(CritSection,NULL)==0;
+#endif
+}
+
+
+static inline void CriticalSectionDelete(CRITSECT_HANDLE *CritSection)
+{
+#ifdef _WIN_ALL
+ DeleteCriticalSection(CritSection);
+#elif defined(_UNIX)
+ pthread_mutex_destroy(CritSection);
+#endif
+}
+
+
+static inline void CriticalSectionStart(CRITSECT_HANDLE *CritSection)
+{
+#ifdef _WIN_ALL
+ EnterCriticalSection(CritSection);
+#elif defined(_UNIX)
+ pthread_mutex_lock(CritSection);
+#endif
+}
+
+
+static inline void CriticalSectionEnd(CRITSECT_HANDLE *CritSection)
+{
+#ifdef _WIN_ALL
+ LeaveCriticalSection(CritSection);
+#elif defined(_UNIX)
+ pthread_mutex_unlock(CritSection);
+#endif
+}
+
+
+static THREAD_HANDLE ThreadCreate(NATIVE_THREAD_PTR Proc,void *Data)
+{
+#ifdef _UNIX
+/*
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+*/
+ pthread_t pt;
+ int Code=pthread_create(&pt,NULL/*&attr*/,Proc,Data);
+ if (Code!=0)
+ {
+ wchar Msg[100];
+ swprintf(Msg,ASIZE(Msg),L"\npthread_create failed, code %d\n",Code);
+ ErrHandler.GeneralErrMsg(Msg);
+ ErrHandler.SysErrMsg();
+ ErrHandler.Exit(RARX_FATAL);
+ }
+ return pt;
+#else
+ DWORD ThreadId;
+ HANDLE hThread=CreateThread(NULL,0x10000,Proc,Data,0,&ThreadId);
+ if (hThread==NULL)
+ {
+ ErrHandler.GeneralErrMsg(L"CreateThread failed");
+ ErrHandler.SysErrMsg();
+ ErrHandler.Exit(RARX_FATAL);
+ }
+ return hThread;
+#endif
+}
+
+
+static void ThreadClose(THREAD_HANDLE hThread)
+{
+#ifdef _UNIX
+ pthread_join(hThread,NULL);
+#else
+ CloseHandle(hThread);
+#endif
+}
+
+
+#ifdef _WIN_ALL
+static void CWaitForSingleObject(HANDLE hHandle)
+{
+ DWORD rc=WaitForSingleObject(hHandle,INFINITE);
+ if (rc==WAIT_FAILED)
+ {
+ ErrHandler.GeneralErrMsg(L"\nWaitForMultipleObjects error %d, GetLastError %d",rc,GetLastError());
+ ErrHandler.Exit(RARX_FATAL);
+ }
+}
+#endif
+
+
+#ifdef _UNIX
+static void cpthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
+{
+ int rc=pthread_cond_wait(cond,mutex);
+ if (rc!=0)
+ {
+ ErrHandler.GeneralErrMsg(L"\npthread_cond_wait error %d",rc);
+ ErrHandler.Exit(RARX_FATAL);
+ }
+}
+#endif
+
+
+uint GetNumberOfCPU()
+{
+#ifndef RAR_SMP
+ return 1;
+#else
+#ifdef _UNIX
+#ifdef _SC_NPROCESSORS_ONLN
+ uint Count=(uint)sysconf(_SC_NPROCESSORS_ONLN);
+ return Count<1 ? 1:Count;
+#elif defined(_APPLE)
+ uint Count;
+ size_t Size=sizeof(Count);
+ return sysctlbyname("hw.ncpu",&Count,&Size,NULL,0)==0 ? Count:1;
+#endif
+#else // !_UNIX
+ DWORD_PTR ProcessMask;
+ DWORD_PTR SystemMask;
+
+ if (!GetProcessAffinityMask(GetCurrentProcess(),&ProcessMask,&SystemMask))
+ return 1;
+ uint Count=0;
+ for (DWORD_PTR Mask=1;Mask!=0;Mask<<=1)
+ if ((ProcessMask & Mask)!=0)
+ Count++;
+ return Count<1 ? 1:Count;
+#endif
+
+#endif // RAR_SMP
+}
+
+
+uint GetNumberOfThreads()
+{
+ uint NumCPU=GetNumberOfCPU();
+ if (NumCPU<1)
+ return 1;
+ if (NumCPU>MaxPoolThreads)
+ return MaxPoolThreads;
+ return NumCPU;
+}
+
+
+
diff --git a/unrar/unrar/threadpool.cpp b/unrar/unrar/threadpool.cpp
new file mode 100644
index 0000000..8c63a8b
--- /dev/null
+++ b/unrar/unrar/threadpool.cpp
@@ -0,0 +1,212 @@
+#include "rar.hpp"
+
+#ifdef RAR_SMP
+#include "threadmisc.cpp"
+
+#ifdef _WIN_ALL
+int ThreadPool::ThreadPriority=THREAD_PRIORITY_NORMAL;
+#endif
+
+ThreadPool::ThreadPool(uint MaxThreads)
+{
+ MaxAllowedThreads = MaxThreads;
+ if (MaxAllowedThreads>MaxPoolThreads)
+ MaxAllowedThreads=MaxPoolThreads;
+ if (MaxAllowedThreads==0)
+ MaxAllowedThreads=1;
+
+ ThreadsCreatedCount=0;
+
+ // If we have more threads than queue size, we'll hang on pool destroying,
+ // not releasing all waiting threads.
+ if (MaxAllowedThreads>ASIZE(TaskQueue))
+ MaxAllowedThreads=ASIZE(TaskQueue);
+
+ Closing=false;
+
+ bool Success = CriticalSectionCreate(&CritSection);
+#ifdef _WIN_ALL
+ QueuedTasksCnt=CreateSemaphore(NULL,0,ASIZE(TaskQueue),NULL);
+ NoneActive=CreateEvent(NULL,TRUE,TRUE,NULL);
+ Success=Success && QueuedTasksCnt!=NULL && NoneActive!=NULL;
+#elif defined(_UNIX)
+ AnyActive = false;
+ QueuedTasksCnt = 0;
+ Success=Success && pthread_cond_init(&AnyActiveCond,NULL)==0 &&
+ pthread_mutex_init(&AnyActiveMutex,NULL)==0 &&
+ pthread_cond_init(&QueuedTasksCntCond,NULL)==0 &&
+ pthread_mutex_init(&QueuedTasksCntMutex,NULL)==0;
+#endif
+ if (!Success)
+ {
+ ErrHandler.GeneralErrMsg(L"\nThread pool initialization failed.");
+ ErrHandler.Exit(RARX_FATAL);
+ }
+
+ QueueTop = 0;
+ QueueBottom = 0;
+ ActiveThreads = 0;
+}
+
+
+ThreadPool::~ThreadPool()
+{
+ WaitDone();
+ Closing=true;
+
+#ifdef _WIN_ALL
+ ReleaseSemaphore(QueuedTasksCnt,ASIZE(TaskQueue),NULL);
+#elif defined(_UNIX)
+ // Threads still can access QueuedTasksCnt for a short time after WaitDone(),
+ // so lock is required. We would occassionally hang without it.
+ pthread_mutex_lock(&QueuedTasksCntMutex);
+ QueuedTasksCnt+=ASIZE(TaskQueue);
+ pthread_mutex_unlock(&QueuedTasksCntMutex);
+
+ pthread_cond_broadcast(&QueuedTasksCntCond);
+#endif
+
+ for(uint I=0;I<ThreadsCreatedCount;I++)
+ {
+#ifdef _WIN_ALL
+ // Waiting until the thread terminates.
+ CWaitForSingleObject(ThreadHandles[I]);
+#endif
+ // Close the thread handle. In Unix it results in pthread_join call,
+ // which also waits for thread termination.
+ ThreadClose(ThreadHandles[I]);
+ }
+
+ CriticalSectionDelete(&CritSection);
+#ifdef _WIN_ALL
+ CloseHandle(QueuedTasksCnt);
+ CloseHandle(NoneActive);
+#elif defined(_UNIX)
+ pthread_cond_destroy(&AnyActiveCond);
+ pthread_mutex_destroy(&AnyActiveMutex);
+ pthread_cond_destroy(&QueuedTasksCntCond);
+ pthread_mutex_destroy(&QueuedTasksCntMutex);
+#endif
+}
+
+
+void ThreadPool::CreateThreads()
+{
+ for(uint I=0;I<MaxAllowedThreads;I++)
+ {
+ ThreadHandles[I] = ThreadCreate(PoolThread, this);
+ ThreadsCreatedCount++;
+#ifdef _WIN_ALL
+ if (ThreadPool::ThreadPriority!=THREAD_PRIORITY_NORMAL)
+ SetThreadPriority(ThreadHandles[I],ThreadPool::ThreadPriority);
+#endif
+ }
+}
+
+
+NATIVE_THREAD_TYPE ThreadPool::PoolThread(void *Param)
+{
+ ((ThreadPool*)Param)->PoolThreadLoop();
+ return 0;
+}
+
+
+void ThreadPool::PoolThreadLoop()
+{
+ QueueEntry Task;
+ while (GetQueuedTask(&Task))
+ {
+ Task.Proc(Task.Param);
+
+ CriticalSectionStart(&CritSection);
+ if (--ActiveThreads == 0)
+ {
+#ifdef _WIN_ALL
+ SetEvent(NoneActive);
+#elif defined(_UNIX)
+ pthread_mutex_lock(&AnyActiveMutex);
+ AnyActive=false;
+ pthread_cond_signal(&AnyActiveCond);
+ pthread_mutex_unlock(&AnyActiveMutex);
+#endif
+ }
+ CriticalSectionEnd(&CritSection);
+ }
+}
+
+
+bool ThreadPool::GetQueuedTask(QueueEntry *Task)
+{
+#ifdef _WIN_ALL
+ CWaitForSingleObject(QueuedTasksCnt);
+#elif defined(_UNIX)
+ pthread_mutex_lock(&QueuedTasksCntMutex);
+ while (QueuedTasksCnt==0)
+ cpthread_cond_wait(&QueuedTasksCntCond,&QueuedTasksCntMutex);
+ QueuedTasksCnt--;
+ pthread_mutex_unlock(&QueuedTasksCntMutex);
+#endif
+
+ if (Closing)
+ return false;
+
+ CriticalSectionStart(&CritSection);
+
+ *Task = TaskQueue[QueueBottom];
+ QueueBottom = (QueueBottom + 1) % ASIZE(TaskQueue);
+
+ CriticalSectionEnd(&CritSection);
+
+ return true;
+}
+
+
+// Add task to queue. We assume that it is always called from main thread,
+// it allows to avoid any locks here. We process collected tasks only
+// when WaitDone is called.
+void ThreadPool::AddTask(PTHREAD_PROC Proc,void *Data)
+{
+ if (ThreadsCreatedCount == 0)
+ CreateThreads();
+
+ // If queue is full, wait until it is empty.
+ if (ActiveThreads>=ASIZE(TaskQueue))
+ WaitDone();
+
+ TaskQueue[QueueTop].Proc = Proc;
+ TaskQueue[QueueTop].Param = Data;
+ QueueTop = (QueueTop + 1) % ASIZE(TaskQueue);
+ ActiveThreads++;
+}
+
+
+// Start queued tasks and wait until all threads are inactive.
+// We assume that it is always called from main thread, when pool threads
+// are sleeping yet.
+void ThreadPool::WaitDone()
+{
+ if (ActiveThreads==0)
+ return;
+#ifdef _WIN_ALL
+ ResetEvent(NoneActive);
+ ReleaseSemaphore(QueuedTasksCnt,ActiveThreads,NULL);
+ CWaitForSingleObject(NoneActive);
+#elif defined(_UNIX)
+ AnyActive=true;
+
+ // Threads reset AnyActive before accessing QueuedTasksCnt and even
+ // preceding WaitDone() call does not guarantee that some slow thread
+ // is not accessing QueuedTasksCnt now. So lock is necessary.
+ pthread_mutex_lock(&QueuedTasksCntMutex);
+ QueuedTasksCnt+=ActiveThreads;
+ pthread_mutex_unlock(&QueuedTasksCntMutex);
+
+ pthread_cond_broadcast(&QueuedTasksCntCond);
+
+ pthread_mutex_lock(&AnyActiveMutex);
+ while (AnyActive)
+ cpthread_cond_wait(&AnyActiveCond,&AnyActiveMutex);
+ pthread_mutex_unlock(&AnyActiveMutex);
+#endif
+}
+#endif // RAR_SMP
diff --git a/unrar/unrar/threadpool.hpp b/unrar/unrar/threadpool.hpp
new file mode 100644
index 0000000..85ed90d
--- /dev/null
+++ b/unrar/unrar/threadpool.hpp
@@ -0,0 +1,107 @@
+#ifndef _RAR_THREADPOOL_
+#define _RAR_THREADPOOL_
+
+#ifndef RAR_SMP
+const uint MaxPoolThreads=1; // For single threaded version.
+#else
+// We need to use the processor groups API to increase it beyond 64.
+// Also be sure to check and adjust if needed per thread and total block size
+// when compressing if going above 64.
+const uint MaxPoolThreads=64;
+
+
+#ifdef _UNIX
+ #include <pthread.h>
+ #include <semaphore.h>
+#endif
+
+// Undefine for debugging.
+#define USE_THREADS
+
+#ifdef _UNIX
+ #define NATIVE_THREAD_TYPE void*
+ typedef void* (*NATIVE_THREAD_PTR)(void *Data);
+ typedef pthread_t THREAD_HANDLE;
+ typedef pthread_mutex_t CRITSECT_HANDLE;
+#else
+ #define NATIVE_THREAD_TYPE DWORD WINAPI
+ typedef DWORD (WINAPI *NATIVE_THREAD_PTR)(void *Data);
+ typedef HANDLE THREAD_HANDLE;
+ typedef CRITICAL_SECTION CRITSECT_HANDLE;
+#endif
+
+typedef void (*PTHREAD_PROC)(void *Data);
+#define THREAD_PROC(fn) void fn(void *Data)
+
+uint GetNumberOfCPU();
+uint GetNumberOfThreads();
+
+
+class ThreadPool
+{
+ private:
+ struct QueueEntry
+ {
+ PTHREAD_PROC Proc;
+ void *Param;
+ };
+
+ void CreateThreads();
+ static NATIVE_THREAD_TYPE PoolThread(void *Param);
+ void PoolThreadLoop();
+ bool GetQueuedTask(QueueEntry *Task);
+
+ // Number of threads in the pool. Must not exceed MaxPoolThreads.
+ uint MaxAllowedThreads;
+ THREAD_HANDLE ThreadHandles[MaxPoolThreads];
+
+ // Number of actually created threads.
+ uint ThreadsCreatedCount;
+
+ uint ActiveThreads;
+
+ QueueEntry TaskQueue[MaxPoolThreads];
+ uint QueueTop;
+ uint QueueBottom;
+
+ bool Closing; // Set true to quit all threads.
+
+#ifdef _WIN_ALL
+ // Semaphore counting number of tasks stored in queue.
+ HANDLE QueuedTasksCnt;
+
+ // Event signalling if no active tasks are performing now.
+ HANDLE NoneActive;
+
+#elif defined(_UNIX)
+ // Semaphores seem to be slower than conditional variables in pthreads,
+ // so we use the conditional variable to count tasks stored in queue.
+ uint QueuedTasksCnt;
+ pthread_cond_t QueuedTasksCntCond;
+ pthread_mutex_t QueuedTasksCntMutex;
+
+ bool AnyActive; // Active tasks present flag.
+ pthread_cond_t AnyActiveCond;
+ pthread_mutex_t AnyActiveMutex;
+#endif
+
+ // Pool critical section. We use the single section for all branches
+ // to avoid deadlocks, when thread1 has section1 and wants section2
+ // and thread2 has section2 and wants section1.
+ CRITSECT_HANDLE CritSection;
+ public:
+ ThreadPool(uint MaxThreads);
+ ~ThreadPool();
+ void AddTask(PTHREAD_PROC Proc,void *Data);
+ void WaitDone();
+
+#ifdef _WIN_ALL
+ static int ThreadPriority;
+ static void SetPriority(int Priority) {ThreadPriority=Priority;}
+#endif
+};
+
+#endif // RAR_SMP
+
+#endif // _RAR_THREADPOOL_
+
diff --git a/unrar/unrar/timefn.cpp b/unrar/unrar/timefn.cpp
index 3a7b834..0abf49d 100644
--- a/unrar/unrar/timefn.cpp
+++ b/unrar/unrar/timefn.cpp
@@ -1,229 +1,284 @@
#include "rar.hpp"
-RarTime::RarTime()
+void RarTime::GetLocal(RarLocalTime *lt)
{
- Reset();
-}
+#ifdef _WIN_ALL
+ FILETIME ft;
+ GetWinFT(&ft);
+ FILETIME lft;
-#ifdef _WIN_32
-RarTime& RarTime::operator =(FILETIME &ft)
-{
- FILETIME lft,zft;
- FileTimeToLocalFileTime(&ft,&lft);
- SYSTEMTIME st;
- FileTimeToSystemTime(&lft,&st);
- rlt.Year=st.wYear;
- rlt.Month=st.wMonth;
- rlt.Day=st.wDay;
- rlt.Hour=st.wHour;
- rlt.Minute=st.wMinute;
- rlt.Second=st.wSecond;
- rlt.wDay=st.wDayOfWeek;
- rlt.yDay=rlt.Day-1;
- for (int I=1;I<rlt.Month;I++)
+ if (WinNT() < WNT_VISTA)
+ {
+ // SystemTimeToTzSpecificLocalTime based code produces 1 hour error on XP.
+ FileTimeToLocalFileTime(&ft,&lft);
+ }
+ else
{
- static int mdays[12]={31,28,31,30,31,30,31,31,30,31,30,31};
- rlt.yDay+=mdays[I-1];
+ // We use these functions instead of FileTimeToLocalFileTime according to
+ // MSDN recommendation: "To account for daylight saving time
+ // when converting a file time to a local time ..."
+ SYSTEMTIME st1,st2;
+ FileTimeToSystemTime(&ft,&st1);
+ SystemTimeToTzSpecificLocalTime(NULL,&st1,&st2);
+ SystemTimeToFileTime(&st2,&lft);
+
+ // Correct precision loss (low 4 decimal digits) in FileTimeToSystemTime.
+ FILETIME rft;
+ SystemTimeToFileTime(&st1,&rft);
+ uint64 Corrected=INT32TO64(ft.dwHighDateTime,ft.dwLowDateTime)-
+ INT32TO64(rft.dwHighDateTime,rft.dwLowDateTime)+
+ INT32TO64(lft.dwHighDateTime,lft.dwLowDateTime);
+ lft.dwLowDateTime=(DWORD)Corrected;
+ lft.dwHighDateTime=(DWORD)(Corrected>>32);
}
- if (rlt.Month>2 && IsLeapYear(rlt.Year))
- rlt.yDay++;
- st.wMilliseconds=0;
- SystemTimeToFileTime(&st,&zft);
- rlt.Reminder=lft.dwLowDateTime-zft.dwLowDateTime;
- return(*this);
+ SYSTEMTIME st;
+ FileTimeToSystemTime(&lft,&st);
+ lt->Year=st.wYear;
+ lt->Month=st.wMonth;
+ lt->Day=st.wDay;
+ lt->Hour=st.wHour;
+ lt->Minute=st.wMinute;
+ lt->Second=st.wSecond;
+ lt->wDay=st.wDayOfWeek;
+ lt->yDay=lt->Day-1;
+
+ static int mdays[12]={31,28,31,30,31,30,31,31,30,31,30,31};
+ for (uint I=1;I<lt->Month && I<=ASIZE(mdays);I++)
+ lt->yDay+=mdays[I-1];
+
+ if (lt->Month>2 && IsLeapYear(lt->Year))
+ lt->yDay++;
+#else
+ time_t ut=GetUnix();
+ struct tm *t;
+ t=localtime(&ut);
+
+ lt->Year=t->tm_year+1900;
+ lt->Month=t->tm_mon+1;
+ lt->Day=t->tm_mday;
+ lt->Hour=t->tm_hour;
+ lt->Minute=t->tm_min;
+ lt->Second=t->tm_sec;
+ lt->wDay=t->tm_wday;
+ lt->yDay=t->tm_yday;
+#endif
+ lt->Reminder=(itime % TICKS_PER_SECOND);
}
-void RarTime::GetWin32(FILETIME *ft)
+void RarTime::SetLocal(RarLocalTime *lt)
{
+#ifdef _WIN_ALL
SYSTEMTIME st;
- st.wYear=rlt.Year;
- st.wMonth=rlt.Month;
- st.wDay=rlt.Day;
- st.wHour=rlt.Hour;
- st.wMinute=rlt.Minute;
- st.wSecond=rlt.Second;
+ st.wYear=lt->Year;
+ st.wMonth=lt->Month;
+ st.wDay=lt->Day;
+ st.wHour=lt->Hour;
+ st.wMinute=lt->Minute;
+ st.wSecond=lt->Second;
st.wMilliseconds=0;
+ st.wDayOfWeek=0;
FILETIME lft;
- SystemTimeToFileTime(&st,&lft);
- lft.dwLowDateTime+=rlt.Reminder;
- if (lft.dwLowDateTime<rlt.Reminder)
- lft.dwHighDateTime++;
- LocalFileTimeToFileTime(&lft,ft);
-}
-#endif
+ if (SystemTimeToFileTime(&st,&lft))
+ {
+ FILETIME ft;
+ if (WinNT() < WNT_VISTA)
+ {
+ // TzSpecificLocalTimeToSystemTime based code produces 1 hour error on XP.
+ LocalFileTimeToFileTime(&lft,&ft);
+ }
+ else
+ {
+ // Reverse procedure which we do in GetLocal.
+ SYSTEMTIME st1,st2;
+ FileTimeToSystemTime(&lft,&st2); // st2 might be unequal to st, because we added lt->Reminder to lft.
+ TzSpecificLocalTimeToSystemTime(NULL,&st2,&st1);
+ SystemTimeToFileTime(&st1,&ft);
+
+ // Correct precision loss (low 4 decimal digits) in FileTimeToSystemTime.
+ FILETIME rft;
+ SystemTimeToFileTime(&st2,&rft);
+ uint64 Corrected=INT32TO64(lft.dwHighDateTime,lft.dwLowDateTime)-
+ INT32TO64(rft.dwHighDateTime,rft.dwLowDateTime)+
+ INT32TO64(ft.dwHighDateTime,ft.dwLowDateTime);
+ ft.dwLowDateTime=(DWORD)Corrected;
+ ft.dwHighDateTime=(DWORD)(Corrected>>32);
+ }
-#if defined(_UNIX) || defined(_EMX)
-RarTime& RarTime::operator =(time_t ut)
-{
- struct tm *t;
- t=localtime(&ut);
+ SetWinFT(&ft);
+ }
+ else
+ Reset();
+#else
+ struct tm t;
- rlt.Year=t->tm_year+1900;
- rlt.Month=t->tm_mon+1;
- rlt.Day=t->tm_mday;
- rlt.Hour=t->tm_hour;
- rlt.Minute=t->tm_min;
- rlt.Second=t->tm_sec;
- rlt.Reminder=0;
- rlt.wDay=t->tm_wday;
- rlt.yDay=t->tm_yday;
- return(*this);
+ t.tm_sec=lt->Second;
+ t.tm_min=lt->Minute;
+ t.tm_hour=lt->Hour;
+ t.tm_mday=lt->Day;
+ t.tm_mon=lt->Month-1;
+ t.tm_year=lt->Year-1900;
+ t.tm_isdst=-1;
+ SetUnix(mktime(&t));
+#endif
+ itime+=lt->Reminder;
}
-time_t RarTime::GetUnix()
-{
- struct tm t;
- t.tm_sec=rlt.Second;
- t.tm_min=rlt.Minute;
- t.tm_hour=rlt.Hour;
- t.tm_mday=rlt.Day;
- t.tm_mon=rlt.Month-1;
- t.tm_year=rlt.Year-1900;
- t.tm_isdst=-1;
- return(mktime(&t));
+
+#ifdef _WIN_ALL
+void RarTime::GetWinFT(FILETIME *ft)
+{
+ _ULARGE_INTEGER ul;
+ ul.QuadPart=GetWin();
+ ft->dwLowDateTime=ul.LowPart;
+ ft->dwHighDateTime=ul.HighPart;
}
-#endif
-Int64 RarTime::GetRaw()
+void RarTime::SetWinFT(FILETIME *ft)
{
- if (!IsSet())
- return(0);
-#ifdef _WIN_32
- FILETIME ft;
- GetWin32(&ft);
- return(int32to64(ft.dwHighDateTime,ft.dwLowDateTime));
-#elif defined(_UNIX) || defined(_EMX)
- time_t ut=GetUnix();
- return(int32to64(0,ut)*10000000+rlt.Reminder);
-#else
- return(0);
-#endif
+ _ULARGE_INTEGER ul = {ft->dwLowDateTime, ft->dwHighDateTime};
+ SetWin(ul.QuadPart);
}
+#endif
-#ifndef SFX_MODULE
-void RarTime::SetRaw(Int64 RawTime)
+// Get 64-bit representation of Windows FILETIME (100ns since 01.01.1601).
+uint64 RarTime::GetWin()
{
-#ifdef _WIN_32
- FILETIME ft;
- ft.dwHighDateTime=int64to32(RawTime>>32);
- ft.dwLowDateTime=int64to32(RawTime);
- *this=ft;
-#elif defined(_UNIX) || defined(_EMX)
- time_t ut=int64to32(RawTime/10000000);
- *this=ut;
- rlt.Reminder=int64to32(RawTime%10000000);
-#endif
+ return itime/(TICKS_PER_SECOND/10000000);
}
-#endif
-bool RarTime::operator == (RarTime &rt)
+// Set 64-bit representation of Windows FILETIME (100ns since 01.01.1601).
+void RarTime::SetWin(uint64 WinTime)
{
- return(rlt.Year==rt.rlt.Year && rlt.Month==rt.rlt.Month &&
- rlt.Day==rt.rlt.Day && rlt.Hour==rt.rlt.Hour &&
- rlt.Minute==rt.rlt.Minute && rlt.Second==rt.rlt.Second &&
- rlt.Reminder==rt.rlt.Reminder);
+ itime=WinTime*(TICKS_PER_SECOND/10000000);
}
-bool RarTime::operator < (RarTime &rt)
+time_t RarTime::GetUnix()
{
- return(GetRaw()<rt.GetRaw());
+ return time_t(GetUnixNS()/1000000000);
}
-bool RarTime::operator <= (RarTime &rt)
+void RarTime::SetUnix(time_t ut)
{
- return(*this<rt || *this==rt);
+ if (sizeof(ut)>4)
+ SetUnixNS(uint64(ut)*1000000000);
+ else
+ {
+ // Convert 32-bit and possibly signed time_t to uint32 first,
+ // uint64 cast is not enough. Otherwise sign can expand to 64 bits.
+ SetUnixNS(uint64(uint32(ut))*1000000000);
+ }
}
-bool RarTime::operator > (RarTime &rt)
+// Get the high precision Unix time in nanoseconds since 01-01-1970.
+uint64 RarTime::GetUnixNS()
{
- return(GetRaw()>rt.GetRaw());
+ // 11644473600000000000 - number of ns between 01-01-1601 and 01-01-1970.
+ uint64 ushift=INT32TO64(0xA1997B0B,0x4C6A0000);
+ return itime*(1000000000/TICKS_PER_SECOND)-ushift;
}
-bool RarTime::operator >= (RarTime &rt)
+// Set the high precision Unix time in nanoseconds since 01-01-1970.
+void RarTime::SetUnixNS(uint64 ns)
{
- return(*this>rt || *this==rt);
+ // 11644473600000000000 - number of ns between 01-01-1601 and 01-01-1970.
+ uint64 ushift=INT32TO64(0xA1997B0B,0x4C6A0000);
+ itime=(ns+ushift)/(1000000000/TICKS_PER_SECOND);
}
uint RarTime::GetDos()
{
- uint DosTime=(rlt.Second/2)|(rlt.Minute<<5)|(rlt.Hour<<11)|
- (rlt.Day<<16)|(rlt.Month<<21)|((rlt.Year-1980)<<25);
- return(DosTime);
+ RarLocalTime lt;
+ GetLocal(&lt);
+ uint DosTime=(lt.Second/2)|(lt.Minute<<5)|(lt.Hour<<11)|
+ (lt.Day<<16)|(lt.Month<<21)|((lt.Year-1980)<<25);
+ return DosTime;
}
void RarTime::SetDos(uint DosTime)
{
- rlt.Second=(DosTime & 0x1f)*2;
- rlt.Minute=(DosTime>>5) & 0x3f;
- rlt.Hour=(DosTime>>11) & 0x1f;
- rlt.Day=(DosTime>>16) & 0x1f;
- rlt.Month=(DosTime>>21) & 0x0f;
- rlt.Year=(DosTime>>25)+1980;
- rlt.Reminder=0;
+ RarLocalTime lt;
+ lt.Second=(DosTime & 0x1f)*2;
+ lt.Minute=(DosTime>>5) & 0x3f;
+ lt.Hour=(DosTime>>11) & 0x1f;
+ lt.Day=(DosTime>>16) & 0x1f;
+ lt.Month=(DosTime>>21) & 0x0f;
+ lt.Year=(DosTime>>25)+1980;
+ lt.Reminder=0;
+ SetLocal(&lt);
}
-#if !defined(GUI) || !defined(SFX_MODULE)
-void RarTime::GetText(char *DateStr,bool FullYear)
+void RarTime::GetText(wchar *DateStr,size_t MaxSize,bool FullMS)
{
- if (FullYear)
- sprintf(DateStr,"%02u-%02u-%u %02u:%02u",rlt.Day,rlt.Month,rlt.Year,rlt.Hour,rlt.Minute);
+ if (IsSet())
+ {
+ RarLocalTime lt;
+ GetLocal(&lt);
+ if (FullMS)
+ swprintf(DateStr,MaxSize,L"%u-%02u-%02u %02u:%02u:%02u,%09u",lt.Year,lt.Month,lt.Day,lt.Hour,lt.Minute,lt.Second,lt.Reminder*(1000000000/TICKS_PER_SECOND));
+ else
+ swprintf(DateStr,MaxSize,L"%u-%02u-%02u %02u:%02u",lt.Year,lt.Month,lt.Day,lt.Hour,lt.Minute);
+ }
else
- sprintf(DateStr,"%02u-%02u-%02u %02u:%02u",rlt.Day,rlt.Month,rlt.Year%100,rlt.Hour,rlt.Minute);
+ {
+ // We use escape before '?' to avoid weird C trigraph characters.
+ wcsncpyz(DateStr,L"\?\?\?\?-\?\?-\?\? \?\?:\?\?",MaxSize);
+ }
}
-#endif
#ifndef SFX_MODULE
-void RarTime::SetIsoText(char *TimeText)
+void RarTime::SetIsoText(const wchar *TimeText)
{
int Field[6];
memset(Field,0,sizeof(Field));
- for (int DigitCount=0;*TimeText!=0;TimeText++)
- if (isdigit(*TimeText))
+ for (uint DigitCount=0;*TimeText!=0;TimeText++)
+ if (IsDigit(*TimeText))
{
int FieldPos=DigitCount<4 ? 0:(DigitCount-4)/2+1;
- if (FieldPos<sizeof(Field)/sizeof(Field[0]))
+ if (FieldPos<ASIZE(Field))
Field[FieldPos]=Field[FieldPos]*10+*TimeText-'0';
DigitCount++;
}
- rlt.Second=Field[5];
- rlt.Minute=Field[4];
- rlt.Hour=Field[3];
- rlt.Day=Field[2]==0 ? 1:Field[2];
- rlt.Month=Field[1]==0 ? 1:Field[1];
- rlt.Year=Field[0];
- rlt.Reminder=0;
+ RarLocalTime lt;
+ lt.Second=Field[5];
+ lt.Minute=Field[4];
+ lt.Hour=Field[3];
+ lt.Day=Field[2]==0 ? 1:Field[2];
+ lt.Month=Field[1]==0 ? 1:Field[1];
+ lt.Year=Field[0];
+ lt.Reminder=0;
+ SetLocal(&lt);
}
#endif
#ifndef SFX_MODULE
-void RarTime::SetAgeText(char *TimeText)
+void RarTime::SetAgeText(const wchar *TimeText)
{
uint Seconds=0,Value=0;
- for (int I=0;TimeText[I]!=0;I++)
+ for (uint I=0;TimeText[I]!=0;I++)
{
- int Ch=TimeText[I];
- if (isdigit(Ch))
+ wchar Ch=TimeText[I];
+ if (IsDigit(Ch))
Value=Value*10+Ch-'0';
else
{
- switch(etoupper(Ch))
+ switch(etoupperw(Ch))
{
case 'D':
Seconds+=Value*24*3600;
@@ -242,45 +297,44 @@ void RarTime::SetAgeText(char *TimeText)
}
}
SetCurrentTime();
- Int64 RawTime=GetRaw();
- SetRaw(RawTime-int32to64(0,Seconds)*10000000);
+ itime-=uint64(Seconds)*TICKS_PER_SECOND;
}
#endif
void RarTime::SetCurrentTime()
{
-#ifdef _WIN_32
+#ifdef _WIN_ALL
FILETIME ft;
SYSTEMTIME st;
GetSystemTime(&st);
SystemTimeToFileTime(&st,&ft);
- *this=ft;
+ SetWinFT(&ft);
#else
time_t st;
time(&st);
- *this=st;
+ SetUnix(st);
#endif
}
-#if !defined(SFX_MODULE) && !defined(_WIN_CE)
-const char *GetMonthName(int Month)
+// Add the specified signed number of nanoseconds.
+void RarTime::Adjust(int64 ns)
{
-#ifdef SILENT
- return("");
-#else
- static MSGID MonthID[]={
- MMonthJan,MMonthFeb,MMonthMar,MMonthApr,MMonthMay,MMonthJun,
- MMonthJul,MMonthAug,MMonthSep,MMonthOct,MMonthNov,MMonthDec
- };
- return(St(MonthID[Month]));
-#endif
+ ns/=1000000000/TICKS_PER_SECOND; // Convert ns to internal ticks.
+ itime+=(uint64)ns;
+}
+
+
+#ifndef SFX_MODULE
+const wchar *GetMonthName(int Month)
+{
+ return uiGetMonthName(Month);
}
#endif
bool IsLeapYear(int Year)
{
- return((Year&3)==0 && (Year%100!=0 || Year%400==0));
+ return (Year&3)==0 && (Year%100!=0 || Year%400==0);
}
diff --git a/unrar/unrar/timefn.hpp b/unrar/unrar/timefn.hpp
index 9ab5032..49b61e8 100644
--- a/unrar/unrar/timefn.hpp
+++ b/unrar/unrar/timefn.hpp
@@ -9,7 +9,7 @@ struct RarLocalTime
uint Hour;
uint Minute;
uint Second;
- uint Reminder;
+ uint Reminder; // Part of time smaller than 1 second, represented in 1/REMINDER_PRECISION intervals.
uint wDay;
uint yDay;
};
@@ -18,40 +18,59 @@ struct RarLocalTime
class RarTime
{
private:
- Int64 GetRaw();
- void SetRaw(Int64 RawTime);
+ static const uint TICKS_PER_SECOND = 1000000000; // Internal precision.
- RarLocalTime rlt;
-
- Int64 Time;
+ // Internal time representation in 1/TICKS_PER_SECOND since 01.01.1601.
+ // We use nanoseconds here to handle the high precision Unix time.
+ // It allows dates up to July 2185.
+ //
+ // If we'll ever need to extend the date range, we can define a lower
+ // precision Windows version of TICKS_PER_SECOND. But then Unix and Windows
+ // versions can differ in least significant digits of "lt" time output
+ // for Unix archives.
+ // Alternatively we can introduce 'bool HighPrecision' set to true
+ // in SetUnixNS() and TicksPerSecond() instead of constant above.
+ // It might be more reliable than defining TicksPerSecond variable,
+ // which wouldn't survive memset of any structure hosting RarTime.
+ // We would need to eliminate all such memsets in the entire code first.
+ uint64 itime;
+ public:
+ // RarLocalTime::Reminder precision. Must be equal to TICKS_PER_SECOND.
+ // Unlike TICKS_PER_SECOND, it is a public field.
+ static const uint REMINDER_PRECISION = TICKS_PER_SECOND;
public:
- RarTime();
-#ifdef _WIN_32
- RarTime& operator =(FILETIME &ft);
- void GetWin32(FILETIME *ft);
+ RarTime() {Reset();}
+ bool operator == (RarTime &rt) {return itime==rt.itime;}
+ bool operator != (RarTime &rt) {return itime!=rt.itime;}
+ bool operator < (RarTime &rt) {return itime<rt.itime;}
+ bool operator <= (RarTime &rt) {return itime<rt.itime || itime==rt.itime;}
+ bool operator > (RarTime &rt) {return itime>rt.itime;}
+ bool operator >= (RarTime &rt) {return itime>rt.itime || itime==rt.itime;}
+
+ void GetLocal(RarLocalTime *lt);
+ void SetLocal(RarLocalTime *lt);
+#ifdef _WIN_ALL
+ void GetWinFT(FILETIME *ft);
+ void SetWinFT(FILETIME *ft);
#endif
-#if defined(_UNIX) || defined(_EMX)
- RarTime& operator =(time_t ut);
+ uint64 GetWin();
+ void SetWin(uint64 WinTime);
time_t GetUnix();
-#endif
- bool operator == (RarTime &rt);
- bool operator < (RarTime &rt);
- bool operator <= (RarTime &rt);
- bool operator > (RarTime &rt);
- bool operator >= (RarTime &rt);
- void GetLocal(RarLocalTime *lt) {*lt=rlt;}
- void SetLocal(RarLocalTime *lt) {rlt=*lt;}
+ void SetUnix(time_t ut);
+ uint64 GetUnixNS();
+ void SetUnixNS(uint64 ns);
uint GetDos();
void SetDos(uint DosTime);
- void GetText(char *DateStr,bool FullYear);
- void SetIsoText(char *TimeText);
- void SetAgeText(char *TimeText);
+ void GetText(wchar *DateStr,size_t MaxSize,bool FullMS);
+ void SetIsoText(const wchar *TimeText);
+ void SetAgeText(const wchar *TimeText);
void SetCurrentTime();
- void Reset() {rlt.Year=0;}
- bool IsSet() {return(rlt.Year!=0);}
+ void Reset() {itime=0;}
+ bool IsSet() {return itime!=0;}
+ void Adjust(int64 ns);
};
-const char *GetMonthName(int Month);
+const wchar *GetMonthName(int Month);
bool IsLeapYear(int Year);
#endif
diff --git a/unrar/unrar/ui.cpp b/unrar/unrar/ui.cpp
new file mode 100644
index 0000000..9713a88
--- /dev/null
+++ b/unrar/unrar/ui.cpp
@@ -0,0 +1,14 @@
+#include "rar.hpp"
+
+#include "uicommon.cpp"
+
+#ifdef SILENT
+#include "uisilent.cpp"
+#else
+
+
+
+
+#include "uiconsole.cpp"
+
+#endif
diff --git a/unrar/unrar/ui.hpp b/unrar/unrar/ui.hpp
new file mode 100644
index 0000000..5def26d
--- /dev/null
+++ b/unrar/unrar/ui.hpp
@@ -0,0 +1,176 @@
+#ifndef _RAR_UI_
+#define _RAR_UI_
+
+// UIERROR_ - error message;
+// UIMSG_ - informational message;
+// UIWAIT_ - message waiting for user confirmation;
+// UIEVENT_ - if simple message is not enough;
+
+enum UIMESSAGE_CODE {
+ UIERROR_SYSERRMSG, UIERROR_GENERALERRMSG, UIERROR_INCERRCOUNT,
+ UIERROR_CHECKSUM, UIERROR_CHECKSUMENC, UIERROR_CHECKSUMPACKED,
+ UIERROR_BADPSW, UIERROR_MEMORY, UIERROR_FILEOPEN, UIERROR_FILECREATE,
+ UIERROR_FILECLOSE, UIERROR_FILESEEK, UIERROR_FILEREAD, UIERROR_FILEWRITE,
+ UIERROR_FILEDELETE, UIERROR_RECYCLEFAILED, UIERROR_FILERENAME,
+ UIERROR_FILEATTR, UIERROR_FILECOPY, UIERROR_FILECOPYHINT,
+ UIERROR_DIRCREATE, UIERROR_SLINKCREATE, UIERROR_HLINKCREATE,
+ UIERROR_NOLINKTARGET, UIERROR_NEEDADMIN, UIERROR_ARCBROKEN,
+ UIERROR_HEADERBROKEN, UIERROR_MHEADERBROKEN, UIERROR_FHEADERBROKEN,
+ UIERROR_SUBHEADERBROKEN, UIERROR_SUBHEADERUNKNOWN,
+ UIERROR_SUBHEADERDATABROKEN, UIERROR_RRDAMAGED, UIERROR_UNKNOWNMETHOD,
+ UIERROR_UNKNOWNENCMETHOD, UIERROR_RENAMING, UIERROR_NEWERRAR,
+ UIERROR_NOTSFX, UIERROR_OLDTOSFX,
+ UIERROR_WRONGSFXVER, UIERROR_HEADENCMISMATCH, UIERROR_DICTOUTMEM,
+ UIERROR_USESMALLERDICT, UIERROR_MODIFYUNKNOWN, UIERROR_MODIFYOLD,
+ UIERROR_MODIFYLOCKED, UIERROR_MODIFYVOLUME, UIERROR_NOTVOLUME,
+ UIERROR_NOTFIRSTVOLUME, UIERROR_RECVOLLIMIT, UIERROR_RECVOLDIFFSETS,
+ UIERROR_RECVOLALLEXIST, UIERROR_RECVOLFOUND, UIERROR_RECONSTRUCTING,
+ UIERROR_RECVOLCANNOTFIX, UIERROR_OPFAILED, UIERROR_UNEXPEOF,
+ UIERROR_BADARCHIVE, UIERROR_CMTBROKEN, UIERROR_INVALIDNAME,
+ UIERROR_NEWRARFORMAT, UIERROR_NOTSUPPORTED, UIERROR_ENCRNOTSUPPORTED,
+ UIERROR_RARZIPONLY, UIERROR_REPAIROLDFORMAT, UIERROR_NOFILESREPAIRED,
+ UIERROR_NOFILESTOADD, UIERROR_NOFILESTODELETE, UIERROR_NOFILESTOEXTRACT,
+ UIERROR_MISSINGVOL, UIERROR_NEEDPREVVOL, UIERROR_UNKNOWNEXTRA,
+ UIERROR_CORRUPTEXTRA, UIERROR_NTFSREQUIRED, UIERROR_ZIPVOLSFX,
+ UIERROR_FILERO, UIERROR_TOOLARGESFX, UIERROR_NOZIPSFX, UIERROR_EMAIL,
+ UIERROR_ACLGET, UIERROR_ACLBROKEN, UIERROR_ACLUNKNOWN, UIERROR_ACLSET,
+ UIERROR_STREAMBROKEN, UIERROR_STREAMUNKNOWN, UIERROR_INCOMPATSWITCH,
+ UIERROR_PATHTOOLONG, UIERROR_DIRSCAN, UIERROR_UOWNERGET,
+ UIERROR_UOWNERBROKEN, UIERROR_UOWNERGETOWNERID, UIERROR_UOWNERGETGROUPID,
+ UIERROR_UOWNERSET, UIERROR_ULINKREAD, UIERROR_ULINKEXIST,
+ UIERROR_OPENPRESERVEATIME, UIERROR_READERRTRUNCATED, UIERROR_READERRCOUNT,
+ UIERROR_DIRNAMEEXISTS,UIERROR_TRUNCPSW,UIERROR_ADJUSTVALUE,
+
+ UIMSG_FIRST,
+ UIMSG_STRING, UIMSG_BUILD, UIMSG_RRSEARCH, UIMSG_ANALYZEFILEDATA,
+ UIMSG_RRFOUND, UIMSG_RRNOTFOUND, UIMSG_RRDAMAGED, UIMSG_BLOCKSRECOVERED,
+ UIMSG_COPYINGDATA, UIMSG_AREADAMAGED, UIMSG_SECTORDAMAGED,
+ UIMSG_SECTORRECOVERED, UIMSG_SECTORNOTRECOVERED, UIMSG_FOUND,
+ UIMSG_CORRECTINGNAME, UIMSG_BADARCHIVE, UIMSG_CREATING, UIMSG_RENAMING,
+ UIMSG_RECVOLCALCCHECKSUM, UIMSG_RECVOLFOUND, UIMSG_RECVOLMISSING,
+ UIMSG_MISSINGVOL, UIMSG_RECONSTRUCTING, UIMSG_CHECKSUM, UIMSG_FAT32SIZE,
+ UIMSG_SKIPENCARC, UIMSG_FILERENAME,
+
+ UIWAIT_FIRST,
+ UIWAIT_DISKFULLNEXT, UIWAIT_FCREATEERROR, UIWAIT_BADPSW,
+
+ UIEVENT_FIRST,
+ UIEVENT_SEARCHDUPFILESSTART, UIEVENT_SEARCHDUPFILESEND,
+ UIEVENT_CLEARATTRSTART, UIEVENT_CLEARATTRFILE,
+ UIEVENT_DELADDEDSTART, UIEVENT_DELADDEDFILE, UIEVENT_FILESFOUND,
+ UIEVENT_ERASEDISK, UIEVENT_FILESUMSTART, UIEVENT_FILESUMPROGRESS,
+ UIEVENT_FILESUMEND, UIEVENT_PROTECTSTART, UIEVENT_PROTECTEND,
+ UIEVENT_TESTADDEDSTART, UIEVENT_TESTADDEDEND, UIEVENT_RRTESTINGSTART,
+ UIEVENT_RRTESTINGEND, UIEVENT_NEWARCHIVE, UIEVENT_NEWREVFILE
+};
+
+// Flags for uiAskReplace function.
+enum UIASKREP_FLAGS {
+ UIASKREP_F_NORENAME=1,UIASKREP_F_EXCHSRCDEST=2,UIASKREP_F_SHOWNAMEONLY=4
+};
+
+// Codes returned by uiAskReplace. Note that uiAskReplaceEx returns only
+// UIASKREP_R_REPLACE, UIASKREP_R_SKIP and UIASKREP_R_CANCEL codes.
+enum UIASKREP_RESULT {
+ UIASKREP_R_REPLACE,UIASKREP_R_SKIP,UIASKREP_R_REPLACEALL,UIASKREP_R_SKIPALL,
+ UIASKREP_R_RENAME,UIASKREP_R_RENAMEAUTO,UIASKREP_R_CANCEL,UIASKREP_R_UNUSED
+};
+
+UIASKREP_RESULT uiAskReplace(wchar *Name,size_t MaxNameSize,int64 FileSize,RarTime *FileTime,uint Flags);
+UIASKREP_RESULT uiAskReplaceEx(CommandData *Cmd,wchar *Name,size_t MaxNameSize,int64 FileSize,RarTime *FileTime,uint Flags);
+
+void uiInit(SOUND_NOTIFY_MODE Sound);
+
+
+void uiStartArchiveExtract(bool Extract,const wchar *ArcName);
+bool uiStartFileExtract(const wchar *FileName,bool Extract,bool Test,bool Skip);
+void uiExtractProgress(int64 CurFileSize,int64 TotalFileSize,int64 CurSize,int64 TotalSize);
+void uiProcessProgress(const char *Command,int64 CurSize,int64 TotalSize);
+
+enum UIPASSWORD_TYPE {UIPASSWORD_GLOBAL,UIPASSWORD_FILE,UIPASSWORD_ARCHIVE};
+bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password,CheckPassword *CheckPwd);
+bool uiIsGlobalPasswordSet();
+
+enum UIALARM_TYPE {UIALARM_ERROR, UIALARM_INFO, UIALARM_QUESTION};
+void uiAlarm(UIALARM_TYPE Type);
+
+void uiEolAfterMsg();
+
+bool uiAskNextVolume(wchar *VolName,size_t MaxSize);
+#if !defined(SILENT) && !defined(SFX_MODULE)
+void uiAskRepeatRead(const wchar *FileName,bool &Ignore,bool &All,bool &Retry,bool &Quit);
+#endif
+bool uiAskRepeatWrite(const wchar *FileName,bool DiskFull);
+
+#ifndef SFX_MODULE
+const wchar *uiGetMonthName(int Month);
+#endif
+
+class uiMsgStore
+{
+ private:
+ static const size_t MAX_MSG = 8;
+ const wchar *Str[MAX_MSG];
+ uint Num[MAX_MSG];
+ uint StrSize,NumSize;
+ UIMESSAGE_CODE Code;
+ public:
+ uiMsgStore(UIMESSAGE_CODE Code)
+ {
+ // Init arrays in case a caller passes fewer parameters than expected.
+ for (uint I=0;I<ASIZE(Str);I++)
+ Str[I]=L"";
+ memset(Num,0,sizeof(Num));
+
+ NumSize=StrSize=0;
+ this->Code=Code;
+ }
+ uiMsgStore& operator << (const wchar *s)
+ {
+ if (StrSize<MAX_MSG)
+ Str[StrSize++]=s;
+ return *this;
+ }
+ uiMsgStore& operator << (uint n)
+ {
+ if (NumSize<MAX_MSG)
+ Num[NumSize++]=n;
+ return *this;
+ }
+
+ void Msg();
+};
+
+
+// Templates recognize usual NULL as integer, not wchar*.
+#define UINULL ((wchar *)NULL)
+
+inline void uiMsgBase(uiMsgStore &Store)
+{
+ // Called last, when no parameters are left.
+}
+
+template<class T1,class... TN> void uiMsgBase(uiMsgStore &Store,T1&& a1,TN&&... aN)
+{
+ // Process first parameter and pass the rest to same uiMsgBase.
+ Store<<a1;
+ uiMsgBase(Store,aN...);
+}
+
+
+// Use variadic templates.
+//
+// We must pass variable parameters by reference, so no temporary copies are
+// created for custom string objects like CStringBase in 7-Zip decompression
+// code. Such temporary copies would be destroyed inside of recursive
+// uiMsgBase calls, leaving us with Str[] items pointing at released memory.
+// Since we pass integer values as well, we can't use & references
+// and must resort to && rvalue references.
+template<class... TN> void uiMsg(UIMESSAGE_CODE Code,TN&&... aN)
+{
+ uiMsgStore Store(Code);
+ uiMsgBase(Store,aN...);
+ Store.Msg();
+}
+
+#endif
diff --git a/unrar/unrar/uicommon.cpp b/unrar/unrar/uicommon.cpp
new file mode 100644
index 0000000..1a12011
--- /dev/null
+++ b/unrar/unrar/uicommon.cpp
@@ -0,0 +1,65 @@
+static SOUND_NOTIFY_MODE uiSoundNotify;
+
+void uiInit(SOUND_NOTIFY_MODE Sound)
+{
+ uiSoundNotify = Sound;
+}
+
+
+// Additionally to handling user input, it analyzes and sets command options.
+// Returns only 'replace', 'skip' and 'cancel' codes.
+UIASKREP_RESULT uiAskReplaceEx(CommandData *Cmd,wchar *Name,size_t MaxNameSize,int64 FileSize,RarTime *FileTime,uint Flags)
+{
+ if (Cmd->Overwrite==OVERWRITE_NONE)
+ return UIASKREP_R_SKIP;
+
+#if !defined(SFX_MODULE) && !defined(SILENT)
+ // Must be before Cmd->AllYes check or -y switch would override -or.
+ if (Cmd->Overwrite==OVERWRITE_AUTORENAME && GetAutoRenamedName(Name,MaxNameSize))
+ return UIASKREP_R_REPLACE;
+#endif
+
+ // This check must be after OVERWRITE_AUTORENAME processing or -y switch
+ // would override -or.
+ if (Cmd->AllYes || Cmd->Overwrite==OVERWRITE_ALL)
+ {
+ PrepareToDelete(Name);
+ return UIASKREP_R_REPLACE;
+ }
+
+ wchar NewName[NM];
+ wcsncpyz(NewName,Name,ASIZE(NewName));
+ UIASKREP_RESULT Choice=uiAskReplace(NewName,ASIZE(NewName),FileSize,FileTime,Flags);
+
+ if (Choice==UIASKREP_R_REPLACE || Choice==UIASKREP_R_REPLACEALL)
+ PrepareToDelete(Name);
+
+ if (Choice==UIASKREP_R_REPLACEALL)
+ {
+ Cmd->Overwrite=OVERWRITE_ALL;
+ return UIASKREP_R_REPLACE;
+ }
+ if (Choice==UIASKREP_R_SKIPALL)
+ {
+ Cmd->Overwrite=OVERWRITE_NONE;
+ return UIASKREP_R_SKIP;
+ }
+ if (Choice==UIASKREP_R_RENAME)
+ {
+ if (PointToName(NewName)==NewName)
+ SetName(Name,NewName,MaxNameSize);
+ else
+ wcsncpyz(Name,NewName,MaxNameSize);
+ if (FileExist(Name))
+ return uiAskReplaceEx(Cmd,Name,MaxNameSize,FileSize,FileTime,Flags);
+ return UIASKREP_R_REPLACE;
+ }
+#if !defined(SFX_MODULE) && !defined(SILENT)
+ if (Choice==UIASKREP_R_RENAMEAUTO && GetAutoRenamedName(Name,MaxNameSize))
+ {
+ Cmd->Overwrite=OVERWRITE_AUTORENAME;
+ return UIASKREP_R_REPLACE;
+ }
+#endif
+ return Choice;
+}
diff --git a/unrar/unrar/uiconsole.cpp b/unrar/unrar/uiconsole.cpp
new file mode 100644
index 0000000..b524c25
--- /dev/null
+++ b/unrar/unrar/uiconsole.cpp
@@ -0,0 +1,484 @@
+static bool AnyMessageDisplayed=false; // For console -idn switch.
+
+// Purely user interface function. Gets and returns user input.
+UIASKREP_RESULT uiAskReplace(wchar *Name,size_t MaxNameSize,int64 FileSize,RarTime *FileTime,uint Flags)
+{
+ wchar SizeText1[20],DateStr1[50],SizeText2[20],DateStr2[50];
+
+ FindData ExistingFD;
+ memset(&ExistingFD,0,sizeof(ExistingFD)); // In case find fails.
+ FindFile::FastFind(Name,&ExistingFD);
+ itoa(ExistingFD.Size,SizeText1,ASIZE(SizeText1));
+ ExistingFD.mtime.GetText(DateStr1,ASIZE(DateStr1),false);
+
+ if (FileSize==INT64NDF || FileTime==NULL)
+ {
+ eprintf(L"\n");
+ eprintf(St(MAskOverwrite),Name);
+ }
+ else
+ {
+ itoa(FileSize,SizeText2,ASIZE(SizeText2));
+ FileTime->GetText(DateStr2,ASIZE(DateStr2),false);
+ if ((Flags & UIASKREP_F_EXCHSRCDEST)==0)
+ eprintf(St(MAskReplace),Name,SizeText1,DateStr1,SizeText2,DateStr2);
+ else
+ eprintf(St(MAskReplace),Name,SizeText2,DateStr2,SizeText1,DateStr1);
+ }
+
+ bool AllowRename=(Flags & UIASKREP_F_NORENAME)==0;
+ int Choice=0;
+ do
+ {
+ Choice=Ask(St(AllowRename ? MYesNoAllRenQ : MYesNoAllQ));
+ } while (Choice==0); // 0 means invalid input.
+ switch(Choice)
+ {
+ case 1:
+ return UIASKREP_R_REPLACE;
+ case 2:
+ return UIASKREP_R_SKIP;
+ case 3:
+ return UIASKREP_R_REPLACEALL;
+ case 4:
+ return UIASKREP_R_SKIPALL;
+ }
+ if (AllowRename && Choice==5)
+ {
+ mprintf(St(MAskNewName));
+ if (getwstr(Name,MaxNameSize))
+ return UIASKREP_R_RENAME;
+ else
+ return UIASKREP_R_SKIP; // Process fwgets failure as if user answered 'No'.
+ }
+ return UIASKREP_R_CANCEL;
+}
+
+
+
+
+void uiStartArchiveExtract(bool Extract,const wchar *ArcName)
+{
+ mprintf(St(Extract ? MExtracting : MExtrTest), ArcName);
+}
+
+
+bool uiStartFileExtract(const wchar *FileName,bool Extract,bool Test,bool Skip)
+{
+ return true;
+}
+
+
+void uiExtractProgress(int64 CurFileSize,int64 TotalFileSize,int64 CurSize,int64 TotalSize)
+{
+ // We set the total size to 0 to update only the current progress and keep
+ // the total progress intact in WinRAR. Unlike WinRAR, console RAR has only
+ // the total progress and updates it with current values in such case.
+ int CurPercent=TotalSize!=0 ? ToPercent(CurSize,TotalSize) : ToPercent(CurFileSize,TotalFileSize);
+ mprintf(L"\b\b\b\b%3d%%",CurPercent);
+}
+
+
+void uiProcessProgress(const char *Command,int64 CurSize,int64 TotalSize)
+{
+ int CurPercent=ToPercent(CurSize,TotalSize);
+ mprintf(L"\b\b\b\b%3d%%",CurPercent);
+}
+
+
+void uiMsgStore::Msg()
+{
+ // When creating volumes, AnyMessageDisplayed must be reset for UIEVENT_NEWARCHIVE,
+ // so it ignores this and all earlier messages like UIEVENT_PROTECTEND
+ // and UIEVENT_PROTECTEND, because they precede "Creating archive" message
+ // and do not interfere with -idn and file names. If we do not ignore them,
+ // uiEolAfterMsg() in uiStartFileAddit() can cause unneeded carriage return
+ // in archiving percent after creating a new volume with -v -idn (and -rr
+ // for UIEVENT_PROTECT*) switches. AnyMessageDisplayed is set for messages
+ // after UIEVENT_NEWARCHIVE, so archiving percent with -idn is moved to
+ // next line and does not delete their last characters.
+ // Similarly we ignore UIEVENT_RRTESTINGEND for volumes, because it is issued
+ // before "Testing archive" and would add an excessive \n otherwise.
+ AnyMessageDisplayed=(Code!=UIEVENT_NEWARCHIVE && Code!=UIEVENT_RRTESTINGEND);
+
+ switch(Code)
+ {
+ case UIERROR_SYSERRMSG:
+ case UIERROR_GENERALERRMSG:
+ Log(NULL,L"\n%ls",Str[0]);
+ break;
+ case UIERROR_CHECKSUM:
+ Log(Str[0],St(MCRCFailed),Str[1]);
+ break;
+ case UIERROR_CHECKSUMENC:
+ Log(Str[0],St(MEncrBadCRC),Str[1]);
+ break;
+ case UIERROR_CHECKSUMPACKED:
+ Log(Str[0],St(MDataBadCRC),Str[1],Str[0]);
+ break;
+ case UIERROR_BADPSW:
+ Log(Str[0],St(MWrongFilePassword),Str[1]);
+ break;
+ case UIWAIT_BADPSW:
+ Log(Str[0],St(MWrongPassword));
+ break;
+ case UIERROR_MEMORY:
+ mprintf(L"\n");
+ Log(NULL,St(MErrOutMem));
+ break;
+ case UIERROR_FILEOPEN:
+ Log(Str[0],St(MCannotOpen),Str[1]);
+ break;
+ case UIERROR_FILECREATE:
+ Log(Str[0],St(MCannotCreate),Str[1]);
+ break;
+ case UIERROR_FILECLOSE:
+ Log(NULL,St(MErrFClose),Str[0]);
+ break;
+ case UIERROR_FILESEEK:
+ Log(NULL,St(MErrSeek),Str[0]);
+ break;
+ case UIERROR_FILEREAD:
+ mprintf(L"\n");
+ Log(Str[0],St(MErrRead),Str[1]);
+ break;
+ case UIERROR_FILEWRITE:
+ Log(Str[0],St(MErrWrite),Str[1]);
+ break;
+#ifndef SFX_MODULE
+ case UIERROR_FILEDELETE:
+ Log(Str[0],St(MCannotDelete),Str[1]);
+ break;
+ case UIERROR_RECYCLEFAILED:
+ Log(Str[0],St(MRecycleFailed));
+ break;
+ case UIERROR_FILERENAME:
+ Log(Str[0],St(MErrRename),Str[1],Str[2]);
+ break;
+#endif
+ case UIERROR_FILEATTR:
+ Log(Str[0],St(MErrChangeAttr),Str[1]);
+ break;
+ case UIERROR_FILECOPY:
+ Log(Str[0],St(MCopyError),Str[1],Str[2]);
+ break;
+ case UIERROR_FILECOPYHINT:
+ Log(Str[0],St(MCopyErrorHint));
+ mprintf(L" "); // For progress percent.
+ break;
+ case UIERROR_DIRCREATE:
+ Log(Str[0],St(MExtrErrMkDir),Str[1]);
+ break;
+ case UIERROR_SLINKCREATE:
+ Log(Str[0],St(MErrCreateLnkS),Str[1]);
+ break;
+ case UIERROR_HLINKCREATE:
+ Log(NULL,St(MErrCreateLnkH),Str[0]);
+ break;
+ case UIERROR_NOLINKTARGET:
+ Log(NULL,St(MErrLnkTarget));
+ mprintf(L" "); // For progress percent.
+ break;
+ case UIERROR_NEEDADMIN:
+ Log(NULL,St(MNeedAdmin));
+ break;
+ case UIERROR_ARCBROKEN:
+ mprintf(L"\n"); // So it is not merged with preceding UIERROR_HEADERBROKEN.
+ Log(Str[0],St(MErrBrokenArc));
+ break;
+ case UIERROR_HEADERBROKEN:
+ Log(Str[0],St(MHeaderBroken));
+ break;
+ case UIERROR_MHEADERBROKEN:
+ Log(Str[0],St(MMainHeaderBroken));
+ break;
+ case UIERROR_FHEADERBROKEN:
+ Log(Str[0],St(MLogFileHead),Str[1]);
+ break;
+ case UIERROR_SUBHEADERBROKEN:
+ Log(Str[0],St(MSubHeadCorrupt));
+ break;
+ case UIERROR_SUBHEADERUNKNOWN:
+ Log(Str[0],St(MSubHeadUnknown));
+ break;
+ case UIERROR_SUBHEADERDATABROKEN:
+ Log(Str[0],St(MSubHeadDataCRC),Str[1]);
+ break;
+ case UIERROR_RRDAMAGED:
+ Log(Str[0],St(MRRDamaged));
+ break;
+ case UIERROR_UNKNOWNMETHOD:
+ Log(Str[0],St(MUnknownMeth),Str[1]);
+ break;
+ case UIERROR_UNKNOWNENCMETHOD:
+ {
+ wchar Msg[256];
+ swprintf(Msg,ASIZE(Msg),St(MUnkEncMethod),Str[1]);
+ Log(Str[0],L"%s: %s",Msg,Str[2]);
+ }
+ break;
+#ifndef SFX_MODULE
+ case UIERROR_RENAMING:
+ Log(Str[0],St(MRenaming),Str[1],Str[2]);
+ break;
+ case UIERROR_NEWERRAR:
+ Log(Str[0],St(MNewerRAR));
+ break;
+#endif
+ case UIERROR_RECVOLDIFFSETS:
+ Log(NULL,St(MRecVolDiffSets),Str[0],Str[1]);
+ break;
+ case UIERROR_RECVOLALLEXIST:
+ mprintf(St(MRecVolAllExist));
+ break;
+ case UIERROR_RECONSTRUCTING:
+ mprintf(St(MReconstructing));
+ break;
+ case UIERROR_RECVOLCANNOTFIX:
+ mprintf(St(MRecVolCannotFix));
+ break;
+ case UIERROR_UNEXPEOF:
+ Log(Str[0],St(MLogUnexpEOF));
+ break;
+ case UIERROR_BADARCHIVE:
+ Log(Str[0],St(MBadArc),Str[0]);
+ break;
+ case UIERROR_CMTBROKEN:
+ Log(Str[0],St(MLogCommBrk));
+ break;
+ case UIERROR_INVALIDNAME:
+ Log(Str[0],St(MInvalidName),Str[1]);
+ mprintf(L"\n"); // Needed when called from CmdExtract::ExtractCurrentFile.
+ break;
+#ifndef SFX_MODULE
+ case UIERROR_OPFAILED:
+ Log(NULL,St(MOpFailed));
+ break;
+ case UIERROR_NEWRARFORMAT:
+ Log(Str[0],St(MNewRarFormat));
+ break;
+#endif
+ case UIERROR_NOFILESTOEXTRACT:
+ mprintf(St(MExtrNoFiles));
+ break;
+ case UIERROR_MISSINGVOL:
+ Log(Str[0],St(MAbsNextVol),Str[0]);
+ mprintf(L" "); // For progress percent.
+ break;
+#ifndef SFX_MODULE
+ case UIERROR_NEEDPREVVOL:
+ Log(Str[0],St(MUnpCannotMerge),Str[1]);
+ break;
+ case UIERROR_UNKNOWNEXTRA:
+ Log(Str[0],St(MUnknownExtra),Str[1]);
+ break;
+ case UIERROR_CORRUPTEXTRA:
+ Log(Str[0],St(MCorruptExtra),Str[1],Str[2]);
+ break;
+#endif
+#if !defined(SFX_MODULE) && defined(_WIN_ALL)
+ case UIERROR_NTFSREQUIRED:
+ Log(NULL,St(MNTFSRequired),Str[0]);
+ break;
+#endif
+#if !defined(SFX_MODULE) && defined(_WIN_ALL)
+ case UIERROR_ACLBROKEN:
+ Log(Str[0],St(MACLBroken),Str[1]);
+ break;
+ case UIERROR_ACLUNKNOWN:
+ Log(Str[0],St(MACLUnknown),Str[1]);
+ break;
+ case UIERROR_ACLSET:
+ Log(Str[0],St(MACLSetError),Str[1]);
+ break;
+ case UIERROR_STREAMBROKEN:
+ Log(Str[0],St(MStreamBroken),Str[1]);
+ break;
+ case UIERROR_STREAMUNKNOWN:
+ Log(Str[0],St(MStreamUnknown),Str[1]);
+ break;
+#endif
+ case UIERROR_INCOMPATSWITCH:
+ mprintf(St(MIncompatSwitch),Str[0],Num[0]);
+ break;
+ case UIERROR_PATHTOOLONG:
+ Log(NULL,L"\n%ls%ls%ls",Str[0],Str[1],Str[2]);
+ Log(NULL,St(MPathTooLong));
+ break;
+#ifndef SFX_MODULE
+ case UIERROR_DIRSCAN:
+ Log(NULL,St(MScanError),Str[0]);
+ break;
+#endif
+ case UIERROR_UOWNERBROKEN:
+ Log(Str[0],St(MOwnersBroken),Str[1]);
+ break;
+ case UIERROR_UOWNERGETOWNERID:
+ Log(Str[0],St(MErrGetOwnerID),Str[1]);
+ break;
+ case UIERROR_UOWNERGETGROUPID:
+ Log(Str[0],St(MErrGetGroupID),Str[1]);
+ break;
+ case UIERROR_UOWNERSET:
+ Log(Str[0],St(MSetOwnersError),Str[1]);
+ break;
+ case UIERROR_ULINKREAD:
+ Log(NULL,St(MErrLnkRead),Str[0]);
+ break;
+ case UIERROR_ULINKEXIST:
+ Log(NULL,St(MSymLinkExists),Str[0]);
+ break;
+ case UIERROR_READERRTRUNCATED:
+ Log(NULL,St(MErrReadTrunc),Str[0]);
+ break;
+ case UIERROR_READERRCOUNT:
+ Log(NULL,St(MErrReadCount),Num[0]);
+ break;
+ case UIERROR_DIRNAMEEXISTS:
+ Log(NULL,St(MDirNameExists));
+ break;
+ case UIERROR_TRUNCPSW:
+ eprintf(St(MTruncPsw),Num[0]);
+ eprintf(L"\n");
+ break;
+ case UIERROR_ADJUSTVALUE:
+ Log(NULL,St(MAdjustValue),Str[0],Str[1]);
+ break;
+
+#ifndef SFX_MODULE
+ case UIMSG_STRING:
+ mprintf(L"\n%s",Str[0]);
+ break;
+#endif
+ case UIMSG_CORRECTINGNAME:
+ Log(Str[0],St(MCorrectingName));
+ break;
+ case UIMSG_BADARCHIVE:
+ mprintf(St(MBadArc),Str[0]);
+ break;
+ case UIMSG_CREATING:
+ mprintf(St(MCreating),Str[0]);
+ break;
+ case UIMSG_RENAMING:
+ mprintf(St(MRenaming),Str[0],Str[1]);
+ break;
+ case UIMSG_RECVOLCALCCHECKSUM:
+ mprintf(St(MCalcCRCAllVol));
+ break;
+ case UIMSG_RECVOLFOUND:
+ mprintf(St(MRecVolFound),Num[0]);
+ break;
+ case UIMSG_RECVOLMISSING:
+ mprintf(St(MRecVolMissing),Num[0]);
+ break;
+ case UIMSG_MISSINGVOL:
+ mprintf(St(MAbsNextVol),Str[0]);
+ break;
+ case UIMSG_RECONSTRUCTING:
+ mprintf(St(MReconstructing));
+ break;
+ case UIMSG_CHECKSUM:
+ mprintf(St(MCRCFailed),Str[0]);
+ break;
+ case UIMSG_FAT32SIZE:
+ mprintf(St(MFAT32Size));
+ mprintf(L" "); // For progress percent.
+ break;
+ case UIMSG_SKIPENCARC:
+ Log(NULL,St(MSkipEncArc),Str[0]);
+ break;
+
+
+
+ case UIEVENT_RRTESTINGSTART:
+ mprintf(L"%s ",St(MTestingRR));
+ break;
+ }
+}
+
+
+bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName,
+ SecPassword *Password,CheckPassword *CheckPwd)
+{
+ // Unlike GUI we cannot provide Cancel button here, so we use the empty
+ // password to abort. Otherwise user not knowing a password would need to
+ // press Ctrl+C multiple times to quit from infinite password request loop.
+ return GetConsolePassword(Type,FileName,Password) && Password->IsSet();
+}
+
+
+bool uiIsGlobalPasswordSet()
+{
+ return false;
+}
+
+
+void uiAlarm(UIALARM_TYPE Type)
+{
+ if (uiSoundNotify==SOUND_NOTIFY_ON)
+ {
+ static clock_t LastTime=-10; // Negative to always beep first time.
+ if ((MonoClock()-LastTime)/CLOCKS_PER_SEC>5)
+ {
+#ifdef _WIN_ALL
+ MessageBeep(-1);
+#else
+ putwchar('\007');
+#endif
+ LastTime=MonoClock();
+ }
+ }
+}
+
+
+
+
+bool uiAskNextVolume(wchar *VolName,size_t MaxSize)
+{
+ eprintf(St(MAskNextVol),VolName);
+ return Ask(St(MContinueQuit))!=2;
+}
+
+
+void uiAskRepeatRead(const wchar *FileName,bool &Ignore,bool &All,bool &Retry,bool &Quit)
+{
+ eprintf(St(MErrReadInfo));
+ int Code=Ask(St(MIgnoreAllRetryQuit));
+
+ Ignore=(Code==1);
+ All=(Code==2);
+ Quit=(Code==4);
+ Retry=!Ignore && !All && !Quit; // Default also for invalid input, not just for 'Retry'.
+}
+
+
+bool uiAskRepeatWrite(const wchar *FileName,bool DiskFull)
+{
+ mprintf(L"\n");
+ Log(NULL,St(DiskFull ? MNotEnoughDisk:MErrWrite),FileName);
+ return Ask(St(MRetryAbort))==1;
+}
+
+
+#ifndef SFX_MODULE
+const wchar *uiGetMonthName(int Month)
+{
+ static MSGID MonthID[12]={
+ MMonthJan,MMonthFeb,MMonthMar,MMonthApr,MMonthMay,MMonthJun,
+ MMonthJul,MMonthAug,MMonthSep,MMonthOct,MMonthNov,MMonthDec
+ };
+ return St(MonthID[Month]);
+}
+#endif
+
+
+void uiEolAfterMsg()
+{
+ if (AnyMessageDisplayed)
+ {
+ // Avoid deleting several last characters of any previous error message
+ // with percentage indicator in -idn mode.
+ AnyMessageDisplayed=false;
+ mprintf(L"\n");
+ }
+}
diff --git a/unrar/unrar/uisilent.cpp b/unrar/unrar/uisilent.cpp
new file mode 100644
index 0000000..8155885
--- /dev/null
+++ b/unrar/unrar/uisilent.cpp
@@ -0,0 +1,75 @@
+// Purely user interface function. Gets and returns user input.
+UIASKREP_RESULT uiAskReplace(wchar *Name,size_t MaxNameSize,int64 FileSize,RarTime *FileTime,uint Flags)
+{
+ return UIASKREP_R_REPLACE;
+}
+
+
+
+
+void uiStartArchiveExtract(bool Extract,const wchar *ArcName)
+{
+}
+
+
+bool uiStartFileExtract(const wchar *FileName,bool Extract,bool Test,bool Skip)
+{
+ return true;
+}
+
+
+void uiExtractProgress(int64 CurFileSize,int64 TotalFileSize,int64 CurSize,int64 TotalSize)
+{
+}
+
+
+void uiProcessProgress(const char *Command,int64 CurSize,int64 TotalSize)
+{
+}
+
+
+void uiMsgStore::Msg()
+{
+}
+
+
+bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName,
+ SecPassword *Password,CheckPassword *CheckPwd)
+{
+ return false;
+}
+
+
+bool uiIsGlobalPasswordSet()
+{
+ return false;
+}
+
+
+void uiAlarm(UIALARM_TYPE Type)
+{
+}
+
+
+bool uiIsAborted()
+{
+ return false;
+}
+
+
+void uiGiveTick()
+{
+}
+
+
+#ifndef SFX_MODULE
+const wchar *uiGetMonthName(int Month)
+{
+ return L"";
+}
+#endif
+
+
+void uiEolAfterMsg()
+{
+}
diff --git a/unrar/unrar/ulinks.cpp b/unrar/unrar/ulinks.cpp
index b8d6aa5..141a97f 100644
--- a/unrar/unrar/ulinks.cpp
+++ b/unrar/unrar/ulinks.cpp
@@ -1,32 +1,136 @@
-#include "rar.hpp"
-
-int ExtractLink(ComprDataIO &DataIO,Archive &Arc,char *DestName,uint &LinkCRC,bool Create)
+static bool UnixSymlink(CommandData *Cmd,const char *Target,const wchar *LinkName,RarTime *ftm,RarTime *fta)
{
-#if defined(SAVE_LINKS) && defined(_UNIX)
- char FileName[NM];
- if (IsLink(Arc.NewLhd.FileAttr))
+ CreatePath(LinkName,true,Cmd->DisableNames);
+
+ // Overwrite prompt was already issued and confirmed earlier, so we can
+ // remove existing symlink or regular file here. PrepareToDelete was also
+ // called earlier inside of uiAskReplaceEx.
+ DelFile(LinkName);
+
+ char LinkNameA[NM];
+ WideToChar(LinkName,LinkNameA,ASIZE(LinkNameA));
+ if (symlink(Target,LinkNameA)==-1) // Error.
{
- int DataSize=Min(Arc.NewLhd.PackSize,sizeof(FileName)-1);
- DataIO.UnpRead((byte *)FileName,DataSize);
- FileName[DataSize]=0;
- if (Create)
+ if (errno==EEXIST)
+ uiMsg(UIERROR_ULINKEXIST,LinkName);
+ else
{
- CreatePath(DestName,NULL,true);
- if (symlink(FileName,DestName)==-1)
- if (errno==EEXIST)
- Log(Arc.FileName,St(MSymLinkExists),DestName);
- else
- {
- Log(Arc.FileName,St(MErrCreateLnk),DestName);
- ErrHandler.SetErrorCode(WARNING);
- }
+ uiMsg(UIERROR_SLINKCREATE,UINULL,LinkName);
+ ErrHandler.SetErrorCode(RARX_WARNING);
}
- int NameSize=Min(DataSize,strlen(FileName));
- LinkCRC=CRC(0xffffffff,FileName,NameSize);
- return(1);
+ return false;
}
+#ifdef USE_LUTIMES
+#ifdef UNIX_TIME_NS
+ timespec times[2];
+ times[0].tv_sec=fta->GetUnix();
+ times[0].tv_nsec=fta->IsSet() ? long(fta->GetUnixNS()%1000000000) : UTIME_NOW;
+ times[1].tv_sec=ftm->GetUnix();
+ times[1].tv_nsec=ftm->IsSet() ? long(ftm->GetUnixNS()%1000000000) : UTIME_NOW;
+ utimensat(AT_FDCWD,LinkNameA,times,AT_SYMLINK_NOFOLLOW);
+#else
+ struct timeval tv[2];
+ tv[0].tv_sec=fta->GetUnix();
+ tv[0].tv_usec=long(fta->GetUnixNS()%1000000000/1000);
+ tv[1].tv_sec=ftm->GetUnix();
+ tv[1].tv_usec=long(ftm->GetUnixNS()%1000000000/1000);
+ lutimes(LinkNameA,tv);
+#endif
#endif
- return(0);
+
+ return true;
+}
+
+
+static bool IsFullPath(const char *PathA) // Unix ASCII version.
+{
+ return *PathA==CPATHDIVIDER;
+}
+
+
+// For security purpose we prefer to be sure that CharToWide completed
+// successfully and even if it truncated a string for some reason,
+// it didn't affect the number of path related characters we analyze
+// in IsRelativeSymlinkSafe later.
+// This check is likely to be excessive, but let's keep it anyway.
+static bool SafeCharToWide(const char *Src,wchar *Dest,size_t DestSize)
+{
+ if (!CharToWide(Src,Dest,DestSize) || *Dest==0)
+ return false;
+ uint SrcChars=0,DestChars=0;
+ for (uint I=0;Src[I]!=0;I++)
+ if (Src[I]=='/' || Src[I]=='.')
+ SrcChars++;
+ for (uint I=0;Dest[I]!=0;I++)
+ if (Dest[I]=='/' || Dest[I]=='.')
+ DestChars++;
+ return SrcChars==DestChars;
+}
+
+
+static bool ExtractUnixLink30(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,
+ const wchar *LinkName,bool &UpLink)
+{
+ char Target[NM];
+ if (IsLink(Arc.FileHead.FileAttr))
+ {
+ size_t DataSize=(size_t)Arc.FileHead.PackSize;
+ if (DataSize>ASIZE(Target)-1)
+ return false;
+ if ((size_t)DataIO.UnpRead((byte *)Target,DataSize)!=DataSize)
+ return false;
+ Target[DataSize]=0;
+
+ DataIO.UnpHash.Init(Arc.FileHead.FileHash.Type,1);
+ DataIO.UnpHash.Update(Target,strlen(Target));
+ DataIO.UnpHash.Result(&Arc.FileHead.FileHash);
+
+ // Return true in case of bad checksum, so link will be processed further
+ // and extraction routine will report the checksum error.
+ if (!DataIO.UnpHash.Cmp(&Arc.FileHead.FileHash,Arc.FileHead.UseHashKey ? Arc.FileHead.HashKey:NULL))
+ return true;
+
+ wchar TargetW[NM];
+ if (!SafeCharToWide(Target,TargetW,ASIZE(TargetW)))
+ return false;
+ // Use Arc.FileHead.FileName instead of LinkName, since LinkName
+ // can include the destination path as a prefix, which can
+ // confuse IsRelativeSymlinkSafe algorithm.
+ if (!Cmd->AbsoluteLinks && (IsFullPath(TargetW) ||
+ !IsRelativeSymlinkSafe(Cmd,Arc.FileHead.FileName,LinkName,TargetW)))
+ return false;
+ UpLink=strstr(Target,"..")!=NULL;
+ return UnixSymlink(Cmd,Target,LinkName,&Arc.FileHead.mtime,&Arc.FileHead.atime);
+ }
+ return false;
+}
+
+
+static bool ExtractUnixLink50(CommandData *Cmd,const wchar *Name,FileHeader *hd)
+{
+ char Target[NM];
+ WideToChar(hd->RedirName,Target,ASIZE(Target));
+ if (hd->RedirType==FSREDIR_WINSYMLINK || hd->RedirType==FSREDIR_JUNCTION)
+ {
+ // Cannot create Windows absolute path symlinks in Unix. Only relative path
+ // Windows symlinks can be created here. RAR 5.0 used \??\ prefix
+ // for Windows absolute symlinks, since RAR 5.1 /??/ is used.
+ // We escape ? as \? to avoid "trigraph" warning
+ if (strncmp(Target,"\\??\\",4)==0 || strncmp(Target,"/\?\?/",4)==0)
+ return false;
+ DosSlashToUnix(Target,Target,ASIZE(Target));
+ }
+
+ wchar TargetW[NM];
+ if (!SafeCharToWide(Target,TargetW,ASIZE(TargetW)))
+ return false;
+ // Use hd->FileName instead of LinkName, since LinkName can include
+ // the destination path as a prefix, which can confuse
+ // IsRelativeSymlinkSafe algorithm.
+ if (!Cmd->AbsoluteLinks && (IsFullPath(TargetW) ||
+ !IsRelativeSymlinkSafe(Cmd,hd->FileName,Name,TargetW)))
+ return false;
+ return UnixSymlink(Cmd,Target,Name,&hd->mtime,&hd->atime);
}
diff --git a/unrar/unrar/ulinks.hpp b/unrar/unrar/ulinks.hpp
deleted file mode 100644
index 69b0e9f..0000000
--- a/unrar/unrar/ulinks.hpp
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef _RAR_ULINKS_
-#define _RAR_ULINKS_
-
-void SaveLinkData(ComprDataIO &DataIO,Archive &TempArc,FileHeader &hd,
- char *Name);
-int ExtractLink(ComprDataIO &DataIO,Archive &Arc,char *DestName,
- uint &LinkCRC,bool Create);
-
-#endif
diff --git a/unrar/unrar/unicode.cpp b/unrar/unrar/unicode.cpp
index affc905..73b09bb 100644
--- a/unrar/unrar/unicode.cpp
+++ b/unrar/unrar/unicode.cpp
@@ -1,183 +1,345 @@
#include "rar.hpp"
+#define MBFUNCTIONS
+
+#if defined(_UNIX) && defined(MBFUNCTIONS)
+
+static bool WideToCharMap(const wchar *Src,char *Dest,size_t DestSize,bool &Success);
+static void CharToWideMap(const char *Src,wchar *Dest,size_t DestSize,bool &Success);
+
+// In Unix we map high ASCII characters which cannot be converted to Unicode
+// to 0xE000 - 0xE0FF private use Unicode area.
+static const uint MapAreaStart=0xE000;
+
+// Mapped string marker. Initially we used 0xFFFF for this purpose,
+// but it causes MSVC2008 swprintf to fail (it treats 0xFFFF as error marker).
+// While we could workaround it, it is safer to use another character.
+static const uint MappedStringMark=0xFFFE;
-#if defined(_EMX) && !defined(_DJGPP)
-#include "unios2.cpp"
#endif
-bool WideToChar(const wchar *Src,char *Dest,int DestSize)
+bool WideToChar(const wchar *Src,char *Dest,size_t DestSize)
{
bool RetCode=true;
-#ifdef _WIN_32
- if (WideCharToMultiByte(CP_ACP,0,Src,-1,Dest,DestSize,NULL,NULL)==0)
- RetCode=false;
-#else
-#ifdef _APPLE
- WideToUtf(Src,Dest,DestSize);
-#else
-#ifdef MBFUNCTIONS
+ *Dest=0; // Set 'Dest' to zero just in case the conversion will fail.
- size_t ResultingSize=wcstombs(Dest,Src,DestSize);
- if (ResultingSize==(size_t)-1)
- RetCode=false;
- if (ResultingSize==0 && *Src!=0)
+#ifdef _WIN_ALL
+ if (WideCharToMultiByte(CP_ACP,0,Src,-1,Dest,(int)DestSize,NULL,NULL)==0)
RetCode=false;
- if ((!RetCode || *Dest==0 && *Src!=0) && DestSize>NM && strlenw(Src)<NM)
+// wcstombs is broken in Android NDK r9.
+#elif defined(_APPLE)
+ WideToUtf(Src,Dest,DestSize);
+
+#elif defined(MBFUNCTIONS)
+ if (!WideToCharMap(Src,Dest,DestSize,RetCode))
{
- /* Workaround for strange Linux Unicode functions bug.
- Some of wcstombs and mbstowcs implementations in some situations
- (we are yet to find out what it depends on) can return an empty
- string and success code if buffer size value is too large.
- */
- return(WideToChar(Src,Dest,NM));
- }
+ mbstate_t ps; // Use thread safe external state based functions.
+ memset (&ps, 0, sizeof(ps));
+ const wchar *SrcParam=Src; // wcsrtombs can change the pointer.
+ // Some implementations of wcsrtombs can cause memory analyzing tools
+ // like valgrind to report uninitialized data access. It happens because
+ // internally these implementations call SSE4 based wcslen function,
+ // which reads 16 bytes at once including those beyond of trailing 0.
+ size_t ResultingSize=wcsrtombs(Dest,&SrcParam,DestSize,&ps);
+
+ if (ResultingSize==(size_t)-1 && errno==EILSEQ)
+ {
+ // Aborted on inconvertible character not zero terminating the result.
+ // EILSEQ helps to distinguish it from small output buffer abort.
+ // We want to convert as much as we can, so we clean the output buffer
+ // and repeat conversion.
+ memset (&ps, 0, sizeof(ps));
+ SrcParam=Src; // wcsrtombs can change the pointer.
+ memset(Dest,0,DestSize);
+ ResultingSize=wcsrtombs(Dest,&SrcParam,DestSize,&ps);
+ }
+
+ if (ResultingSize==(size_t)-1)
+ RetCode=false;
+ if (ResultingSize==0 && *Src!=0)
+ RetCode=false;
+ }
#else
- if (UnicodeEnabled())
+ for (int I=0;I<DestSize;I++)
{
-#if defined(_EMX) && !defined(_DJGPP)
- int len=Min(strlenw(Src)+1,DestSize-1);
- if (uni_fromucs((UniChar*)Src,len,Dest,(size_t*)&DestSize)==-1 ||
- DestSize>len*2)
- RetCode=false;
- Dest[DestSize]=0;
-#endif
+ Dest[I]=(char)Src[I];
+ if (Src[I]==0)
+ break;
}
- else
- for (int I=0;I<DestSize;I++)
- {
- Dest[I]=(char)Src[I];
- if (Src[I]==0)
- break;
- }
-#endif
#endif
-#endif
- return(RetCode);
+ if (DestSize>0)
+ Dest[DestSize-1]=0;
+
+ // We tried to return the empty string if conversion is failed,
+ // but it does not work well. WideCharToMultiByte returns 'failed' code
+ // and partially converted string even if we wanted to convert only a part
+ // of string and passed DestSize smaller than required for fully converted
+ // string. Such call is the valid behavior in RAR code and we do not expect
+ // the empty string in this case.
+
+ return RetCode;
}
-bool CharToWide(const char *Src,wchar *Dest,int DestSize)
+bool CharToWide(const char *Src,wchar *Dest,size_t DestSize)
{
bool RetCode=true;
-#ifdef _WIN_32
- if (MultiByteToWideChar(CP_ACP,0,Src,-1,Dest,DestSize)==0)
+ *Dest=0; // Set 'Dest' to zero just in case the conversion will fail.
+
+#ifdef _WIN_ALL
+ if (MultiByteToWideChar(CP_ACP,0,Src,-1,Dest,(int)DestSize)==0)
RetCode=false;
-#else
-#ifdef _APPLE
+
+// mbstowcs is broken in Android NDK r9.
+#elif defined(_APPLE)
UtfToWide(Src,Dest,DestSize);
-#else
-#ifdef MBFUNCTIONS
- size_t ResultingSize=mbstowcs(Dest,Src,DestSize);
+#elif defined(MBFUNCTIONS)
+ mbstate_t ps;
+ memset (&ps, 0, sizeof(ps));
+ const char *SrcParam=Src; // mbsrtowcs can change the pointer.
+ size_t ResultingSize=mbsrtowcs(Dest,&SrcParam,DestSize,&ps);
if (ResultingSize==(size_t)-1)
RetCode=false;
if (ResultingSize==0 && *Src!=0)
RetCode=false;
- if ((!RetCode || *Dest==0 && *Src!=0) && DestSize>NM && strlen(Src)<NM)
- {
- /* Workaround for strange Linux Unicode functions bug.
- Some of wcstombs and mbstowcs implementations in some situations
- (we are yet to find out what it depends on) can return an empty
- string and success code if buffer size value is too large.
- */
- return(CharToWide(Src,Dest,NM));
- }
+ if (RetCode==false && DestSize>1)
+ CharToWideMap(Src,Dest,DestSize,RetCode);
#else
- if (UnicodeEnabled())
+ for (int I=0;I<DestSize;I++)
{
-#if defined(_EMX) && !defined(_DJGPP)
- int len=Min(strlen(Src)+1,DestSize-1);
- if (uni_toucs((char*)Src,len,(UniChar*)Dest,(size_t*)&DestSize)==-1 ||
- DestSize>len)
- DestSize=0;
- RetCode=false;
+ Dest[I]=(wchar_t)Src[I];
+ if (Src[I]==0)
+ break;
+ }
#endif
+ if (DestSize>0)
+ Dest[DestSize-1]=0;
+
+ // We tried to return the empty string if conversion is failed,
+ // but it does not work well. MultiByteToWideChar returns 'failed' code
+ // even if we wanted to convert only a part of string and passed DestSize
+ // smaller than required for fully converted string. Such call is the valid
+ // behavior in RAR code and we do not expect the empty string in this case.
+
+ return RetCode;
+}
+
+
+#if defined(_UNIX) && defined(MBFUNCTIONS)
+// Convert and restore mapped inconvertible Unicode characters.
+// We use it for extended ASCII names in Unix.
+bool WideToCharMap(const wchar *Src,char *Dest,size_t DestSize,bool &Success)
+{
+ // String with inconvertible characters mapped to private use Unicode area
+ // must have the mark code somewhere.
+ if (wcschr(Src,(wchar)MappedStringMark)==NULL)
+ return false;
+
+ // Seems to be that wcrtomb in some memory analyzing libraries
+ // can produce uninitilized output while reporting success on garbage input.
+ // So we clean the destination to calm analyzers.
+ memset(Dest,0,DestSize);
+
+ Success=true;
+ uint SrcPos=0,DestPos=0;
+ while (Src[SrcPos]!=0 && DestPos<DestSize-MB_CUR_MAX)
+ {
+ if (uint(Src[SrcPos])==MappedStringMark)
+ {
+ SrcPos++;
+ continue;
+ }
+ // For security reasons do not restore low ASCII codes, so mapping cannot
+ // be used to hide control codes like path separators.
+ if (uint(Src[SrcPos])>=MapAreaStart+0x80 && uint(Src[SrcPos])<MapAreaStart+0x100)
+ Dest[DestPos++]=char(uint(Src[SrcPos++])-MapAreaStart);
+ else
+ {
+ mbstate_t ps;
+ memset(&ps,0,sizeof(ps));
+ if (wcrtomb(Dest+DestPos,Src[SrcPos],&ps)==(size_t)-1)
+ {
+ Dest[DestPos]='_';
+ Success=false;
+ }
+ SrcPos++;
+ memset(&ps,0,sizeof(ps));
+ int Length=mbrlen(Dest+DestPos,MB_CUR_MAX,&ps);
+ DestPos+=Max(Length,1);
+ }
}
- else
- for (int I=0;I<DestSize;I++)
+ Dest[Min(DestPos,DestSize-1)]=0;
+ return true;
+}
+#endif
+
+
+#if defined(_UNIX) && defined(MBFUNCTIONS)
+// Convert and map inconvertible Unicode characters.
+// We use it for extended ASCII names in Unix.
+void CharToWideMap(const char *Src,wchar *Dest,size_t DestSize,bool &Success)
+{
+ // Map inconvertible characters to private use Unicode area 0xE000.
+ // Mark such string by placing special non-character code before
+ // first inconvertible character.
+ Success=false;
+ bool MarkAdded=false;
+ uint SrcPos=0,DestPos=0;
+ while (DestPos<DestSize)
+ {
+ if (Src[SrcPos]==0)
{
- Dest[I]=(wchar_t)Src[I];
- if (Src[I]==0)
+ Success=true;
+ break;
+ }
+ mbstate_t ps;
+ memset(&ps,0,sizeof(ps));
+ size_t res=mbrtowc(Dest+DestPos,Src+SrcPos,MB_CUR_MAX,&ps);
+ if (res==(size_t)-1 || res==(size_t)-2)
+ {
+ // For security reasons we do not want to map low ASCII characters,
+ // so we do not have additional .. and path separator codes.
+ if (byte(Src[SrcPos])>=0x80)
+ {
+ if (!MarkAdded)
+ {
+ Dest[DestPos++]=MappedStringMark;
+ MarkAdded=true;
+ if (DestPos>=DestSize)
+ break;
+ }
+ Dest[DestPos++]=byte(Src[SrcPos++])+MapAreaStart;
+ }
+ else
break;
}
-#endif
-#endif
-#endif
- return(RetCode);
+ else
+ {
+ memset(&ps,0,sizeof(ps));
+ int Length=mbrlen(Src+SrcPos,MB_CUR_MAX,&ps);
+ SrcPos+=Max(Length,1);
+ DestPos++;
+ }
+ }
+ Dest[Min(DestPos,DestSize-1)]=0;
}
+#endif
-byte* WideToRaw(const wchar *Src,byte *Dest,int DestSize)
+// SrcSize is source data size in wide characters, not in bytes.
+// DestSize is the maximum allowed destination size.
+byte* WideToRaw(const wchar *Src,size_t SrcSize,byte *Dest,size_t DestSize)
{
- for (int I=0;I<DestSize;I++,Src++)
+ for (size_t I=0;I<SrcSize && I*2+1<DestSize;I++,Src++)
{
Dest[I*2]=(byte)*Src;
Dest[I*2+1]=(byte)(*Src>>8);
if (*Src==0)
break;
}
- return(Dest);
+ return Dest;
}
-wchar* RawToWide(const byte *Src,wchar *Dest,int DestSize)
+wchar* RawToWide(const byte *Src,wchar *Dest,size_t DestSize)
{
- for (int I=0;I<DestSize;I++)
+ for (size_t I=0;I<DestSize;I++)
if ((Dest[I]=Src[I*2]+(Src[I*2+1]<<8))==0)
break;
- return(Dest);
+ return Dest;
}
-void WideToUtf(const wchar *Src,char *Dest,int DestSize)
+void WideToUtf(const wchar *Src,char *Dest,size_t DestSize)
{
- DestSize--;
- while (*Src!=0 && --DestSize>=0)
+ long dsize=(long)DestSize;
+ dsize--;
+ while (*Src!=0 && --dsize>=0)
{
uint c=*(Src++);
if (c<0x80)
*(Dest++)=c;
else
- if (c<0x800 && --DestSize>=0)
+ if (c<0x800 && --dsize>=0)
{
*(Dest++)=(0xc0|(c>>6));
*(Dest++)=(0x80|(c&0x3f));
}
else
- if (c<0x10000 && (DestSize-=2)>=0)
+ {
+ if (c>=0xd800 && c<=0xdbff && *Src>=0xdc00 && *Src<=0xdfff) // Surrogate pair.
+ {
+ c=((c-0xd800)<<10)+(*Src-0xdc00)+0x10000;
+ Src++;
+ }
+ if (c<0x10000 && (dsize-=2)>=0)
{
*(Dest++)=(0xe0|(c>>12));
*(Dest++)=(0x80|((c>>6)&0x3f));
*(Dest++)=(0x80|(c&0x3f));
}
else
- if (c < 0x200000 && (DestSize-=3)>=0)
+ if (c < 0x200000 && (dsize-=3)>=0)
{
*(Dest++)=(0xf0|(c>>18));
*(Dest++)=(0x80|((c>>12)&0x3f));
*(Dest++)=(0x80|((c>>6)&0x3f));
*(Dest++)=(0x80|(c&0x3f));
}
+ }
}
*Dest=0;
}
-void UtfToWide(const char *Src,wchar *Dest,int DestSize)
+size_t WideToUtfSize(const wchar *Src)
+{
+ size_t Size=0;
+ for (;*Src!=0;Src++)
+ if (*Src<0x80)
+ Size++;
+ else
+ if (*Src<0x800)
+ Size+=2;
+ else
+ if ((uint)*Src<0x10000) //(uint) to avoid Clang/win "always true" warning for 16-bit wchar_t.
+ {
+ if (Src[0]>=0xd800 && Src[0]<=0xdbff && Src[1]>=0xdc00 && Src[1]<=0xdfff)
+ {
+ Size+=4; // 4 output bytes for Unicode surrogate pair.
+ Src++;
+ }
+ else
+ Size+=3;
+ }
+ else
+ if ((uint)*Src<0x200000) //(uint) to avoid Clang/win "always true" warning for 16-bit wchar_t.
+ Size+=4;
+ return Size+1; // Include terminating zero.
+}
+
+
+bool UtfToWide(const char *Src,wchar *Dest,size_t DestSize)
{
- DestSize--;
+ bool Success=true;
+ long dsize=(long)DestSize;
+ dsize--;
while (*Src!=0)
{
- uint c=(byte)*(Src++),d;
+ uint c=byte(*(Src++)),d;
if (c<0x80)
d=c;
else
if ((c>>5)==6)
{
if ((*Src&0xc0)!=0x80)
+ {
+ Success=false;
break;
+ }
d=((c&0x1f)<<6)|(*Src&0x3f);
Src++;
}
@@ -185,7 +347,10 @@ void UtfToWide(const char *Src,wchar *Dest,int DestSize)
if ((c>>4)==14)
{
if ((Src[0]&0xc0)!=0x80 || (Src[1]&0xc0)!=0x80)
+ {
+ Success=false;
break;
+ }
d=((c&0xf)<<12)|((Src[0]&0x3f)<<6)|(Src[1]&0x3f);
Src+=2;
}
@@ -193,244 +358,221 @@ void UtfToWide(const char *Src,wchar *Dest,int DestSize)
if ((c>>3)==30)
{
if ((Src[0]&0xc0)!=0x80 || (Src[1]&0xc0)!=0x80 || (Src[2]&0xc0)!=0x80)
+ {
+ Success=false;
break;
+ }
d=((c&7)<<18)|((Src[0]&0x3f)<<12)|((Src[1]&0x3f)<<6)|(Src[2]&0x3f);
Src+=3;
}
else
+ {
+ Success=false;
break;
- if (--DestSize<0)
+ }
+ if (--dsize<0)
break;
if (d>0xffff)
{
- if (--DestSize<0 || d>0x10ffff)
+ if (--dsize<0)
break;
- *(Dest++)=((d-0x10000)>>10)+0xd800;
- *(Dest++)=(d&0x3ff)+0xdc00;
+ if (d>0x10ffff) // UTF-8 must end at 0x10ffff according to RFC 3629.
+ {
+ Success=false;
+ continue;
+ }
+ if (sizeof(*Dest)==2) // Use the surrogate pair.
+ {
+ *(Dest++)=((d-0x10000)>>10)+0xd800;
+ *(Dest++)=(d&0x3ff)+0xdc00;
+ }
+ else
+ *(Dest++)=d;
}
else
*(Dest++)=d;
}
*Dest=0;
+ return Success;
}
-bool UnicodeEnabled()
-{
-#ifdef UNICODE_SUPPORTED
- #ifdef _EMX
- return(uni_ready);
- #else
- return(true);
- #endif
-#else
- return(false);
-#endif
-}
-
-
-int strlenw(const wchar *str)
+// For zero terminated strings.
+bool IsTextUtf8(const byte *Src)
{
- int length=0;
- while (*(str++)!=0)
- length++;
- return(length);
+ return IsTextUtf8(Src,strlen((const char *)Src));
}
-wchar* strcpyw(wchar *dest,const wchar *src)
+// Source data can be both with and without UTF-8 BOM.
+bool IsTextUtf8(const byte *Src,size_t SrcSize)
{
- do {
- *(dest++)=*src;
- } while (*(src++)!=0);
- return(dest);
-}
-
-
-wchar* strncpyw(wchar *dest,const wchar *src,size_t n)
-{
- do {
- *(dest++)=*src;
- } while (*(src++)!=0 && (int)(--n) > 0);
- return(dest);
-}
-
-
-wchar* strcatw(wchar *dest,const wchar *src)
-{
- return(strcpyw(dest+strlenw(dest),src));
+ while (SrcSize-- > 0)
+ {
+ byte C=*(Src++);
+ int HighOne=0; // Number of leftmost '1' bits.
+ for (byte Mask=0x80;Mask!=0 && (C & Mask)!=0;Mask>>=1)
+ HighOne++;
+ if (HighOne==1 || HighOne>6)
+ return false;
+ while (--HighOne > 0)
+ if (SrcSize-- <= 0 || (*(Src++) & 0xc0)!=0x80)
+ return false;
+ }
+ return true;
}
-#ifndef SFX_MODULE
-wchar* strncatw(wchar *dest,const wchar *src,size_t n)
+int wcsicomp(const wchar *s1,const wchar *s2)
{
- dest+=strlenw(dest);
+#ifdef _WIN_ALL
+ return CompareStringW(LOCALE_USER_DEFAULT,NORM_IGNORECASE|SORT_STRINGSORT,s1,-1,s2,-1)-2;
+#else
while (true)
- if ((int)(--n)<0)
- {
- *dest=0;
- break;
- }
- else
- if ((*(dest++)=*(src++))==0)
- break;
- return(dest);
-}
-#endif
-
-
-int strcmpw(const wchar *s1,const wchar *s2)
-{
- while (*s1==*s2)
{
+ wchar u1 = towupper(*s1);
+ wchar u2 = towupper(*s2);
+ if (u1 != u2)
+ return u1 < u2 ? -1 : 1;
if (*s1==0)
- return(0);
+ break;
s1++;
s2++;
}
- return(*s1<*s2 ? -1:1);
+ return 0;
+#endif
}
-int strncmpw(const wchar *s1,const wchar *s2,size_t n)
+int wcsnicomp(const wchar *s1,const wchar *s2,size_t n)
{
- while ((int)(n--)>0)
+#ifdef _WIN_ALL
+ // If we specify 'n' exceeding the actual string length, CompareString goes
+ // beyond the trailing zero and compares garbage. So we need to limit 'n'
+ // to real string length.
+ size_t l1=Min(wcslen(s1)+1,n);
+ size_t l2=Min(wcslen(s2)+1,n);
+ return CompareStringW(LOCALE_USER_DEFAULT,NORM_IGNORECASE|SORT_STRINGSORT,s1,(int)l1,s2,(int)l2)-2;
+#else
+ if (n==0)
+ return 0;
+ while (true)
{
- if (*s1<*s2)
- return(-1);
- if (*s1>*s2)
- return(-1);
- if (*s1==0)
+ wchar u1 = towupper(*s1);
+ wchar u2 = towupper(*s2);
+ if (u1 != u2)
+ return u1 < u2 ? -1 : 1;
+ if (*s1==0 || --n==0)
break;
s1++;
s2++;
}
- return(0);
+ return 0;
+#endif
}
-#ifndef SFX_MODULE
-int stricmpw(const wchar *s1,const wchar *s2)
+// Case insensitive wcsstr().
+const wchar_t* wcscasestr(const wchar_t *str, const wchar_t *search)
{
- char Ansi1[NM*sizeof(wchar)],Ansi2[NM*sizeof(wchar)];
- WideToChar(s1,Ansi1,sizeof(Ansi1));
- WideToChar(s2,Ansi2,sizeof(Ansi2));
- return(stricomp(Ansi1,Ansi2));
+ for (size_t i=0;str[i]!=0;i++)
+ for (size_t j=0;;j++)
+ {
+ if (search[j]==0)
+ return str+i;
+ if (tolowerw(str[i+j])!=tolowerw(search[j]))
+ break;
+ }
+ return NULL;
}
-#endif
-#if !defined(SFX_MODULE) && !defined(_WIN_CE)
-inline int strnicmpw_w2c(const wchar *s1,const wchar *s2,size_t n)
+#ifndef SFX_MODULE
+wchar* wcslower(wchar *s)
{
- wchar Wide1[NM*2],Wide2[NM*2];
- strncpyw(Wide1,s1,sizeof(Wide1)/sizeof(Wide1[0])-1);
- strncpyw(Wide2,s2,sizeof(Wide2)/sizeof(Wide2[0])-1);
- Wide1[Min(sizeof(Wide1)/sizeof(Wide1[0])-1,n)]=0;
- Wide2[Min(sizeof(Wide2)/sizeof(Wide2[0])-1,n)]=0;
- char Ansi1[NM*2],Ansi2[NM*2];
- WideToChar(Wide1,Ansi1,sizeof(Ansi1));
- WideToChar(Wide2,Ansi2,sizeof(Ansi2));
- return(stricomp(Ansi1,Ansi2));
+#ifdef _WIN_ALL
+ // _wcslwr requires setlocale and we do not want to depend on setlocale
+ // in Windows. Also CharLower involves less overhead.
+ CharLower(s);
+#else
+ for (wchar *c=s;*c!=0;c++)
+ *c=towlower(*c);
+#endif
+ return s;
}
#endif
#ifndef SFX_MODULE
-int strnicmpw(const wchar *s1,const wchar *s2,size_t n)
+wchar* wcsupper(wchar *s)
{
- return(strnicmpw_w2c(s1,s2,n));
-}
+#ifdef _WIN_ALL
+ // _wcsupr requires setlocale and we do not want to depend on setlocale
+ // in Windows. Also CharUpper involves less overhead.
+ CharUpper(s);
+#else
+ for (wchar *c=s;*c!=0;c++)
+ *c=towupper(*c);
#endif
-
-
-wchar* strchrw(const wchar *s,int c)
-{
- while (*s)
- {
- if (*s==c)
- return((wchar *)s);
- s++;
- }
- return(NULL);
-}
-
-
-wchar* strrchrw(const wchar *s,int c)
-{
- for (int I=strlenw(s)-1;I>=0;I--)
- if (s[I]==c)
- return((wchar *)(s+I));
- return(NULL);
+ return s;
}
+#endif
-wchar* strpbrkw(const wchar *s1,const wchar *s2)
-{
- while (*s1)
- {
- if (strchrw(s2,*s1)!=NULL)
- return((wchar *)s1);
- s1++;
- }
- return(NULL);
-}
-#ifndef SFX_MODULE
-wchar* strlowerw(wchar *Str)
+int toupperw(int ch)
{
- for (wchar *ChPtr=Str;*ChPtr;ChPtr++)
- if (*ChPtr<128)
- *ChPtr=loctolower(*ChPtr);
- return(Str);
-}
+#if defined(_WIN_ALL)
+ // CharUpper is more reliable than towupper in Windows, which seems to be
+ // C locale dependent even in Unicode version. For example, towupper failed
+ // to convert lowercase Russian characters. Use 0xffff mask to prevent crash
+ // if value larger than 0xffff is passed to this function.
+ return (int)(INT_PTR)CharUpper((wchar *)(INT_PTR)(ch&0xffff));
+#else
+ return towupper(ch);
#endif
-
-
-#ifndef SFX_MODULE
-wchar* strupperw(wchar *Str)
-{
- for (wchar *ChPtr=Str;*ChPtr;ChPtr++)
- if (*ChPtr<128)
- *ChPtr=loctoupper(*ChPtr);
- return(Str);
}
-#endif
-#ifndef SFX_MODULE
-wchar* strdupw(const wchar *Str)
+int tolowerw(int ch)
{
- if (Str==NULL)
- return(NULL);
- wchar *n=(wchar *)malloc((strlenw(Str)+1)*sizeof(wchar));
- if (n==NULL)
- return(NULL);
- strcpyw(n,Str);
- return(n);
-}
+#if defined(_WIN_ALL)
+ // CharLower is more reliable than towlower in Windows.
+ // See comment for towupper above. Use 0xffff mask to prevent crash
+ // if value larger than 0xffff is passed to this function.
+ return (int)(INT_PTR)CharLower((wchar *)(INT_PTR)(ch&0xffff));
+#else
+ return towlower(ch);
#endif
+}
-#ifndef SFX_MODULE
-int toupperw(int ch)
+int atoiw(const wchar *s)
{
- return((ch<128) ? loctoupper(ch):ch);
+ return (int)atoilw(s);
}
-#endif
-int atoiw(const wchar *s)
+int64 atoilw(const wchar *s)
{
- int n=0;
+ bool sign=false;
+ if (*s=='-') // We do use signed integers here, for example, in GUI SFX.
+ {
+ s++;
+ sign=true;
+ }
+ // Use unsigned type here, since long string can overflow the variable
+ // and signed integer overflow is undefined behavior in C++.
+ uint64 n=0;
while (*s>='0' && *s<='9')
{
n=n*10+(*s-'0');
s++;
}
- return(n);
+ // Check int64(n)>=0 to avoid the signed overflow with undefined behavior
+ // when negating 0x8000000000000000.
+ return sign && int64(n)>=0 ? -int64(n) : int64(n);
}
@@ -448,66 +590,18 @@ void SupportDBCS::Init()
CPINFO CPInfo;
GetCPInfo(CP_ACP,&CPInfo);
DBCSMode=CPInfo.MaxCharSize > 1;
- for (int I=0;I<sizeof(IsLeadByte)/sizeof(IsLeadByte[0]);I++)
- IsLeadByte[I]=IsDBCSLeadByte(I);
+ for (uint I=0;I<ASIZE(IsLeadByte);I++)
+ IsLeadByte[I]=IsDBCSLeadByte(I)!=0;
}
char* SupportDBCS::charnext(const char *s)
{
- return (char *)(IsLeadByte[*s] ? s+2:s+1);
-}
-
-
-uint SupportDBCS::strlend(const char *s)
-{
- uint Length=0;
- while (*s!=0)
- {
- if (IsLeadByte[*s])
- s+=2;
- else
- s++;
- Length++;
- }
- return(Length);
-}
-
-
-char* SupportDBCS::strchrd(const char *s, int c)
-{
- while (*s!=0)
- if (IsLeadByte[*s])
- s+=2;
- else
- if (*s==c)
- return((char *)s);
- else
- s++;
- return(NULL);
-}
-
-
-void SupportDBCS::copychrd(char *dest,const char *src)
-{
- dest[0]=src[0];
- if (IsLeadByte[src[0]])
- dest[1]=src[1];
+ // Zero cannot be the trail byte. So if next byte after the lead byte
+ // is 0, the string is corrupt and we'll better return the pointer to 0,
+ // to break string processing loops.
+ return (char *)(IsLeadByte[(byte)*s] && s[1]!=0 ? s+2:s+1);
}
+#endif
-char* SupportDBCS::strrchrd(const char *s, int c)
-{
- const char *found=NULL;
- while (*s!=0)
- if (IsLeadByte[*s])
- s+=2;
- else
- {
- if (*s==c)
- found=s;
- s++;
- }
- return((char *)found);
-}
-#endif
diff --git a/unrar/unrar/unicode.hpp b/unrar/unrar/unicode.hpp
index 3b4e2c3..9bfd9c5 100644
--- a/unrar/unrar/unicode.hpp
+++ b/unrar/unrar/unicode.hpp
@@ -1,48 +1,31 @@
#ifndef _RAR_UNICODE_
#define _RAR_UNICODE_
-#ifndef _EMX
-#define MBFUNCTIONS
-#endif
-
-#if defined(MBFUNCTIONS) || defined(_WIN_32) || defined(_EMX) && !defined(_DJGPP)
-#define UNICODE_SUPPORTED
-#endif
-
-#ifdef _WIN_32
+#if defined( _WIN_ALL)
#define DBCS_SUPPORTED
#endif
-#ifdef _EMX
-int uni_init(int codepage);
-int uni_done();
+bool WideToChar(const wchar *Src,char *Dest,size_t DestSize);
+bool CharToWide(const char *Src,wchar *Dest,size_t DestSize);
+byte* WideToRaw(const wchar *Src,size_t SrcSize,byte *Dest,size_t DestSize);
+wchar* RawToWide(const byte *Src,wchar *Dest,size_t DestSize);
+void WideToUtf(const wchar *Src,char *Dest,size_t DestSize);
+size_t WideToUtfSize(const wchar *Src);
+bool UtfToWide(const char *Src,wchar *Dest,size_t DestSize);
+bool IsTextUtf8(const byte *Src);
+bool IsTextUtf8(const byte *Src,size_t SrcSize);
+
+int wcsicomp(const wchar *s1,const wchar *s2);
+int wcsnicomp(const wchar *s1,const wchar *s2,size_t n);
+const wchar_t* wcscasestr(const wchar_t *str, const wchar_t *search);
+#ifndef SFX_MODULE
+wchar* wcslower(wchar *s);
+wchar* wcsupper(wchar *s);
#endif
-
-bool WideToChar(const wchar *Src,char *Dest,int DestSize=0x1000000);
-bool CharToWide(const char *Src,wchar *Dest,int DestSize=0x1000000);
-byte* WideToRaw(const wchar *Src,byte *Dest,int DestSize=0x1000000);
-wchar* RawToWide(const byte *Src,wchar *Dest,int DestSize=0x1000000);
-void WideToUtf(const wchar *Src,char *Dest,int DestSize);
-void UtfToWide(const char *Src,wchar *Dest,int DestSize);
-bool UnicodeEnabled();
-
-int strlenw(const wchar *str);
-wchar* strcpyw(wchar *dest,const wchar *src);
-wchar* strncpyw(wchar *dest,const wchar *src,size_t n);
-wchar* strcatw(wchar *dest,const wchar *src);
-wchar* strncatw(wchar *dest,const wchar *src,size_t n);
-int strcmpw(const wchar *s1,const wchar *s2);
-int strncmpw(const wchar *s1,const wchar *s2,size_t n);
-int stricmpw(const wchar *s1,const wchar *s2);
-int strnicmpw(const wchar *s1,const wchar *s2,size_t n);
-wchar *strchrw(const wchar *s,int c);
-wchar* strrchrw(const wchar *s,int c);
-wchar* strpbrkw(const wchar *s1,const wchar *s2);
-wchar* strlowerw(wchar *Str);
-wchar* strupperw(wchar *Str);
-wchar* strdupw(const wchar *Str);
int toupperw(int ch);
+int tolowerw(int ch);
int atoiw(const wchar *s);
+int64 atoilw(const wchar *s);
#ifdef DBCS_SUPPORTED
class SupportDBCS
@@ -50,34 +33,20 @@ class SupportDBCS
public:
SupportDBCS();
void Init();
-
char* charnext(const char *s);
- uint strlend(const char *s);
- char *strchrd(const char *s, int c);
- char *strrchrd(const char *s, int c);
- void copychrd(char *dest,const char *src);
bool IsLeadByte[256];
bool DBCSMode;
};
-
extern SupportDBCS gdbcs;
inline char* charnext(const char *s) {return (char *)(gdbcs.DBCSMode ? gdbcs.charnext(s):s+1);}
-inline uint strlend(const char *s) {return (uint)(gdbcs.DBCSMode ? gdbcs.strlend(s):strlen(s));}
-inline char* strchrd(const char *s, int c) {return (char *)(gdbcs.DBCSMode ? gdbcs.strchrd(s,c):strchr(s,c));}
-inline char* strrchrd(const char *s, int c) {return (char *)(gdbcs.DBCSMode ? gdbcs.strrchrd(s,c):strrchr(s,c));}
-inline void copychrd(char *dest,const char *src) {if (gdbcs.DBCSMode) gdbcs.copychrd(dest,src); else *dest=*src;}
-inline bool IsDBCSMode() {return(gdbcs.DBCSMode);}
-inline void InitDBCS() {gdbcs.Init();}
+inline bool IsDBCSMode() {return gdbcs.DBCSMode;}
#else
#define charnext(s) ((s)+1)
-#define strlend strlen
-#define strchrd strchr
-#define strrchrd strrchr
-#define IsDBCSMode() (true)
-inline void copychrd(char *dest,const char *src) {*dest=*src;}
+#define IsDBCSMode() (false)
#endif
+
#endif
diff --git a/unrar/unrar/unios2.cpp b/unrar/unrar/unios2.cpp
deleted file mode 100644
index 1261473..0000000
--- a/unrar/unrar/unios2.cpp
+++ /dev/null
@@ -1,128 +0,0 @@
-// (c) 2001,2004 by Max Alekseyev
-// ver. 2.1
-
-#include <stddef.h>
-
-#define INCL_DOSMODULEMGR
-#include <os2.h>
-
-typedef void* UconvObject;
-typedef unsigned short UniChar;
-
-int uni_init(int codepage);
-
-int uni_done();
-
-int uni_toucs( /* translate to Unicode */
- char*, /* I - input string */
- size_t, /* I - length of input string (chars) */
- UniChar*, /* O - output Unicode string */
- size_t* ); /* O - length of output string (UniChars) */
-
-int uni_fromucs( /* translate from Unicode */
- UniChar*, /* I - input Unicode string */
- size_t, /* I - length of input string (UniChars) */
- char*, /* O - output string */
- size_t* ); /* O - length of output string (chars) */
-
-/* IMPLEMENTATION */
-
-static int (*uniMapCpToUcsCp) (
- unsigned long, /* I - Codepage to convert */
- UniChar*, /* O - Output buffer */
- size_t ); /* I - UniChars in output buffer */
-
-static int (*uniCreateUconvObject) (
- UniChar*, /* I - Unicode name of uconv table */
- UconvObject* );/* O - Uconv object handle */
-
-static int (*uniFreeUconvObject) (
- UconvObject ); /* I - Uconv object handle */
-
-static int (*uniUconvToUcs) (
- UconvObject, /* I - Uconv object handle */
- void**, /* IO - Input buffer */
- size_t*, /* IO - Input buffer size (bytes) */
- UniChar**, /* IO - Output buffer size */
- size_t*, /* IO - Output size (chars) */
- size_t* ); /* IO - Substitution count */
-
-static int (*uniUconvFromUcs) (
- UconvObject, /* I - Uconv object handle */
- UniChar**, /* IO - Input buffer */
- size_t*, /* IO - Input buffer size (bytes) */
- void**, /* IO - Output buffer size */
- size_t*, /* IO - Output size (chars) */
- size_t* ); /* IO - Substitution count */
-
-static int uni_ready = 0;
-static HMODULE uni_UCONV;
-static UconvObject uni_obj;
-
-int uni_init(int codepage) {
- UniChar unistr[256];
-
- uni_ready = 0;
-
- if(!&DosLoadModule) {
- /* DOS enviroment detected */
- return -1;
- }
-
- if( DosLoadModule(0,0,(PCSZ)"UCONV",&uni_UCONV) ) {
- /* no Unicode API found (obsolete OS/2 version) */
- return -2;
- }
-
- if( !DosQueryProcAddr(uni_UCONV,0,(PCSZ)"UniMapCpToUcsCp", (PPFN)&uniMapCpToUcsCp ) &&
- !DosQueryProcAddr(uni_UCONV,0,(PCSZ)"UniUconvToUcs", (PPFN)&uniUconvToUcs ) &&
- !DosQueryProcAddr(uni_UCONV,0,(PCSZ)"UniUconvFromUcs", (PPFN)&uniUconvFromUcs ) &&
- !DosQueryProcAddr(uni_UCONV,0,(PCSZ)"UniCreateUconvObject",(PPFN)&uniCreateUconvObject) &&
- !DosQueryProcAddr(uni_UCONV,0,(PCSZ)"UniFreeUconvObject", (PPFN)&uniFreeUconvObject )
- ) {
- unistr[0] = 0;
- if( (!codepage || !uniMapCpToUcsCp(codepage, unistr, 256)) && !uniCreateUconvObject(unistr,&uni_obj) ) {
- uni_ready = 1;
- return 0;
- }
- }
- DosFreeModule(uni_UCONV);
- return -2;
-}
-
-int uni_toucs(char* src, size_t srclen, UniChar* dst, size_t* dstlen) {
- size_t srcbytes, srcsize, dstsize, subsc=0;
-
- if(!uni_ready) return -1;
-
- dstsize = srcbytes = srclen * sizeof(UniChar);
-
- if( uniUconvToUcs(uni_obj,(void**)&src,&srclen,&dst,&dstsize,&subsc) ) {
- return -1;
- }
- *dstlen = srcbytes - dstsize;
- return 0;
-}
-
-int uni_fromucs(UniChar* src, size_t srclen, char* dst, size_t* dstlen) {
- size_t srcbytes, srcsize, dstsize, subsc=0;
-
- if(!uni_ready) return -1;
-
- dstsize = srcbytes = *dstlen;
-
- if( uniUconvFromUcs(uni_obj,&src,&srclen,(void**)&dst,&dstsize,&subsc) ) {
- return -1;
- }
- *dstlen = srcbytes - dstsize;
- return 0;
-}
-
-int uni_done() {
- if( uni_ready ) {
- uniFreeUconvObject(uni_obj);
- DosFreeModule(uni_UCONV);
- uni_ready = 0;
- }
- return 0;
-}
diff --git a/unrar/unrar/unpack.cpp b/unrar/unrar/unpack.cpp
index 3e26153..9236e74 100644
--- a/unrar/unrar/unpack.cpp
+++ b/unrar/unrar/unpack.cpp
@@ -3,1007 +3,363 @@
#include "coder.cpp"
#include "suballoc.cpp"
#include "model.cpp"
+#include "unpackinline.cpp"
+#ifdef RAR_SMP
+#include "unpack50mt.cpp"
+#endif
#ifndef SFX_MODULE
#include "unpack15.cpp"
#include "unpack20.cpp"
#endif
+#include "unpack30.cpp"
+#include "unpack50.cpp"
+#include "unpack50frag.cpp"
Unpack::Unpack(ComprDataIO *DataIO)
+:Inp(true),VMCodeInp(true)
{
UnpIO=DataIO;
Window=NULL;
- ExternalWindow=false;
+ Fragmented=false;
Suspended=false;
UnpAllBuf=false;
UnpSomeRead=false;
-}
-
-
-Unpack::~Unpack()
-{
- if (Window!=NULL && !ExternalWindow)
- delete[] Window;
- InitFilters();
-}
-
-
-void Unpack::Init(byte *Window)
-{
- if (Window==NULL)
- {
- Unpack::Window=new byte[MAXWINSIZE];
-#ifndef ALLOW_EXCEPTIONS
- if (Unpack::Window==NULL)
- ErrHandler.MemoryError();
+#ifdef RAR_SMP
+ MaxUserThreads=1;
+ UnpThreadPool=NULL;
+ ReadBufMT=NULL;
+ UnpThreadData=NULL;
#endif
- }
- else
- {
- Unpack::Window=Window;
- ExternalWindow=true;
- }
- UnpInitData(false);
+ MaxWinSize=0;
+ MaxWinMask=0;
+ // Perform initialization, which should be done only once for all files.
+ // It prevents crash if first DoUnpack call is later made with wrong
+ // (true) 'Solid' value.
+ UnpInitData(false);
#ifndef SFX_MODULE
// RAR 1.5 decompression initialization
- OldUnpInitData(false);
+ UnpInitData15(false);
InitHuff();
#endif
}
-void Unpack::DoUnpack(int Method,bool Solid)
+Unpack::~Unpack()
{
- switch(Method)
- {
-#ifndef SFX_MODULE
- case 15: // rar 1.5 compression
- Unpack15(Solid);
- break;
- case 20: // rar 2.x compression
- case 26: // files larger than 2GB
- Unpack20(Solid);
- break;
+ InitFilters30(false);
+
+ if (Window!=NULL)
+ free(Window);
+#ifdef RAR_SMP
+ delete UnpThreadPool;
+ delete[] ReadBufMT;
+ delete[] UnpThreadData;
#endif
- case 29: // rar 3.x compression
- case 36: // alternative hash
- Unpack29(Solid);
- break;
- }
-}
-
-
-inline void Unpack::InsertOldDist(unsigned int Distance)
-{
- OldDist[3]=OldDist[2];
- OldDist[2]=OldDist[1];
- OldDist[1]=OldDist[0];
- OldDist[0]=Distance;
}
-inline void Unpack::InsertLastMatch(unsigned int Length,unsigned int Distance)
+#ifdef RAR_SMP
+void Unpack::SetThreads(uint Threads)
{
- LastDist=Distance;
- LastLength=Length;
-}
-
-
-void Unpack::CopyString(unsigned int Length,unsigned int Distance)
-{
- unsigned int DestPtr=UnpPtr-Distance;
- if (DestPtr<MAXWINSIZE-260 && UnpPtr<MAXWINSIZE-260)
- {
- Window[UnpPtr++]=Window[DestPtr++];
- while (--Length>0)
- Window[UnpPtr++]=Window[DestPtr++];
- }
- else
- while (Length--)
- {
- Window[UnpPtr]=Window[DestPtr++ & MAXWINMASK];
- UnpPtr=(UnpPtr+1) & MAXWINMASK;
- }
-}
-
-
-int Unpack::DecodeNumber(struct Decode *Dec)
-{
- unsigned int Bits;
- unsigned int BitField=getbits() & 0xfffe;
- if (BitField<Dec->DecodeLen[8])
- if (BitField<Dec->DecodeLen[4])
- if (BitField<Dec->DecodeLen[2])
- if (BitField<Dec->DecodeLen[1])
- Bits=1;
- else
- Bits=2;
- else
- if (BitField<Dec->DecodeLen[3])
- Bits=3;
- else
- Bits=4;
- else
- if (BitField<Dec->DecodeLen[6])
- if (BitField<Dec->DecodeLen[5])
- Bits=5;
- else
- Bits=6;
- else
- if (BitField<Dec->DecodeLen[7])
- Bits=7;
- else
- Bits=8;
- else
- if (BitField<Dec->DecodeLen[12])
- if (BitField<Dec->DecodeLen[10])
- if (BitField<Dec->DecodeLen[9])
- Bits=9;
- else
- Bits=10;
- else
- if (BitField<Dec->DecodeLen[11])
- Bits=11;
- else
- Bits=12;
- else
- if (BitField<Dec->DecodeLen[14])
- if (BitField<Dec->DecodeLen[13])
- Bits=13;
- else
- Bits=14;
- else
- Bits=15;
-
- addbits(Bits);
- unsigned int N=Dec->DecodePos[Bits]+((BitField-Dec->DecodeLen[Bits-1])>>(16-Bits));
- if (N>=Dec->MaxNum)
- N=0;
- return(Dec->DecodeNum[N]);
+ // More than 8 threads are unlikely to provide noticeable gain
+ // for unpacking, but would use the additional memory.
+ MaxUserThreads=Min(Threads,8);
+ UnpThreadPool=new ThreadPool(MaxUserThreads);
}
+#endif
-void Unpack::Unpack29(bool Solid)
+void Unpack::Init(size_t WinSize,bool Solid)
{
- static unsigned char LDecode[]={0,1,2,3,4,5,6,7,8,10,12,14,16,20,24,28,32,40,48,56,64,80,96,112,128,160,192,224};
- static unsigned char LBits[]= {0,0,0,0,0,0,0,0,1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5};
- static int DDecode[DC];
- static byte DBits[DC];
- static int DBitLengthCounts[]= {4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,14,0,12};
- static unsigned char SDDecode[]={0,4,8,16,32,64,128,192};
- static unsigned char SDBits[]= {2,2,3, 4, 5, 6, 6, 6};
- unsigned int Bits;
-
- if (DDecode[1]==0)
- {
- int Dist=0,BitLength=0,Slot=0;
- for (int I=0;I<sizeof(DBitLengthCounts)/sizeof(DBitLengthCounts[0]);I++,BitLength++)
- for (int J=0;J<DBitLengthCounts[I];J++,Slot++,Dist+=(1<<BitLength))
- {
- DDecode[Slot]=Dist;
- DBits[Slot]=BitLength;
- }
- }
+ // If 32-bit RAR unpacks an archive with 4 GB dictionary, the window size
+ // will be 0 because of size_t overflow. Let's issue the memory error.
+ if (WinSize==0)
+ ErrHandler.MemoryError();
+
+ // Minimum window size must be at least twice more than maximum possible
+ // size of filter block, which is 0x10000 in RAR now. If window size is
+ // smaller, we can have a block with never cleared flt->NextWindow flag
+ // in UnpWriteBuf(). Minimum window size 0x20000 would be enough, but let's
+ // use 0x40000 for extra safety and possible filter area size expansion.
+ const size_t MinAllocSize=0x40000;
+ if (WinSize<MinAllocSize)
+ WinSize=MinAllocSize;
+
+ if (WinSize<=MaxWinSize) // Use the already allocated window.
+ return;
+ if ((WinSize>>16)>0x10000) // Window size must not exceed 4 GB.
+ return;
- FileExtracted=true;
+ // Archiving code guarantees that window size does not grow in the same
+ // solid stream. So if we are here, we are either creating a new window
+ // or increasing the size of non-solid window. So we could safely reject
+ // current window data without copying them to a new window, though being
+ // extra cautious, we still handle the solid window grow case below.
+ bool Grow=Solid && (Window!=NULL || Fragmented);
- if (!Suspended)
- {
- UnpInitData(Solid);
- if (!UnpReadBuf())
- return;
- if ((!Solid || !TablesRead) && !ReadTables())
- return;
- }
+ // We do not handle growth for existing fragmented window.
+ if (Grow && Fragmented)
+ throw std::bad_alloc();
- while (true)
- {
- UnpPtr&=MAXWINMASK;
+ byte *NewWindow=Fragmented ? NULL : (byte *)malloc(WinSize);
- if (InAddr>ReadBorder)
+ if (NewWindow==NULL)
+ if (Grow || WinSize<0x1000000)
{
- if (!UnpReadBuf())
- break;
+ // We do not support growth for new fragmented window.
+ // Also exclude RAR4 and small dictionaries.
+ throw std::bad_alloc();
}
- if (((WrPtr-UnpPtr) & MAXWINMASK)<260 && WrPtr!=UnpPtr)
+ else
{
- UnpWriteBuf();
- if (WrittenFileSize>DestUnpSize)
- return;
- if (Suspended)
+ if (Window!=NULL) // If allocated by preceding files.
{
- FileExtracted=false;
- return;
+ free(Window);
+ Window=NULL;
}
+ FragWindow.Init(WinSize);
+ Fragmented=true;
}
- if (UnpBlockType==BLOCK_PPM)
- {
- int Ch=PPM.DecodeChar();
- if (Ch==-1)
- {
- PPM.CleanUp();
- // turn off PPM compression mode in case of error, so UnRAR will
- // call PPM.DecodeInit in case it needs to turn it on back later.
- UnpBlockType=BLOCK_LZ;
- break;
- }
- if (Ch==PPMEscChar)
- {
- int NextCh=PPM.DecodeChar();
- if (NextCh==0)
- {
- if (!ReadTables())
- break;
- continue;
- }
- if (NextCh==2 || NextCh==-1)
- break;
- if (NextCh==3)
- {
- if (!ReadVMCodePPM())
- break;
- continue;
- }
- if (NextCh==4)
- {
- unsigned int Distance=0,Length;
- bool Failed=false;
- for (int I=0;I<4 && !Failed;I++)
- {
- int Ch=PPM.DecodeChar();
- if (Ch==-1)
- Failed=true;
- else
- if (I==3)
- Length=(byte)Ch;
- else
- Distance=(Distance<<8)+(byte)Ch;
- }
- if (Failed)
- break;
-
-#ifdef _MSC_VER
- // avoid a warning about uninitialized 'Length' variable
- #pragma warning( disable : 4701 )
-#endif
- CopyString(Length+32,Distance+2);
- continue;
- }
- if (NextCh==5)
- {
- int Length=PPM.DecodeChar();
- if (Length==-1)
- break;
- CopyString(Length+4,1);
- continue;
- }
- }
- Window[UnpPtr++]=Ch;
- continue;
- }
-
- int Number=DecodeNumber((struct Decode *)&LD);
- if (Number<256)
- {
- Window[UnpPtr++]=(byte)Number;
- continue;
- }
- if (Number>=271)
- {
- int Length=LDecode[Number-=271]+3;
- if ((Bits=LBits[Number])>0)
- {
- Length+=getbits()>>(16-Bits);
- addbits(Bits);
- }
-
- int DistNumber=DecodeNumber((struct Decode *)&DD);
- unsigned int Distance=DDecode[DistNumber]+1;
- if ((Bits=DBits[DistNumber])>0)
- {
- if (DistNumber>9)
- {
- if (Bits>4)
- {
- Distance+=((getbits()>>(20-Bits))<<4);
- addbits(Bits-4);
- }
- if (LowDistRepCount>0)
- {
- LowDistRepCount--;
- Distance+=PrevLowDist;
- }
- else
- {
- int LowDist=DecodeNumber((struct Decode *)&LDD);
- if (LowDist==16)
- {
- LowDistRepCount=LOW_DIST_REP_COUNT-1;
- Distance+=PrevLowDist;
- }
- else
- {
- Distance+=LowDist;
- PrevLowDist=LowDist;
- }
- }
- }
- else
- {
- Distance+=getbits()>>(16-Bits);
- addbits(Bits);
- }
- }
+ if (!Fragmented)
+ {
+ // Clean the window to generate the same output when unpacking corrupt
+ // RAR files, which may access unused areas of sliding dictionary.
+ memset(NewWindow,0,WinSize);
- if (Distance>=0x2000)
- {
- Length++;
- if (Distance>=0x40000L)
- Length++;
- }
+ // If Window is not NULL, it means that window size has grown.
+ // In solid streams we need to copy data to a new window in such case.
+ // RAR archiving code does not allow it in solid streams now,
+ // but let's implement it anyway just in case we'll change it sometimes.
+ if (Grow)
+ for (size_t I=1;I<=MaxWinSize;I++)
+ NewWindow[(UnpPtr-I)&(WinSize-1)]=Window[(UnpPtr-I)&(MaxWinSize-1)];
- InsertOldDist(Distance);
- InsertLastMatch(Length,Distance);
- CopyString(Length,Distance);
- continue;
- }
- if (Number==256)
- {
- if (!ReadEndOfBlock())
- break;
- continue;
- }
- if (Number==257)
- {
- if (!ReadVMCode())
- break;
- continue;
- }
- if (Number==258)
- {
- if (LastLength!=0)
- CopyString(LastLength,LastDist);
- continue;
- }
- if (Number<263)
- {
- int DistNum=Number-259;
- unsigned int Distance=OldDist[DistNum];
- for (int I=DistNum;I>0;I--)
- OldDist[I]=OldDist[I-1];
- OldDist[0]=Distance;
-
- int LengthNumber=DecodeNumber((struct Decode *)&RD);
- int Length=LDecode[LengthNumber]+2;
- if ((Bits=LBits[LengthNumber])>0)
- {
- Length+=getbits()>>(16-Bits);
- addbits(Bits);
- }
- InsertLastMatch(Length,Distance);
- CopyString(Length,Distance);
- continue;
- }
- if (Number<272)
- {
- unsigned int Distance=SDDecode[Number-=263]+1;
- if ((Bits=SDBits[Number])>0)
- {
- Distance+=getbits()>>(16-Bits);
- addbits(Bits);
- }
- InsertOldDist(Distance);
- InsertLastMatch(2,Distance);
- CopyString(2,Distance);
- continue;
- }
+ if (Window!=NULL)
+ free(Window);
+ Window=NewWindow;
}
- UnpWriteBuf();
+
+ MaxWinSize=WinSize;
+ MaxWinMask=MaxWinSize-1;
}
-bool Unpack::ReadEndOfBlock()
+void Unpack::DoUnpack(uint Method,bool Solid)
{
- unsigned int BitField=getbits();
- bool NewTable,NewFile=false;
- if (BitField & 0x8000)
- {
- NewTable=true;
- addbits(1);
- }
- else
+ // Methods <50 will crash in Fragmented mode when accessing NULL Window.
+ // They cannot be called in such mode now, but we check it below anyway
+ // just for extra safety.
+ switch(Method)
{
- NewFile=true;
- NewTable=(BitField & 0x4000);
- addbits(2);
+#ifndef SFX_MODULE
+ case 15: // rar 1.5 compression
+ if (!Fragmented)
+ Unpack15(Solid);
+ break;
+ case 20: // rar 2.x compression
+ case 26: // files larger than 2GB
+ if (!Fragmented)
+ Unpack20(Solid);
+ break;
+#endif
+ case 29: // rar 3.x compression
+ if (!Fragmented)
+ Unpack29(Solid);
+ break;
+ case 50: // RAR 5.0 compression algorithm.
+#ifdef RAR_SMP
+ if (MaxUserThreads>1)
+ {
+// We do not use the multithreaded unpack routine to repack RAR archives
+// in 'suspended' mode, because unlike the single threaded code it can
+// write more than one dictionary for same loop pass. So we would need
+// larger buffers of unknown size. Also we do not support multithreading
+// in fragmented window mode.
+ if (!Fragmented)
+ {
+ Unpack5MT(Solid);
+ break;
+ }
+ }
+#endif
+ Unpack5(Solid);
+ break;
}
- TablesRead=!NewTable;
- return !(NewFile || NewTable && !ReadTables());
}
-bool Unpack::ReadVMCode()
+void Unpack::UnpInitData(bool Solid)
{
- unsigned int FirstByte=getbits()>>8;
- addbits(8);
- int Length=(FirstByte & 7)+1;
- if (Length==7)
- {
- Length=(getbits()>>8)+7;
- addbits(8);
- }
- else
- if (Length==8)
- {
- Length=getbits();
- addbits(16);
- }
- Array<byte> VMCode(Length);
- for (int I=0;I<Length;I++)
+ if (!Solid)
{
- if (InAddr>=ReadTop-1 && !UnpReadBuf() && I<Length-1)
- return(false);
- VMCode[I]=getbits()>>8;
- addbits(8);
+ memset(OldDist,0,sizeof(OldDist));
+ OldDistPtr=0;
+ LastDist=LastLength=0;
+// memset(Window,0,MaxWinSize);
+ memset(&BlockTables,0,sizeof(BlockTables));
+ UnpPtr=WrPtr=0;
+ WriteBorder=Min(MaxWinSize,UNPACK_MAX_WRITE)&MaxWinMask;
}
- return(AddVMCode(FirstByte,&VMCode[0],Length));
-}
+ // Filters never share several solid files, so we can safely reset them
+ // even in solid archive.
+ InitFilters();
+ Inp.InitBitInput();
+ WrittenFileSize=0;
+ ReadTop=0;
+ ReadBorder=0;
-bool Unpack::ReadVMCodePPM()
-{
- unsigned int FirstByte=PPM.DecodeChar();
- if ((int)FirstByte==-1)
- return(false);
- int Length=(FirstByte & 7)+1;
- if (Length==7)
- {
- int B1=PPM.DecodeChar();
- if (B1==-1)
- return(false);
- Length=B1+7;
- }
- else
- if (Length==8)
- {
- int B1=PPM.DecodeChar();
- if (B1==-1)
- return(false);
- int B2=PPM.DecodeChar();
- if (B2==-1)
- return(false);
- Length=B1*256+B2;
- }
- Array<byte> VMCode(Length);
- for (int I=0;I<Length;I++)
- {
- int Ch=PPM.DecodeChar();
- if (Ch==-1)
- return(false);
- VMCode[I]=Ch;
- }
- return(AddVMCode(FirstByte,&VMCode[0],Length));
+ memset(&BlockHeader,0,sizeof(BlockHeader));
+ BlockHeader.BlockSize=-1; // '-1' means not defined yet.
+#ifndef SFX_MODULE
+ UnpInitData20(Solid);
+#endif
+ UnpInitData30(Solid);
+ UnpInitData50(Solid);
}
-bool Unpack::AddVMCode(unsigned int FirstByte,byte *Code,int CodeSize)
+// LengthTable contains the length in bits for every element of alphabet.
+// Dec is the structure to decode Huffman code/
+// Size is size of length table and DecodeNum field in Dec structure,
+void Unpack::MakeDecodeTables(byte *LengthTable,DecodeTable *Dec,uint Size)
{
- BitInput Inp;
- Inp.InitBitInput();
- memcpy(Inp.InBuf,Code,Min(BitInput::MAX_SIZE,CodeSize));
- VM.Init();
+ // Size of alphabet and DecodePos array.
+ Dec->MaxNum=Size;
- uint FiltPos;
- if (FirstByte & 0x80)
- {
- FiltPos=RarVM::ReadData(Inp);
- if (FiltPos==0)
- InitFilters();
- else
- FiltPos--;
- }
- else
- FiltPos=LastFilter; // use the same filter as last time
+ // Calculate how many entries for every bit length in LengthTable we have.
+ uint LengthCount[16];
+ memset(LengthCount,0,sizeof(LengthCount));
+ for (size_t I=0;I<Size;I++)
+ LengthCount[LengthTable[I] & 0xf]++;
- if (FiltPos>Filters.Size() || FiltPos>OldFilterLengths.Size())
- return(false);
- LastFilter=FiltPos;
- bool NewFilter=(FiltPos==Filters.Size());
+ // We must not calculate the number of zero length codes.
+ LengthCount[0]=0;
- UnpackFilter *StackFilter=new UnpackFilter; // new filter for PrgStack
+ // Set the entire DecodeNum to zero.
+ memset(Dec->DecodeNum,0,Size*sizeof(*Dec->DecodeNum));
- UnpackFilter *Filter;
- if (NewFilter) // new filter code, never used before since VM reset
- {
- // too many different filters, corrupt archive
- if (FiltPos>1024)
- return(false);
-
- Filters.Add(1);
- Filters[Filters.Size()-1]=Filter=new UnpackFilter;
- StackFilter->ParentFilter=Filters.Size()-1;
- OldFilterLengths.Add(1);
- Filter->ExecCount=0;
- }
- else // filter was used in the past
- {
- Filter=Filters[FiltPos];
- StackFilter->ParentFilter=FiltPos;
- Filter->ExecCount++;
- }
+ // Initialize not really used entry for zero length code.
+ Dec->DecodePos[0]=0;
- int EmptyCount=0;
- for (int I=0;I<PrgStack.Size();I++)
- {
- PrgStack[I-EmptyCount]=PrgStack[I];
- if (PrgStack[I]==NULL)
- EmptyCount++;
- if (EmptyCount>0)
- PrgStack[I]=NULL;
- }
- if (EmptyCount==0)
- {
- PrgStack.Add(1);
- EmptyCount=1;
- }
- int StackPos=PrgStack.Size()-EmptyCount;
- PrgStack[StackPos]=StackFilter;
- StackFilter->ExecCount=Filter->ExecCount;
-
- uint BlockStart=RarVM::ReadData(Inp);
- if (FirstByte & 0x40)
- BlockStart+=258;
- StackFilter->BlockStart=(BlockStart+UnpPtr)&MAXWINMASK;
- if (FirstByte & 0x20)
- StackFilter->BlockLength=RarVM::ReadData(Inp);
- else
- StackFilter->BlockLength=FiltPos<OldFilterLengths.Size() ? OldFilterLengths[FiltPos]:0;
- StackFilter->NextWindow=WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MAXWINMASK)<=BlockStart;
-
-// DebugLog("\nNextWindow: UnpPtr=%08x WrPtr=%08x BlockStart=%08x",UnpPtr,WrPtr,BlockStart);
-
- OldFilterLengths[FiltPos]=StackFilter->BlockLength;
-
- memset(StackFilter->Prg.InitR,0,sizeof(StackFilter->Prg.InitR));
- StackFilter->Prg.InitR[3]=VM_GLOBALMEMADDR;
- StackFilter->Prg.InitR[4]=StackFilter->BlockLength;
- StackFilter->Prg.InitR[5]=StackFilter->ExecCount;
-
- if (FirstByte & 0x10) // set registers to optional parameters if any
- {
- unsigned int InitMask=Inp.fgetbits()>>9;
- Inp.faddbits(7);
- for (int I=0;I<7;I++)
- if (InitMask & (1<<I))
- StackFilter->Prg.InitR[I]=RarVM::ReadData(Inp);
- }
+ // Start code for bit length 1 is 0.
+ Dec->DecodeLen[0]=0;
- if (NewFilter)
- {
- uint VMCodeSize=RarVM::ReadData(Inp);
- if (VMCodeSize>=0x10000 || VMCodeSize==0)
- return(false);
- Array<byte> VMCode(VMCodeSize);
- for (int I=0;I<VMCodeSize;I++)
- {
- if (Inp.Overflow(3))
- return(false);
- VMCode[I]=Inp.fgetbits()>>8;
- Inp.faddbits(8);
- }
- VM.Prepare(&VMCode[0],VMCodeSize,&Filter->Prg);
- }
- StackFilter->Prg.AltCmd=&Filter->Prg.Cmd[0];
- StackFilter->Prg.CmdCount=Filter->Prg.CmdCount;
+ // Right aligned upper limit code for current bit length.
+ uint UpperLimit=0;
- int StaticDataSize=Filter->Prg.StaticData.Size();
- if (StaticDataSize>0 && StaticDataSize<VM_GLOBALMEMSIZE)
+ for (size_t I=1;I<16;I++)
{
- // read statically defined data contained in DB commands
- StackFilter->Prg.StaticData.Add(StaticDataSize);
- memcpy(&StackFilter->Prg.StaticData[0],&Filter->Prg.StaticData[0],StaticDataSize);
- }
+ // Adjust the upper limit code.
+ UpperLimit+=LengthCount[I];
- if (StackFilter->Prg.GlobalData.Size()<VM_FIXEDGLOBALSIZE)
- {
- StackFilter->Prg.GlobalData.Reset();
- StackFilter->Prg.GlobalData.Add(VM_FIXEDGLOBALSIZE);
- }
- byte *GlobalData=&StackFilter->Prg.GlobalData[0];
- for (int I=0;I<7;I++)
- VM.SetLowEndianValue((uint *)&GlobalData[I*4],StackFilter->Prg.InitR[I]);
- VM.SetLowEndianValue((uint *)&GlobalData[0x1c],StackFilter->BlockLength);
- VM.SetLowEndianValue((uint *)&GlobalData[0x20],0);
- VM.SetLowEndianValue((uint *)&GlobalData[0x2c],StackFilter->ExecCount);
- memset(&GlobalData[0x30],0,16);
-
- if (FirstByte & 8) // put data block passed as parameter if any
- {
- if (Inp.Overflow(3))
- return(false);
- uint DataSize=RarVM::ReadData(Inp);
- if (DataSize>VM_GLOBALMEMSIZE-VM_FIXEDGLOBALSIZE)
- return(false);
- unsigned int CurSize=StackFilter->Prg.GlobalData.Size();
- if (CurSize<DataSize+VM_FIXEDGLOBALSIZE)
- StackFilter->Prg.GlobalData.Add(DataSize+VM_FIXEDGLOBALSIZE-CurSize);
- byte *GlobalData=&StackFilter->Prg.GlobalData[VM_FIXEDGLOBALSIZE];
- for (int I=0;I<DataSize;I++)
- {
- if (Inp.Overflow(3))
- return(false);
- GlobalData[I]=Inp.fgetbits()>>8;
- Inp.faddbits(8);
- }
- }
- return(true);
-}
+ // Left aligned upper limit code.
+ uint LeftAligned=UpperLimit<<(16-I);
+ // Prepare the upper limit code for next bit length.
+ UpperLimit*=2;
-bool Unpack::UnpReadBuf()
-{
- int DataSize=ReadTop-InAddr;
- if (DataSize<0)
- return(false);
- if (InAddr>BitInput::MAX_SIZE/2)
- {
- if (DataSize>0)
- memmove(InBuf,InBuf+InAddr,DataSize);
- InAddr=0;
- ReadTop=DataSize;
+ // Store the left aligned upper limit code.
+ Dec->DecodeLen[I]=(uint)LeftAligned;
+
+ // Every item of this array contains the sum of all preceding items.
+ // So it contains the start position in code list for every bit length.
+ Dec->DecodePos[I]=Dec->DecodePos[I-1]+LengthCount[I-1];
}
- else
- DataSize=ReadTop;
- int ReadCode=UnpIO->UnpRead(InBuf+DataSize,(BitInput::MAX_SIZE-DataSize)&~0xf);
- if (ReadCode>0)
- ReadTop+=ReadCode;
- ReadBorder=ReadTop-30;
- return(ReadCode!=-1);
-}
+ // Prepare the copy of DecodePos. We'll modify this copy below,
+ // so we cannot use the original DecodePos.
+ uint CopyDecodePos[ASIZE(Dec->DecodePos)];
+ memcpy(CopyDecodePos,Dec->DecodePos,sizeof(CopyDecodePos));
-void Unpack::UnpWriteBuf()
-{
- unsigned int WrittenBorder=WrPtr;
- unsigned int WriteSize=(UnpPtr-WrittenBorder)&MAXWINMASK;
- for (int I=0;I<PrgStack.Size();I++)
+ // For every bit length in the bit length table and so for every item
+ // of alphabet.
+ for (uint I=0;I<Size;I++)
{
- UnpackFilter *flt=PrgStack[I];
- if (flt==NULL)
- continue;
- if (flt->NextWindow)
- {
- flt->NextWindow=false;
- continue;
- }
- unsigned int BlockStart=flt->BlockStart;
- unsigned int BlockLength=flt->BlockLength;
- if (((BlockStart-WrittenBorder)&MAXWINMASK)<WriteSize)
- {
- if (WrittenBorder!=BlockStart)
- {
- UnpWriteArea(WrittenBorder,BlockStart);
- WrittenBorder=BlockStart;
- WriteSize=(UnpPtr-WrittenBorder)&MAXWINMASK;
- }
- if (BlockLength<=WriteSize)
- {
- unsigned int BlockEnd=(BlockStart+BlockLength)&MAXWINMASK;
- if (BlockStart<BlockEnd || BlockEnd==0)
- VM.SetMemory(0,Window+BlockStart,BlockLength);
- else
- {
- unsigned int FirstPartLength=MAXWINSIZE-BlockStart;
- VM.SetMemory(0,Window+BlockStart,FirstPartLength);
- VM.SetMemory(FirstPartLength,Window,BlockEnd);
- }
-
- VM_PreparedProgram *ParentPrg=&Filters[flt->ParentFilter]->Prg;
- VM_PreparedProgram *Prg=&flt->Prg;
-
- if (ParentPrg->GlobalData.Size()>VM_FIXEDGLOBALSIZE)
- {
- // copy global data from previous script execution if any
- Prg->GlobalData.Alloc(ParentPrg->GlobalData.Size());
- memcpy(&Prg->GlobalData[VM_FIXEDGLOBALSIZE],&ParentPrg->GlobalData[VM_FIXEDGLOBALSIZE],ParentPrg->GlobalData.Size()-VM_FIXEDGLOBALSIZE);
- }
-
- ExecuteCode(Prg);
-
- if (Prg->GlobalData.Size()>VM_FIXEDGLOBALSIZE)
- {
- // save global data for next script execution
- if (ParentPrg->GlobalData.Size()<Prg->GlobalData.Size())
- ParentPrg->GlobalData.Alloc(Prg->GlobalData.Size());
- memcpy(&ParentPrg->GlobalData[VM_FIXEDGLOBALSIZE],&Prg->GlobalData[VM_FIXEDGLOBALSIZE],Prg->GlobalData.Size()-VM_FIXEDGLOBALSIZE);
- }
- else
- ParentPrg->GlobalData.Reset();
-
- byte *FilteredData=Prg->FilteredData;
- unsigned int FilteredDataSize=Prg->FilteredDataSize;
-
- delete PrgStack[I];
- PrgStack[I]=NULL;
- while (I+1<PrgStack.Size())
- {
- UnpackFilter *NextFilter=PrgStack[I+1];
- if (NextFilter==NULL || NextFilter->BlockStart!=BlockStart ||
- NextFilter->BlockLength!=FilteredDataSize || NextFilter->NextWindow)
- break;
-
- // apply several filters to same data block
-
- VM.SetMemory(0,FilteredData,FilteredDataSize);
-
- VM_PreparedProgram *ParentPrg=&Filters[NextFilter->ParentFilter]->Prg;
- VM_PreparedProgram *NextPrg=&NextFilter->Prg;
+ // Get the current bit length.
+ byte CurBitLength=LengthTable[I] & 0xf;
- if (ParentPrg->GlobalData.Size()>VM_FIXEDGLOBALSIZE)
- {
- // copy global data from previous script execution if any
- NextPrg->GlobalData.Alloc(ParentPrg->GlobalData.Size());
- memcpy(&NextPrg->GlobalData[VM_FIXEDGLOBALSIZE],&ParentPrg->GlobalData[VM_FIXEDGLOBALSIZE],ParentPrg->GlobalData.Size()-VM_FIXEDGLOBALSIZE);
- }
+ if (CurBitLength!=0)
+ {
+ // Last position in code list for current bit length.
+ uint LastPos=CopyDecodePos[CurBitLength];
- ExecuteCode(NextPrg);
+ // Prepare the decode table, so this position in code list will be
+ // decoded to current alphabet item number.
+ Dec->DecodeNum[LastPos]=(ushort)I;
- if (NextPrg->GlobalData.Size()>VM_FIXEDGLOBALSIZE)
- {
- // save global data for next script execution
- if (ParentPrg->GlobalData.Size()<NextPrg->GlobalData.Size())
- ParentPrg->GlobalData.Alloc(NextPrg->GlobalData.Size());
- memcpy(&ParentPrg->GlobalData[VM_FIXEDGLOBALSIZE],&NextPrg->GlobalData[VM_FIXEDGLOBALSIZE],NextPrg->GlobalData.Size()-VM_FIXEDGLOBALSIZE);
- }
- else
- ParentPrg->GlobalData.Reset();
-
- FilteredData=NextPrg->FilteredData;
- FilteredDataSize=NextPrg->FilteredDataSize;
- I++;
- delete PrgStack[I];
- PrgStack[I]=NULL;
- }
- UnpIO->UnpWrite(FilteredData,FilteredDataSize);
- UnpSomeRead=true;
- WrittenFileSize+=FilteredDataSize;
- WrittenBorder=BlockEnd;
- WriteSize=(UnpPtr-WrittenBorder)&MAXWINMASK;
- }
- else
- {
- for (int J=I;J<PrgStack.Size();J++)
- {
- UnpackFilter *flt=PrgStack[J];
- if (flt!=NULL && flt->NextWindow)
- flt->NextWindow=false;
- }
- WrPtr=WrittenBorder;
- return;
- }
+ // We'll use next position number for this bit length next time.
+ // So we pass through the entire range of positions available
+ // for every bit length.
+ CopyDecodePos[CurBitLength]++;
}
}
-
- UnpWriteArea(WrittenBorder,UnpPtr);
- WrPtr=UnpPtr;
-}
-
-void Unpack::ExecuteCode(VM_PreparedProgram *Prg)
-{
- if (Prg->GlobalData.Size()>0)
+ // Define the number of bits to process in quick mode. We use more bits
+ // for larger alphabets. More bits means that more codes will be processed
+ // in quick mode, but also that more time will be spent to preparation
+ // of tables for quick decode.
+ switch (Size)
{
- Prg->InitR[6]=int64to32(WrittenFileSize);
- VM.SetLowEndianValue((uint *)&Prg->GlobalData[0x24],int64to32(WrittenFileSize));
- VM.SetLowEndianValue((uint *)&Prg->GlobalData[0x28],int64to32(WrittenFileSize>>32));
- VM.Execute(Prg);
+ case NC:
+ case NC20:
+ case NC30:
+ Dec->QuickBits=MAX_QUICK_DECODE_BITS;
+ break;
+ default:
+ Dec->QuickBits=MAX_QUICK_DECODE_BITS>3 ? MAX_QUICK_DECODE_BITS-3 : 0;
+ break;
}
-}
+ // Size of tables for quick mode.
+ uint QuickDataSize=1<<Dec->QuickBits;
-void Unpack::UnpWriteArea(unsigned int StartPtr,unsigned int EndPtr)
-{
- if (EndPtr!=StartPtr)
- UnpSomeRead=true;
- if (EndPtr<StartPtr)
+ // Bit length for current code, start from 1 bit codes. It is important
+ // to use 1 bit instead of 0 for minimum code length, so we are moving
+ // forward even when processing a corrupt archive.
+ uint CurBitLength=1;
+
+ // For every right aligned bit string which supports the quick decoding.
+ for (uint Code=0;Code<QuickDataSize;Code++)
{
- UnpWriteData(&Window[StartPtr],-StartPtr & MAXWINMASK);
- UnpWriteData(Window,EndPtr);
- UnpAllBuf=true;
- }
- else
- UnpWriteData(&Window[StartPtr],EndPtr-StartPtr);
-}
+ // Left align the current code, so it will be in usual bit field format.
+ uint BitField=Code<<(16-Dec->QuickBits);
+ // Prepare the table for quick decoding of bit lengths.
+
+ // Find the upper limit for current bit field and adjust the bit length
+ // accordingly if necessary.
+ while (CurBitLength<ASIZE(Dec->DecodeLen) && BitField>=Dec->DecodeLen[CurBitLength])
+ CurBitLength++;
-void Unpack::UnpWriteData(byte *Data,int Size)
-{
- if (WrittenFileSize>=DestUnpSize)
- return;
- int WriteSize=Size;
- Int64 LeftToWrite=DestUnpSize-WrittenFileSize;
- if (WriteSize>LeftToWrite)
- WriteSize=int64to32(LeftToWrite);
- UnpIO->UnpWrite(Data,WriteSize);
- WrittenFileSize+=Size;
-}
+ // Translation of right aligned bit string to bit length.
+ Dec->QuickLen[Code]=CurBitLength;
+ // Prepare the table for quick translation of position in code list
+ // to position in alphabet.
-bool Unpack::ReadTables()
-{
- byte BitLength[BC];
- unsigned char Table[HUFF_TABLE_SIZE];
- if (InAddr>ReadTop-25)
- if (!UnpReadBuf())
- return(false);
- faddbits((8-InBit)&7);
- unsigned int BitField=fgetbits();
- if (BitField & 0x8000)
- {
- UnpBlockType=BLOCK_PPM;
- return(PPM.DecodeInit(this,PPMEscChar));
- }
- UnpBlockType=BLOCK_LZ;
-
- PrevLowDist=0;
- LowDistRepCount=0;
+ // Calculate the distance from the start code for current bit length.
+ uint Dist=BitField-Dec->DecodeLen[CurBitLength-1];
- if (!(BitField & 0x4000))
- memset(UnpOldTable,0,sizeof(UnpOldTable));
- faddbits(2);
+ // Right align the distance.
+ Dist>>=(16-CurBitLength);
- for (int I=0;I<BC;I++)
- {
- int Length=(byte)(fgetbits() >> 12);
- faddbits(4);
- if (Length==15)
+ // Now we can calculate the position in the code list. It is the sum
+ // of first position for current bit length and right aligned distance
+ // between our bit field and start code for current bit length.
+ uint Pos;
+ if (CurBitLength<ASIZE(Dec->DecodePos) &&
+ (Pos=Dec->DecodePos[CurBitLength]+Dist)<Size)
{
- int ZeroCount=(byte)(fgetbits() >> 12);
- faddbits(4);
- if (ZeroCount==0)
- BitLength[I]=15;
- else
- {
- ZeroCount+=2;
- while (ZeroCount-- > 0 && I<sizeof(BitLength)/sizeof(BitLength[0]))
- BitLength[I++]=0;
- I--;
- }
+ // Define the code to alphabet number translation.
+ Dec->QuickNum[Code]=Dec->DecodeNum[Pos];
}
else
- BitLength[I]=Length;
- }
- MakeDecodeTables(BitLength,(struct Decode *)&BD,BC);
-
- const int TableSize=HUFF_TABLE_SIZE;
- for (int I=0;I<TableSize;)
- {
- if (InAddr>ReadTop-5)
- if (!UnpReadBuf())
- return(false);
- int Number=DecodeNumber((struct Decode *)&BD);
- if (Number<16)
{
- Table[I]=(Number+UnpOldTable[I]) & 0xf;
- I++;
+ // Can be here for length table filled with zeroes only (empty).
+ Dec->QuickNum[Code]=0;
}
- else
- if (Number<18)
- {
- int N;
- if (Number==16)
- {
- N=(fgetbits() >> 13)+3;
- faddbits(3);
- }
- else
- {
- N=(fgetbits() >> 9)+11;
- faddbits(7);
- }
- while (N-- > 0 && I<TableSize)
- {
- Table[I]=Table[I-1];
- I++;
- }
- }
- else
- {
- int N;
- if (Number==18)
- {
- N=(fgetbits() >> 13)+3;
- faddbits(3);
- }
- else
- {
- N=(fgetbits() >> 9)+11;
- faddbits(7);
- }
- while (N-- > 0 && I<TableSize)
- Table[I++]=0;
- }
}
- TablesRead=true;
- if (InAddr>ReadTop)
- return(false);
- MakeDecodeTables(&Table[0],(struct Decode *)&LD,NC);
- MakeDecodeTables(&Table[NC],(struct Decode *)&DD,DC);
- MakeDecodeTables(&Table[NC+DC],(struct Decode *)&LDD,LDC);
- MakeDecodeTables(&Table[NC+DC+LDC],(struct Decode *)&RD,RC);
- memcpy(UnpOldTable,Table,sizeof(UnpOldTable));
- return(true);
-}
-
-
-void Unpack::UnpInitData(int Solid)
-{
- if (!Solid)
- {
- TablesRead=false;
- memset(OldDist,0,sizeof(OldDist));
- OldDistPtr=0;
- LastDist=LastLength=0;
-// memset(Window,0,MAXWINSIZE);
- memset(UnpOldTable,0,sizeof(UnpOldTable));
- memset(&LD,0,sizeof(LD));
- memset(&DD,0,sizeof(DD));
- memset(&LDD,0,sizeof(LDD));
- memset(&RD,0,sizeof(RD));
- memset(&BD,0,sizeof(BD));
- UnpPtr=WrPtr=0;
- PPMEscChar=2;
- UnpBlockType=BLOCK_LZ;
-
- InitFilters();
- }
- InitBitInput();
- WrittenFileSize=0;
- ReadTop=0;
- ReadBorder=0;
-#ifndef SFX_MODULE
- UnpInitData20(Solid);
-#endif
-}
-
-
-void Unpack::InitFilters()
-{
- OldFilterLengths.Reset();
- LastFilter=0;
-
- for (int I=0;I<Filters.Size();I++)
- delete Filters[I];
- Filters.Reset();
- for (int I=0;I<PrgStack.Size();I++)
- delete PrgStack[I];
- PrgStack.Reset();
-}
-
-
-void Unpack::MakeDecodeTables(unsigned char *LenTab,struct Decode *Dec,int Size)
-{
- int LenCount[16],TmpPos[16],I;
- long M,N;
- memset(LenCount,0,sizeof(LenCount));
- memset(Dec->DecodeNum,0,Size*sizeof(*Dec->DecodeNum));
- for (I=0;I<Size;I++)
- LenCount[LenTab[I] & 0xF]++;
-
- LenCount[0]=0;
- for (TmpPos[0]=Dec->DecodePos[0]=Dec->DecodeLen[0]=0,N=0,I=1;I<16;I++)
- {
- N=2*(N+LenCount[I]);
- M=N<<(15-I);
- if (M>0xFFFF)
- M=0xFFFF;
- Dec->DecodeLen[I]=(unsigned int)M;
- TmpPos[I]=Dec->DecodePos[I]=Dec->DecodePos[I-1]+LenCount[I-1];
- }
-
- for (I=0;I<Size;I++)
- if (LenTab[I]!=0)
- Dec->DecodeNum[TmpPos[LenTab[I] & 0xF]++]=I;
- Dec->MaxNum=Size;
}
diff --git a/unrar/unrar/unpack.hpp b/unrar/unrar/unpack.hpp
index 75df302..737b31c 100644
--- a/unrar/unrar/unpack.hpp
+++ b/unrar/unrar/unpack.hpp
@@ -1,80 +1,173 @@
#ifndef _RAR_UNPACK_
#define _RAR_UNPACK_
-enum BLOCK_TYPES {BLOCK_LZ,BLOCK_PPM};
-
-struct Decode
+// Maximum allowed number of compressed bits processed in quick mode.
+#define MAX_QUICK_DECODE_BITS 10
+
+// Maximum number of filters per entire data block. Must be at least
+// twice more than MAX_PACK_FILTERS to store filters from two data blocks.
+#define MAX_UNPACK_FILTERS 8192
+
+// Maximum number of filters per entire data block for RAR3 unpack.
+// Must be at least twice more than v3_MAX_PACK_FILTERS to store filters
+// from two data blocks.
+#define MAX3_UNPACK_FILTERS 8192
+
+// Limit maximum number of channels in RAR3 delta filter to some reasonable
+// value to prevent too slow processing of corrupt archives with invalid
+// channels number. Must be equal or larger than v3_MAX_FILTER_CHANNELS.
+// No need to provide it for RAR5, which uses only 5 bits to store channels.
+#define MAX3_UNPACK_CHANNELS 1024
+
+// Maximum size of single filter block. We restrict it to limit memory
+// allocation. Must be equal or larger than MAX_ANALYZE_SIZE.
+#define MAX_FILTER_BLOCK_SIZE 0x400000
+
+// Write data in 4 MB or smaller blocks. Must not exceed PACK_MAX_READ,
+// so we keep the number of buffered filters in unpacker reasonable.
+#define UNPACK_MAX_WRITE 0x400000
+
+// Decode compressed bit fields to alphabet numbers.
+struct DecodeTable:PackDef
{
- unsigned int MaxNum;
- unsigned int DecodeLen[16];
- unsigned int DecodePos[16];
- unsigned int DecodeNum[2];
+ // Real size of DecodeNum table.
+ uint MaxNum;
+
+ // Left aligned start and upper limit codes defining code space
+ // ranges for bit lengths. DecodeLen[BitLength-1] defines the start of
+ // range for bit length and DecodeLen[BitLength] defines next code
+ // after the end of range or in other words the upper limit code
+ // for specified bit length.
+ uint DecodeLen[16];
+
+ // Every item of this array contains the sum of all preceding items.
+ // So it contains the start position in code list for every bit length.
+ uint DecodePos[16];
+
+ // Number of compressed bits processed in quick mode.
+ // Must not exceed MAX_QUICK_DECODE_BITS.
+ uint QuickBits;
+
+ // Translates compressed bits (up to QuickBits length)
+ // to bit length in quick mode.
+ byte QuickLen[1<<MAX_QUICK_DECODE_BITS];
+
+ // Translates compressed bits (up to QuickBits length)
+ // to position in alphabet in quick mode.
+ // 'ushort' saves some memory and even provides a little speed gain
+ // comparting to 'uint' here.
+ ushort QuickNum[1<<MAX_QUICK_DECODE_BITS];
+
+ // Translate the position in code list to position in alphabet.
+ // We do not allocate it dynamically to avoid performance overhead
+ // introduced by pointer, so we use the largest possible table size
+ // as array dimension. Real size of this array is defined in MaxNum.
+ // We use this array if compressed bit field is too lengthy
+ // for QuickLen based translation.
+ // 'ushort' saves some memory and even provides a little speed gain
+ // comparting to 'uint' here.
+ ushort DecodeNum[LARGEST_TABLE_SIZE];
};
-struct LitDecode
+
+struct UnpackBlockHeader
{
- unsigned int MaxNum;
- unsigned int DecodeLen[16];
- unsigned int DecodePos[16];
- unsigned int DecodeNum[NC];
+ int BlockSize;
+ int BlockBitSize;
+ int BlockStart;
+ int HeaderSize;
+ bool LastBlockInFile;
+ bool TablePresent;
};
-struct DistDecode
+
+struct UnpackBlockTables
{
- unsigned int MaxNum;
- unsigned int DecodeLen[16];
- unsigned int DecodePos[16];
- unsigned int DecodeNum[DC];
+ DecodeTable LD; // Decode literals.
+ DecodeTable DD; // Decode distances.
+ DecodeTable LDD; // Decode lower bits of distances.
+ DecodeTable RD; // Decode repeating distances.
+ DecodeTable BD; // Decode bit lengths in Huffman table.
};
-struct LowDistDecode
-{
- unsigned int MaxNum;
- unsigned int DecodeLen[16];
- unsigned int DecodePos[16];
- unsigned int DecodeNum[LDC];
+
+#ifdef RAR_SMP
+enum UNP_DEC_TYPE {
+ UNPDT_LITERAL=0,UNPDT_MATCH,UNPDT_FULLREP,UNPDT_REP,UNPDT_FILTER
};
-struct RepDecode
+struct UnpackDecodedItem
{
- unsigned int MaxNum;
- unsigned int DecodeLen[16];
- unsigned int DecodePos[16];
- unsigned int DecodeNum[RC];
+ byte Type; // 'byte' instead of enum type to reduce memory use.
+ ushort Length;
+ union
+ {
+ uint Distance;
+ byte Literal[8]; // Store up to 8 chars here to speed up extraction.
+ };
};
-struct BitDecode
+
+struct UnpackThreadData
{
- unsigned int MaxNum;
- unsigned int DecodeLen[16];
- unsigned int DecodePos[16];
- unsigned int DecodeNum[BC];
+ Unpack *UnpackPtr;
+ BitInput Inp;
+ bool HeaderRead;
+ UnpackBlockHeader BlockHeader;
+ bool TableRead;
+ UnpackBlockTables BlockTables;
+ int DataSize; // Data left in buffer. Can be less than block size.
+ bool DamagedData;
+ bool LargeBlock;
+ bool NoDataLeft; // 'true' if file is read completely.
+ bool Incomplete; // Not entire block was processed, need to read more data.
+
+ UnpackDecodedItem *Decoded;
+ uint DecodedSize;
+ uint DecodedAllocated;
+ uint ThreadNumber; // For debugging.
+
+ UnpackThreadData()
+ :Inp(false)
+ {
+ Decoded=NULL;
+ }
+ ~UnpackThreadData()
+ {
+ if (Decoded!=NULL)
+ free(Decoded);
+ }
};
+#endif
+
struct UnpackFilter
{
+ byte Type;
+ uint BlockStart;
+ uint BlockLength;
+ byte Channels;
+// uint Width;
+// byte PosR;
+ bool NextWindow;
+};
+
+
+struct UnpackFilter30
+{
unsigned int BlockStart;
unsigned int BlockLength;
- unsigned int ExecCount;
bool NextWindow;
- // position of parent filter in Filters array used as prototype for filter
+ // Position of parent filter in Filters array used as prototype for filter
// in PrgStack array. Not defined for filters in Filters array.
unsigned int ParentFilter;
VM_PreparedProgram Prg;
};
-/***************************** Unpack v 2.0 *********************************/
-struct MultDecode
-{
- unsigned int MaxNum;
- unsigned int DecodeLen[16];
- unsigned int DecodePos[16];
- unsigned int DecodeNum[MC20];
-};
-struct AudioVariables
+struct AudioVariables // For RAR 2.0 archives only.
{
int K1,K2,K3,K4,K5;
int D1,D2,D3,D4;
@@ -83,84 +176,112 @@ struct AudioVariables
unsigned int ByteCount;
int LastChar;
};
-/***************************** Unpack v 2.0 *********************************/
-class Unpack:private BitInput
+// We can use the fragmented dictionary in case heap does not have the single
+// large enough memory block. It is slower than normal dictionary.
+class FragmentedWindow
{
private:
- friend class Pack;
+ enum {MAX_MEM_BLOCKS=32};
- void Unpack29(bool Solid);
+ void Reset();
+ byte *Mem[MAX_MEM_BLOCKS];
+ size_t MemSize[MAX_MEM_BLOCKS];
+ public:
+ FragmentedWindow();
+ ~FragmentedWindow();
+ void Init(size_t WinSize);
+ byte& operator [](size_t Item);
+ void CopyString(uint Length,uint Distance,size_t &UnpPtr,size_t MaxWinMask);
+ void CopyData(byte *Dest,size_t WinPos,size_t Size);
+ size_t GetBlockSize(size_t StartPos,size_t RequiredSize);
+};
+
+
+class Unpack:PackDef
+{
+ private:
+
+ void Unpack5(bool Solid);
+ void Unpack5MT(bool Solid);
bool UnpReadBuf();
void UnpWriteBuf();
- void ExecuteCode(VM_PreparedProgram *Prg);
- void UnpWriteArea(unsigned int StartPtr,unsigned int EndPtr);
- void UnpWriteData(byte *Data,int Size);
- bool ReadTables();
- void MakeDecodeTables(unsigned char *LenTab,struct Decode *Dec,int Size);
- int DecodeNumber(struct Decode *Dec);
+ byte* ApplyFilter(byte *Data,uint DataSize,UnpackFilter *Flt);
+ void UnpWriteArea(size_t StartPtr,size_t EndPtr);
+ void UnpWriteData(byte *Data,size_t Size);
+ _forceinline uint SlotToLength(BitInput &Inp,uint Slot);
+ void UnpInitData50(bool Solid);
+ bool ReadBlockHeader(BitInput &Inp,UnpackBlockHeader &Header);
+ bool ReadTables(BitInput &Inp,UnpackBlockHeader &Header,UnpackBlockTables &Tables);
+ void MakeDecodeTables(byte *LengthTable,DecodeTable *Dec,uint Size);
+ _forceinline uint DecodeNumber(BitInput &Inp,DecodeTable *Dec);
void CopyString();
inline void InsertOldDist(unsigned int Distance);
- inline void InsertLastMatch(unsigned int Length,unsigned int Distance);
- void UnpInitData(int Solid);
- void CopyString(unsigned int Length,unsigned int Distance);
- bool ReadEndOfBlock();
- bool ReadVMCode();
- bool ReadVMCodePPM();
- bool AddVMCode(unsigned int FirstByte,byte *Code,int CodeSize);
+ void UnpInitData(bool Solid);
+ _forceinline void CopyString(uint Length,uint Distance);
+ uint ReadFilterData(BitInput &Inp);
+ bool ReadFilter(BitInput &Inp,UnpackFilter &Filter);
+ bool AddFilter(UnpackFilter &Filter);
+ bool AddFilter();
void InitFilters();
ComprDataIO *UnpIO;
- ModelPPM PPM;
- int PPMEscChar;
+ BitInput Inp;
- RarVM VM;
+#ifdef RAR_SMP
+ void InitMT();
+ bool UnpackLargeBlock(UnpackThreadData &D);
+ bool ProcessDecoded(UnpackThreadData &D);
- /* Filters code, one entry per filter */
- Array<UnpackFilter*> Filters;
+ ThreadPool *UnpThreadPool;
+ UnpackThreadData *UnpThreadData;
+ uint MaxUserThreads;
+ byte *ReadBufMT;
+#endif
- /* Filters stack, several entrances of same filter are possible */
- Array<UnpackFilter*> PrgStack;
+ Array<byte> FilterSrcMemory;
+ Array<byte> FilterDstMemory;
- /* lengths of preceding blocks, one length per filter. Used to reduce
- size required to write block length if lengths are repeating */
- Array<int> OldFilterLengths;
+ // Filters code, one entry per filter.
+ Array<UnpackFilter> Filters;
- int LastFilter;
+ uint OldDist[4],OldDistPtr;
+ uint LastLength;
- bool TablesRead;
- struct LitDecode LD;
- struct DistDecode DD;
- struct LowDistDecode LDD;
- struct RepDecode RD;
- struct BitDecode BD;
+ // LastDist is necessary only for RAR2 and older with circular OldDist
+ // array. In RAR3 last distance is always stored in OldDist[0].
+ uint LastDist;
- unsigned int OldDist[4],OldDistPtr;
- unsigned int LastDist,LastLength;
-
- unsigned int UnpPtr,WrPtr;
+ size_t UnpPtr,WrPtr;
- int ReadTop;
+ // Top border of read packed data.
+ int ReadTop;
+
+ // Border to call UnpReadBuf. We use it instead of (ReadTop-C)
+ // for optimization reasons. Ensures that we have C bytes in buffer
+ // unless we are at the end of file.
int ReadBorder;
- unsigned char UnpOldTable[HUFF_TABLE_SIZE];
+ UnpackBlockHeader BlockHeader;
+ UnpackBlockTables BlockTables;
- int UnpBlockType;
+ size_t WriteBorder;
byte *Window;
- bool ExternalWindow;
+ FragmentedWindow FragWindow;
+ bool Fragmented;
- Int64 DestUnpSize;
+
+ int64 DestUnpSize;
bool Suspended;
bool UnpAllBuf;
bool UnpSomeRead;
- Int64 WrittenFileSize;
+ int64 WrittenFileSize;
bool FileExtracted;
- int PrevLowDist,LowDistRepCount;
/***************************** Unpack v 1.5 *********************************/
void Unpack15(bool Solid);
@@ -168,49 +289,115 @@ class Unpack:private BitInput
void LongLZ();
void HuffDecode();
void GetFlagsBuf();
- void OldUnpInitData(int Solid);
+ void UnpInitData15(int Solid);
void InitHuff();
- void CorrHuff(unsigned int *CharSet,unsigned int *NumToPlace);
- void OldCopyString(unsigned int Distance,unsigned int Length);
- unsigned int DecodeNum(int Num,unsigned int StartPos,
- unsigned int *DecTab,unsigned int *PosTab);
- void OldUnpWriteBuf();
-
- unsigned int ChSet[256],ChSetA[256],ChSetB[256],ChSetC[256];
- unsigned int Place[256],PlaceA[256],PlaceB[256],PlaceC[256];
- unsigned int NToPl[256],NToPlB[256],NToPlC[256];
- unsigned int FlagBuf,AvrPlc,AvrPlcB,AvrLn1,AvrLn2,AvrLn3;
+ void CorrHuff(ushort *CharSet,byte *NumToPlace);
+ void CopyString15(uint Distance,uint Length);
+ uint DecodeNum(uint Num,uint StartPos,uint *DecTab,uint *PosTab);
+
+ ushort ChSet[256],ChSetA[256],ChSetB[256],ChSetC[256];
+ byte NToPl[256],NToPlB[256],NToPlC[256];
+ uint FlagBuf,AvrPlc,AvrPlcB,AvrLn1,AvrLn2,AvrLn3;
int Buf60,NumHuf,StMode,LCount,FlagsCnt;
- unsigned int Nhfb,Nlzb,MaxDist3;
+ uint Nhfb,Nlzb,MaxDist3;
/***************************** Unpack v 1.5 *********************************/
/***************************** Unpack v 2.0 *********************************/
void Unpack20(bool Solid);
- struct MultDecode MD[4];
+
+ DecodeTable MD[4]; // Decode multimedia data, up to 4 channels.
+
unsigned char UnpOldTable20[MC20*4];
- int UnpAudioBlock,UnpChannels,UnpCurChannel,UnpChannelDelta;
- void CopyString20(unsigned int Length,unsigned int Distance);
+ bool UnpAudioBlock;
+ uint UnpChannels,UnpCurChannel;
+ int UnpChannelDelta;
+ void CopyString20(uint Length,uint Distance);
bool ReadTables20();
+ void UnpWriteBuf20();
void UnpInitData20(int Solid);
void ReadLastTables();
byte DecodeAudio(int Delta);
struct AudioVariables AudV[4];
/***************************** Unpack v 2.0 *********************************/
+/***************************** Unpack v 3.0 *********************************/
+ enum BLOCK_TYPES {BLOCK_LZ,BLOCK_PPM};
+
+ void UnpInitData30(bool Solid);
+ void Unpack29(bool Solid);
+ void InitFilters30(bool Solid);
+ bool ReadEndOfBlock();
+ bool ReadVMCode();
+ bool ReadVMCodePPM();
+ bool AddVMCode(uint FirstByte,byte *Code,uint CodeSize);
+ int SafePPMDecodeChar();
+ bool ReadTables30();
+ bool UnpReadBuf30();
+ void UnpWriteBuf30();
+ void ExecuteCode(VM_PreparedProgram *Prg);
+
+ int PrevLowDist,LowDistRepCount;
+
+ ModelPPM PPM;
+ int PPMEscChar;
+
+ byte UnpOldTable[HUFF_TABLE_SIZE30];
+ int UnpBlockType;
+
+ // If we already read decoding tables for Unpack v2,v3,v5.
+ // We should not use a single variable for all algorithm versions,
+ // because we can have a corrupt archive with one algorithm file
+ // followed by another algorithm file with "solid" flag and we do not
+ // want to reuse tables from one algorithm in another.
+ bool TablesRead2,TablesRead3,TablesRead5;
+
+ // Virtual machine to execute filters code.
+ RarVM VM;
+
+ // Buffer to read VM filters code. We moved it here from AddVMCode
+ // function to reduce time spent in BitInput constructor.
+ BitInput VMCodeInp;
+
+ // Filters code, one entry per filter.
+ Array<UnpackFilter30 *> Filters30;
+
+ // Filters stack, several entrances of same filter are possible.
+ Array<UnpackFilter30 *> PrgStack;
+
+ // Lengths of preceding data blocks, one length of one last block
+ // for every filter. Used to reduce the size required to write
+ // the data block length if lengths are repeating.
+ Array<int> OldFilterLengths;
+
+ int LastFilter;
+/***************************** Unpack v 3.0 *********************************/
+
public:
Unpack(ComprDataIO *DataIO);
~Unpack();
- void Init(byte *Window=NULL);
- void DoUnpack(int Method,bool Solid);
+ void Init(size_t WinSize,bool Solid);
+ void DoUnpack(uint Method,bool Solid);
bool IsFileExtracted() {return(FileExtracted);}
- void SetDestSize(Int64 DestSize) {DestUnpSize=DestSize;FileExtracted=false;}
+ void SetDestSize(int64 DestSize) {DestUnpSize=DestSize;FileExtracted=false;}
void SetSuspended(bool Suspended) {Unpack::Suspended=Suspended;}
- unsigned int GetChar()
+#ifdef RAR_SMP
+ void SetThreads(uint Threads);
+ void UnpackDecode(UnpackThreadData &D);
+#endif
+
+ size_t MaxWinSize;
+ size_t MaxWinMask;
+
+ uint GetChar()
{
- if (InAddr>BitInput::MAX_SIZE-30)
+ if (Inp.InAddr>BitInput::MAX_SIZE-30)
+ {
UnpReadBuf();
- return(InBuf[InAddr++]);
+ if (Inp.InAddr>=BitInput::MAX_SIZE) // If nothing was read.
+ return 0;
+ }
+ return Inp.InBuf[Inp.InAddr++];
}
};
diff --git a/unrar/unrar/unpack15.cpp b/unrar/unrar/unpack15.cpp
index 170085b..1e7cf76 100644
--- a/unrar/unrar/unpack15.cpp
+++ b/unrar/unrar/unpack15.cpp
@@ -39,22 +39,17 @@ static unsigned int PosHf4[]={0,0,0,0,0,0,0,0,0,255,0,0,0};
void Unpack::Unpack15(bool Solid)
{
- if (Suspended)
- UnpPtr=WrPtr;
- else
+ UnpInitData(Solid);
+ UnpInitData15(Solid);
+ UnpReadBuf();
+ if (!Solid)
{
- UnpInitData(Solid);
- OldUnpInitData(Solid);
- UnpReadBuf();
- if (!Solid)
- {
- InitHuff();
- UnpPtr=0;
- }
- else
- UnpPtr=WrPtr;
- --DestUnpSize;
+ InitHuff();
+ UnpPtr=0;
}
+ else
+ UnpPtr=WrPtr;
+ --DestUnpSize;
if (DestUnpSize>=0)
{
GetFlagsBuf();
@@ -63,16 +58,12 @@ void Unpack::Unpack15(bool Solid)
while (DestUnpSize>=0)
{
- UnpPtr&=MAXWINMASK;
+ UnpPtr&=MaxWinMask;
- if (InAddr>ReadTop-30 && !UnpReadBuf())
+ if (Inp.InAddr>ReadTop-30 && !UnpReadBuf())
break;
- if (((WrPtr-UnpPtr) & MAXWINMASK)<270 && WrPtr!=UnpPtr)
- {
- OldUnpWriteBuf();
- if (Suspended)
- return;
- }
+ if (((WrPtr-UnpPtr) & MaxWinMask)<270 && WrPtr!=UnpPtr)
+ UnpWriteBuf20();
if (StMode)
{
HuffDecode();
@@ -116,23 +107,7 @@ void Unpack::Unpack15(bool Solid)
}
}
}
- OldUnpWriteBuf();
-}
-
-
-void Unpack::OldUnpWriteBuf()
-{
- if (UnpPtr!=WrPtr)
- UnpSomeRead=true;
- if (UnpPtr<WrPtr)
- {
- UnpIO->UnpWrite(&Window[WrPtr],-WrPtr & MAXWINMASK);
- UnpIO->UnpWrite(Window,UnpPtr);
- UnpAllBuf=true;
- }
- else
- UnpIO->UnpWrite(&Window[WrPtr],UnpPtr-WrPtr);
- WrPtr=UnpPtr;
+ UnpWriteBuf20();
}
@@ -155,13 +130,13 @@ void Unpack::ShortLZ()
int DistancePlace;
NumHuf=0;
- unsigned int BitField=fgetbits();
+ unsigned int BitField=Inp.fgetbits();
if (LCount==2)
{
- faddbits(1);
+ Inp.faddbits(1);
if (BitField >= 0x8000)
{
- OldCopyString((unsigned int)LastDist,LastLength);
+ CopyString15((unsigned int)LastDist,LastLength);
return;
}
BitField <<= 1;
@@ -178,14 +153,14 @@ void Unpack::ShortLZ()
for (Length=0;;Length++)
if (((BitField^ShortXor1[Length]) & (~(0xff>>GetShortLen1(Length))))==0)
break;
- faddbits(GetShortLen1(Length));
+ Inp.faddbits(GetShortLen1(Length));
}
else
{
for (Length=0;;Length++)
if (((BitField^ShortXor2[Length]) & (~(0xff>>GetShortLen2(Length))))==0)
break;
- faddbits(GetShortLen2(Length));
+ Inp.faddbits(GetShortLen2(Length));
}
if (Length >= 9)
@@ -193,25 +168,25 @@ void Unpack::ShortLZ()
if (Length == 9)
{
LCount++;
- OldCopyString((unsigned int)LastDist,LastLength);
+ CopyString15((unsigned int)LastDist,LastLength);
return;
}
if (Length == 14)
{
LCount=0;
- Length=DecodeNum(fgetbits(),STARTL2,DecL2,PosL2)+5;
- Distance=(fgetbits()>>1) | 0x8000;
- faddbits(15);
+ Length=DecodeNum(Inp.fgetbits(),STARTL2,DecL2,PosL2)+5;
+ Distance=(Inp.fgetbits()>>1) | 0x8000;
+ Inp.faddbits(15);
LastLength=Length;
LastDist=Distance;
- OldCopyString(Distance,Length);
+ CopyString15(Distance,Length);
return;
}
LCount=0;
SaveLength=Length;
Distance=OldDist[(OldDistPtr-(Length-9)) & 3];
- Length=DecodeNum(fgetbits(),STARTL1,DecL1,PosL1)+2;
+ Length=DecodeNum(Inp.fgetbits(),STARTL1,DecL1,PosL1)+2;
if (Length==0x101 && SaveLength==10)
{
Buf60 ^= 1;
@@ -226,7 +201,7 @@ void Unpack::ShortLZ()
OldDistPtr = OldDistPtr & 3;
LastLength=Length;
LastDist=Distance;
- OldCopyString(Distance,Length);
+ CopyString15(Distance,Length);
return;
}
@@ -234,13 +209,11 @@ void Unpack::ShortLZ()
AvrLn1 += Length;
AvrLn1 -= AvrLn1 >> 4;
- DistancePlace=DecodeNum(fgetbits(),STARTHF2,DecHf2,PosHf2) & 0xff;
+ DistancePlace=DecodeNum(Inp.fgetbits(),STARTHF2,DecHf2,PosHf2) & 0xff;
Distance=ChSetA[DistancePlace];
if (--DistancePlace != -1)
{
- PlaceA[Distance]--;
LastDistance=ChSetA[DistancePlace];
- PlaceA[LastDistance]++;
ChSetA[DistancePlace+1]=LastDistance;
ChSetA[DistancePlace]=Distance;
}
@@ -249,7 +222,7 @@ void Unpack::ShortLZ()
OldDistPtr = OldDistPtr & 3;
LastLength=Length;
LastDist=Distance;
- OldCopyString(Distance,Length);
+ CopyString15(Distance,Length);
}
@@ -269,7 +242,7 @@ void Unpack::LongLZ()
}
OldAvr2=AvrLn2;
- unsigned int BitField=fgetbits();
+ unsigned int BitField=Inp.fgetbits();
if (AvrLn2 >= 122)
Length=DecodeNum(BitField,STARTL2,DecL2,PosL2);
else
@@ -279,19 +252,19 @@ void Unpack::LongLZ()
if (BitField < 0x100)
{
Length=BitField;
- faddbits(16);
+ Inp.faddbits(16);
}
else
{
for (Length=0;((BitField<<Length)&0x8000)==0;Length++)
;
- faddbits(Length+1);
+ Inp.faddbits(Length+1);
}
AvrLn2 += Length;
AvrLn2 -= AvrLn2 >> 5;
- BitField=fgetbits();
+ BitField=Inp.fgetbits();
if (AvrPlcB > 0x28ff)
DistancePlace=DecodeNum(BitField,STARTHF2,DecHf2,PosHf2);
else
@@ -312,11 +285,11 @@ void Unpack::LongLZ()
break;
}
- ChSetB[DistancePlace]=ChSetB[NewDistancePlace];
+ ChSetB[DistancePlace & 0xff]=ChSetB[NewDistancePlace];
ChSetB[NewDistancePlace]=Distance;
- Distance=((Distance & 0xff00) | (fgetbits() >> 8)) >> 1;
- faddbits(7);
+ Distance=((Distance & 0xff00) | (Inp.fgetbits() >> 8)) >> 1;
+ Inp.faddbits(7);
OldAvr3=AvrLn3;
if (Length!=1 && Length!=4)
@@ -341,7 +314,7 @@ void Unpack::LongLZ()
OldDistPtr = OldDistPtr & 3;
LastLength=Length;
LastDist=Distance;
- OldCopyString(Distance,Length);
+ CopyString15(Distance,Length);
}
@@ -352,7 +325,7 @@ void Unpack::HuffDecode()
unsigned int Distance;
int BytePlace;
- unsigned int BitField=fgetbits();
+ unsigned int BitField=Inp.fgetbits();
if (AvrPlc > 0x75ff)
BytePlace=DecodeNum(BitField,STARTHF4,DecHf4,PosHf4);
@@ -374,8 +347,8 @@ void Unpack::HuffDecode()
BytePlace=0x100;
if (--BytePlace==-1)
{
- BitField=fgetbits();
- faddbits(1);
+ BitField=Inp.fgetbits();
+ Inp.faddbits(1);
if (BitField & 0x8000)
{
NumHuf=StMode=0;
@@ -384,11 +357,11 @@ void Unpack::HuffDecode()
else
{
Length = (BitField & 0x4000) ? 4 : 3;
- faddbits(1);
- Distance=DecodeNum(fgetbits(),STARTHF2,DecHf2,PosHf2);
- Distance = (Distance << 5) | (fgetbits() >> 11);
- faddbits(5);
- OldCopyString(Distance,Length);
+ Inp.faddbits(1);
+ Distance=DecodeNum(Inp.fgetbits(),STARTHF2,DecHf2,PosHf2);
+ Distance = (Distance << 5) | (Inp.fgetbits() >> 11);
+ Inp.faddbits(5);
+ CopyString15(Distance,Length);
return;
}
}
@@ -426,7 +399,15 @@ void Unpack::HuffDecode()
void Unpack::GetFlagsBuf()
{
unsigned int Flags,NewFlagsPlace;
- unsigned int FlagsPlace=DecodeNum(fgetbits(),STARTHF2,DecHf2,PosHf2);
+ unsigned int FlagsPlace=DecodeNum(Inp.fgetbits(),STARTHF2,DecHf2,PosHf2);
+
+ // Our Huffman table stores 257 items and needs all them in other parts
+ // of code such as when StMode is on, so the first item is control item.
+ // While normally we do not use the last item to code the flags byte here,
+ // we need to check for value 256 when unpacking in case we unpack
+ // a corrupt archive.
+ if (FlagsPlace>=sizeof(ChSetC)/sizeof(ChSetC[0]))
+ return;
while (1)
{
@@ -443,7 +424,7 @@ void Unpack::GetFlagsBuf()
}
-void Unpack::OldUnpInitData(int Solid)
+void Unpack::UnpInitData15(int Solid)
{
if (!Solid)
{
@@ -464,8 +445,6 @@ void Unpack::InitHuff()
{
for (unsigned int I=0;I<256;I++)
{
- Place[I]=PlaceA[I]=PlaceB[I]=I;
- PlaceC[I]=(~I+1) & 0xff;
ChSet[I]=ChSetB[I]=I<<8;
ChSetA[I]=I;
ChSetC[I]=((~I+1) & 0xff)<<8;
@@ -477,7 +456,7 @@ void Unpack::InitHuff()
}
-void Unpack::CorrHuff(unsigned int *CharSet,unsigned int *NumToPlace)
+void Unpack::CorrHuff(ushort *CharSet,byte *NumToPlace)
{
int I,J;
for (I=7;I>=0;I--)
@@ -489,23 +468,22 @@ void Unpack::CorrHuff(unsigned int *CharSet,unsigned int *NumToPlace)
}
-void Unpack::OldCopyString(unsigned int Distance,unsigned int Length)
+void Unpack::CopyString15(uint Distance,uint Length)
{
DestUnpSize-=Length;
while (Length--)
{
- Window[UnpPtr]=Window[(UnpPtr-Distance) & MAXWINMASK];
- UnpPtr=(UnpPtr+1) & MAXWINMASK;
+ Window[UnpPtr]=Window[(UnpPtr-Distance) & MaxWinMask];
+ UnpPtr=(UnpPtr+1) & MaxWinMask;
}
}
-unsigned int Unpack::DecodeNum(int Num,unsigned int StartPos,
- unsigned int *DecTab,unsigned int *PosTab)
+uint Unpack::DecodeNum(uint Num,uint StartPos,uint *DecTab,uint *PosTab)
{
int I;
for (Num&=0xfff0,I=0;DecTab[I]<=Num;I++)
StartPos++;
- faddbits(StartPos);
+ Inp.faddbits(StartPos);
return(((Num-(I ? DecTab[I-1]:0))>>(16-StartPos))+PosTab[StartPos]);
}
diff --git a/unrar/unrar/unpack20.cpp b/unrar/unrar/unpack20.cpp
index ce0ab1f..93c8ba0 100644
--- a/unrar/unrar/unpack20.cpp
+++ b/unrar/unrar/unpack20.cpp
@@ -1,28 +1,12 @@
#include "rar.hpp"
-void Unpack::CopyString20(unsigned int Length,unsigned int Distance)
+void Unpack::CopyString20(uint Length,uint Distance)
{
- LastDist=OldDist[OldDistPtr++ & 3]=Distance;
+ LastDist=OldDist[OldDistPtr++]=Distance;
+ OldDistPtr = OldDistPtr & 3; // Needed if RAR 1.5 file is called after RAR 2.0.
LastLength=Length;
DestUnpSize-=Length;
-
- unsigned int DestPtr=UnpPtr-Distance;
- if (DestPtr<MAXWINSIZE-300 && UnpPtr<MAXWINSIZE-300)
- {
- Window[UnpPtr++]=Window[DestPtr++];
- Window[UnpPtr++]=Window[DestPtr++];
- while (Length>2)
- {
- Length--;
- Window[UnpPtr++]=Window[DestPtr++];
- }
- }
- else
- while (Length--)
- {
- Window[UnpPtr]=Window[DestPtr++ & MAXWINMASK];
- UnpPtr=(UnpPtr+1) & MAXWINMASK;
- }
+ CopyString(Length,Distance);
}
@@ -30,11 +14,11 @@ void Unpack::Unpack20(bool Solid)
{
static unsigned char LDecode[]={0,1,2,3,4,5,6,7,8,10,12,14,16,20,24,28,32,40,48,56,64,80,96,112,128,160,192,224};
static unsigned char LBits[]= {0,0,0,0,0,0,0,0,1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5};
- static int DDecode[]={0,1,2,3,4,6,8,12,16,24,32,48,64,96,128,192,256,384,512,768,1024,1536,2048,3072,4096,6144,8192,12288,16384,24576,32768U,49152U,65536,98304,131072,196608,262144,327680,393216,458752,524288,589824,655360,720896,786432,851968,917504,983040};
+ static uint DDecode[]={0,1,2,3,4,6,8,12,16,24,32,48,64,96,128,192,256,384,512,768,1024,1536,2048,3072,4096,6144,8192,12288,16384,24576,32768U,49152U,65536,98304,131072,196608,262144,327680,393216,458752,524288,589824,655360,720896,786432,851968,917504,983040};
static unsigned char DBits[]= {0,0,0,0,1,1,2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16};
static unsigned char SDDecode[]={0,4,8,16,32,64,128,192};
static unsigned char SDBits[]= {2,2,3, 4, 5, 6, 6, 6};
- unsigned int Bits;
+ uint Bits;
if (Suspended)
UnpPtr=WrPtr;
@@ -43,28 +27,27 @@ void Unpack::Unpack20(bool Solid)
UnpInitData(Solid);
if (!UnpReadBuf())
return;
- if (!Solid)
- if (!ReadTables20())
- return;
+ if ((!Solid || !TablesRead2) && !ReadTables20())
+ return;
--DestUnpSize;
}
- while (is64plus(DestUnpSize))
+ while (DestUnpSize>=0)
{
- UnpPtr&=MAXWINMASK;
+ UnpPtr&=MaxWinMask;
- if (InAddr>ReadTop-30)
+ if (Inp.InAddr>ReadTop-30)
if (!UnpReadBuf())
break;
- if (((WrPtr-UnpPtr) & MAXWINMASK)<270 && WrPtr!=UnpPtr)
+ if (((WrPtr-UnpPtr) & MaxWinMask)<270 && WrPtr!=UnpPtr)
{
- OldUnpWriteBuf();
+ UnpWriteBuf20();
if (Suspended)
return;
}
if (UnpAudioBlock)
{
- int AudioNumber=DecodeNumber((struct Decode *)&MD[UnpCurChannel]);
+ uint AudioNumber=DecodeNumber(Inp,&MD[UnpCurChannel]);
if (AudioNumber==256)
{
@@ -72,14 +55,14 @@ void Unpack::Unpack20(bool Solid)
break;
continue;
}
- Window[UnpPtr++]=DecodeAudio(AudioNumber);
+ Window[UnpPtr++]=DecodeAudio((int)AudioNumber);
if (++UnpCurChannel==UnpChannels)
UnpCurChannel=0;
--DestUnpSize;
continue;
}
- int Number=DecodeNumber((struct Decode *)&LD);
+ uint Number=DecodeNumber(Inp,&BlockTables.LD);
if (Number<256)
{
Window[UnpPtr++]=(byte)Number;
@@ -88,19 +71,19 @@ void Unpack::Unpack20(bool Solid)
}
if (Number>269)
{
- int Length=LDecode[Number-=270]+3;
+ uint Length=LDecode[Number-=270]+3;
if ((Bits=LBits[Number])>0)
{
- Length+=getbits()>>(16-Bits);
- addbits(Bits);
+ Length+=Inp.getbits()>>(16-Bits);
+ Inp.addbits(Bits);
}
- int DistNumber=DecodeNumber((struct Decode *)&DD);
- unsigned int Distance=DDecode[DistNumber]+1;
+ uint DistNumber=DecodeNumber(Inp,&BlockTables.DD);
+ uint Distance=DDecode[DistNumber]+1;
if ((Bits=DBits[DistNumber])>0)
{
- Distance+=getbits()>>(16-Bits);
- addbits(Bits);
+ Distance+=Inp.getbits()>>(16-Bits);
+ Inp.addbits(Bits);
}
if (Distance>=0x2000)
@@ -126,13 +109,13 @@ void Unpack::Unpack20(bool Solid)
}
if (Number<261)
{
- unsigned int Distance=OldDist[(OldDistPtr-(Number-256)) & 3];
- int LengthNumber=DecodeNumber((struct Decode *)&RD);
- int Length=LDecode[LengthNumber]+2;
+ uint Distance=OldDist[(OldDistPtr-(Number-256)) & 3];
+ uint LengthNumber=DecodeNumber(Inp,&BlockTables.RD);
+ uint Length=LDecode[LengthNumber]+2;
if ((Bits=LBits[LengthNumber])>0)
{
- Length+=getbits()>>(16-Bits);
- addbits(Bits);
+ Length+=Inp.getbits()>>(16-Bits);
+ Inp.addbits(Bits);
}
if (Distance>=0x101)
{
@@ -149,60 +132,75 @@ void Unpack::Unpack20(bool Solid)
}
if (Number<270)
{
- unsigned int Distance=SDDecode[Number-=261]+1;
+ uint Distance=SDDecode[Number-=261]+1;
if ((Bits=SDBits[Number])>0)
{
- Distance+=getbits()>>(16-Bits);
- addbits(Bits);
+ Distance+=Inp.getbits()>>(16-Bits);
+ Inp.addbits(Bits);
}
CopyString20(2,Distance);
continue;
}
}
ReadLastTables();
- OldUnpWriteBuf();
+ UnpWriteBuf20();
+}
+
+
+void Unpack::UnpWriteBuf20()
+{
+ if (UnpPtr!=WrPtr)
+ UnpSomeRead=true;
+ if (UnpPtr<WrPtr)
+ {
+ UnpIO->UnpWrite(&Window[WrPtr],-(int)WrPtr & MaxWinMask);
+ UnpIO->UnpWrite(Window,UnpPtr);
+ UnpAllBuf=true;
+ }
+ else
+ UnpIO->UnpWrite(&Window[WrPtr],UnpPtr-WrPtr);
+ WrPtr=UnpPtr;
}
bool Unpack::ReadTables20()
{
byte BitLength[BC20];
- unsigned char Table[MC20*4];
- int TableSize,N,I;
- if (InAddr>ReadTop-25)
+ byte Table[MC20*4];
+ if (Inp.InAddr>ReadTop-25)
if (!UnpReadBuf())
- return(false);
- unsigned int BitField=getbits();
- UnpAudioBlock=(BitField & 0x8000);
+ return false;
+ uint BitField=Inp.getbits();
+ UnpAudioBlock=(BitField & 0x8000)!=0;
if (!(BitField & 0x4000))
memset(UnpOldTable20,0,sizeof(UnpOldTable20));
- addbits(2);
+ Inp.addbits(2);
+ uint TableSize;
if (UnpAudioBlock)
{
UnpChannels=((BitField>>12) & 3)+1;
if (UnpCurChannel>=UnpChannels)
UnpCurChannel=0;
- addbits(2);
+ Inp.addbits(2);
TableSize=MC20*UnpChannels;
}
else
TableSize=NC20+DC20+RC20;
- for (I=0;I<BC20;I++)
+ for (uint I=0;I<BC20;I++)
{
- BitLength[I]=(byte)(getbits() >> 12);
- addbits(4);
+ BitLength[I]=(byte)(Inp.getbits() >> 12);
+ Inp.addbits(4);
}
- MakeDecodeTables(BitLength,(struct Decode *)&BD,BC20);
- I=0;
- while (I<TableSize)
+ MakeDecodeTables(BitLength,&BlockTables.BD,BC20);
+ for (uint I=0;I<TableSize;)
{
- if (InAddr>ReadTop-5)
+ if (Inp.InAddr>ReadTop-5)
if (!UnpReadBuf())
- return(false);
- int Number=DecodeNumber((struct Decode *)&BD);
+ return false;
+ uint Number=DecodeNumber(Inp,&BlockTables.BD);
if (Number<16)
{
Table[I]=(Number+UnpOldTable20[I]) & 0xf;
@@ -211,56 +209,61 @@ bool Unpack::ReadTables20()
else
if (Number==16)
{
- N=(getbits() >> 14)+3;
- addbits(2);
- while (N-- > 0 && I<TableSize)
- {
- Table[I]=Table[I-1];
- I++;
- }
+ uint N=(Inp.getbits() >> 14)+3;
+ Inp.addbits(2);
+ if (I==0)
+ return false; // We cannot have "repeat previous" code at the first position.
+ else
+ while (N-- > 0 && I<TableSize)
+ {
+ Table[I]=Table[I-1];
+ I++;
+ }
}
else
{
+ uint N;
if (Number==17)
{
- N=(getbits() >> 13)+3;
- addbits(3);
+ N=(Inp.getbits() >> 13)+3;
+ Inp.addbits(3);
}
else
{
- N=(getbits() >> 9)+11;
- addbits(7);
+ N=(Inp.getbits() >> 9)+11;
+ Inp.addbits(7);
}
while (N-- > 0 && I<TableSize)
Table[I++]=0;
}
}
- if (InAddr>ReadTop)
- return(true);
+ TablesRead2=true;
+ if (Inp.InAddr>ReadTop)
+ return true;
if (UnpAudioBlock)
- for (I=0;I<UnpChannels;I++)
- MakeDecodeTables(&Table[I*MC20],(struct Decode *)&MD[I],MC20);
+ for (uint I=0;I<UnpChannels;I++)
+ MakeDecodeTables(&Table[I*MC20],&MD[I],MC20);
else
{
- MakeDecodeTables(&Table[0],(struct Decode *)&LD,NC20);
- MakeDecodeTables(&Table[NC20],(struct Decode *)&DD,DC20);
- MakeDecodeTables(&Table[NC20+DC20],(struct Decode *)&RD,RC20);
+ MakeDecodeTables(&Table[0],&BlockTables.LD,NC20);
+ MakeDecodeTables(&Table[NC20],&BlockTables.DD,DC20);
+ MakeDecodeTables(&Table[NC20+DC20],&BlockTables.RD,RC20);
}
- memcpy(UnpOldTable20,Table,sizeof(UnpOldTable20));
- return(true);
+ memcpy(UnpOldTable20,Table,TableSize);
+ return true;
}
void Unpack::ReadLastTables()
{
- if (ReadTop>=InAddr+5)
+ if (ReadTop>=Inp.InAddr+5)
if (UnpAudioBlock)
{
- if (DecodeNumber((struct Decode *)&MD[UnpCurChannel])==256)
+ if (DecodeNumber(Inp,&MD[UnpCurChannel])==256)
ReadTables20();
}
else
- if (DecodeNumber((struct Decode *)&LD)==269)
+ if (DecodeNumber(Inp,&BlockTables.LD)==269)
ReadTables20();
}
@@ -269,7 +272,10 @@ void Unpack::UnpInitData20(int Solid)
{
if (!Solid)
{
- UnpAudioBlock=UnpChannelDelta=UnpCurChannel=0;
+ TablesRead2=false;
+ UnpAudioBlock=false;
+ UnpChannelDelta=0;
+ UnpCurChannel=0;
UnpChannels=1;
memset(AudV,0,sizeof(AudV));
@@ -290,9 +296,12 @@ byte Unpack::DecodeAudio(int Delta)
int PCh=8*V->LastChar+V->K1*V->D1+V->K2*V->D2+V->K3*V->D3+V->K4*V->D4+V->K5*UnpChannelDelta;
PCh=(PCh>>3) & 0xFF;
- unsigned int Ch=PCh-Delta;
+ uint Ch=PCh-Delta;
- int D=((signed char)Delta)<<3;
+ int D=(signed char)Delta;
+ // Left shift of negative value is undefined behavior in C++,
+ // so we cast it to unsigned to follow the standard.
+ D=(uint)D<<3;
V->Dif[0]+=abs(D);
V->Dif[1]+=abs(D-V->D1);
@@ -311,9 +320,9 @@ byte Unpack::DecodeAudio(int Delta)
if ((V->ByteCount & 0x1F)==0)
{
- unsigned int MinDif=V->Dif[0],NumMinDif=0;
+ uint MinDif=V->Dif[0],NumMinDif=0;
V->Dif[0]=0;
- for (int I=1;I<sizeof(V->Dif)/sizeof(V->Dif[0]);I++)
+ for (uint I=1;I<ASIZE(V->Dif);I++)
{
if (V->Dif[I]<MinDif)
{
@@ -366,5 +375,5 @@ byte Unpack::DecodeAudio(int Delta)
break;
}
}
- return((byte)Ch);
+ return (byte)Ch;
}
diff --git a/unrar/unrar/unpack30.cpp b/unrar/unrar/unpack30.cpp
new file mode 100644
index 0000000..7c2adfa
--- /dev/null
+++ b/unrar/unrar/unpack30.cpp
@@ -0,0 +1,765 @@
+// We use it instead of direct PPM.DecodeChar call to be sure that
+// we reset PPM structures in case of corrupt data. It is important,
+// because these structures can be invalid after PPM.DecodeChar returned -1.
+inline int Unpack::SafePPMDecodeChar()
+{
+ int Ch=PPM.DecodeChar();
+ if (Ch==-1) // Corrupt PPM data found.
+ {
+ PPM.CleanUp(); // Reset possibly corrupt PPM data structures.
+ UnpBlockType=BLOCK_LZ; // Set faster and more fail proof LZ mode.
+ }
+ return(Ch);
+}
+
+
+void Unpack::Unpack29(bool Solid)
+{
+ static unsigned char LDecode[]={0,1,2,3,4,5,6,7,8,10,12,14,16,20,24,28,32,40,48,56,64,80,96,112,128,160,192,224};
+ static unsigned char LBits[]= {0,0,0,0,0,0,0,0,1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5};
+ static int DDecode[DC];
+ static byte DBits[DC];
+ static int DBitLengthCounts[]= {4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,14,0,12};
+ static unsigned char SDDecode[]={0,4,8,16,32,64,128,192};
+ static unsigned char SDBits[]= {2,2,3, 4, 5, 6, 6, 6};
+ unsigned int Bits;
+
+ if (DDecode[1]==0)
+ {
+ int Dist=0,BitLength=0,Slot=0;
+ for (int I=0;I<ASIZE(DBitLengthCounts);I++,BitLength++)
+ for (int J=0;J<DBitLengthCounts[I];J++,Slot++,Dist+=(1<<BitLength))
+ {
+ DDecode[Slot]=Dist;
+ DBits[Slot]=BitLength;
+ }
+ }
+
+ FileExtracted=true;
+
+ if (!Suspended)
+ {
+ UnpInitData(Solid);
+ if (!UnpReadBuf30())
+ return;
+ if ((!Solid || !TablesRead3) && !ReadTables30())
+ return;
+ }
+
+ while (true)
+ {
+ UnpPtr&=MaxWinMask;
+
+ if (Inp.InAddr>ReadBorder)
+ {
+ if (!UnpReadBuf30())
+ break;
+ }
+ if (((WrPtr-UnpPtr) & MaxWinMask)<=MAX3_INC_LZ_MATCH && WrPtr!=UnpPtr)
+ {
+ UnpWriteBuf30();
+ if (WrittenFileSize>DestUnpSize)
+ return;
+ if (Suspended)
+ {
+ FileExtracted=false;
+ return;
+ }
+ }
+ if (UnpBlockType==BLOCK_PPM)
+ {
+ // Here speed is critical, so we do not use SafePPMDecodeChar,
+ // because sometimes even the inline function can introduce
+ // some additional penalty.
+ int Ch=PPM.DecodeChar();
+ if (Ch==-1) // Corrupt PPM data found.
+ {
+ PPM.CleanUp(); // Reset possibly corrupt PPM data structures.
+ UnpBlockType=BLOCK_LZ; // Set faster and more fail proof LZ mode.
+ break;
+ }
+ if (Ch==PPMEscChar)
+ {
+ int NextCh=SafePPMDecodeChar();
+ if (NextCh==0) // End of PPM encoding.
+ {
+ if (!ReadTables30())
+ break;
+ continue;
+ }
+ if (NextCh==-1) // Corrupt PPM data found.
+ break;
+ if (NextCh==2) // End of file in PPM mode.
+ break;
+ if (NextCh==3) // Read VM code.
+ {
+ if (!ReadVMCodePPM())
+ break;
+ continue;
+ }
+ if (NextCh==4) // LZ inside of PPM.
+ {
+ unsigned int Distance=0,Length;
+ bool Failed=false;
+ for (int I=0;I<4 && !Failed;I++)
+ {
+ int Ch=SafePPMDecodeChar();
+ if (Ch==-1)
+ Failed=true;
+ else
+ if (I==3)
+ Length=(byte)Ch;
+ else
+ Distance=(Distance<<8)+(byte)Ch;
+ }
+ if (Failed)
+ break;
+
+ CopyString(Length+32,Distance+2);
+ continue;
+ }
+ if (NextCh==5) // One byte distance match (RLE) inside of PPM.
+ {
+ int Length=SafePPMDecodeChar();
+ if (Length==-1)
+ break;
+ CopyString(Length+4,1);
+ continue;
+ }
+ // If we are here, NextCh must be 1, what means that current byte
+ // is equal to our 'escape' byte, so we just store it to Window.
+ }
+ Window[UnpPtr++]=Ch;
+ continue;
+ }
+
+ uint Number=DecodeNumber(Inp,&BlockTables.LD);
+ if (Number<256)
+ {
+ Window[UnpPtr++]=(byte)Number;
+ continue;
+ }
+ if (Number>=271)
+ {
+ uint Length=LDecode[Number-=271]+3;
+ if ((Bits=LBits[Number])>0)
+ {
+ Length+=Inp.getbits()>>(16-Bits);
+ Inp.addbits(Bits);
+ }
+
+ uint DistNumber=DecodeNumber(Inp,&BlockTables.DD);
+ uint Distance=DDecode[DistNumber]+1;
+ if ((Bits=DBits[DistNumber])>0)
+ {
+ if (DistNumber>9)
+ {
+ if (Bits>4)
+ {
+ Distance+=((Inp.getbits()>>(20-Bits))<<4);
+ Inp.addbits(Bits-4);
+ }
+ if (LowDistRepCount>0)
+ {
+ LowDistRepCount--;
+ Distance+=PrevLowDist;
+ }
+ else
+ {
+ uint LowDist=DecodeNumber(Inp,&BlockTables.LDD);
+ if (LowDist==16)
+ {
+ LowDistRepCount=LOW_DIST_REP_COUNT-1;
+ Distance+=PrevLowDist;
+ }
+ else
+ {
+ Distance+=LowDist;
+ PrevLowDist=LowDist;
+ }
+ }
+ }
+ else
+ {
+ Distance+=Inp.getbits()>>(16-Bits);
+ Inp.addbits(Bits);
+ }
+ }
+
+ if (Distance>=0x2000)
+ {
+ Length++;
+ if (Distance>=0x40000)
+ Length++;
+ }
+
+ InsertOldDist(Distance);
+ LastLength=Length;
+ CopyString(Length,Distance);
+ continue;
+ }
+ if (Number==256)
+ {
+ if (!ReadEndOfBlock())
+ break;
+ continue;
+ }
+ if (Number==257)
+ {
+ if (!ReadVMCode())
+ break;
+ continue;
+ }
+ if (Number==258)
+ {
+ if (LastLength!=0)
+ CopyString(LastLength,OldDist[0]);
+ continue;
+ }
+ if (Number<263)
+ {
+ uint DistNum=Number-259;
+ uint Distance=OldDist[DistNum];
+ for (uint I=DistNum;I>0;I--)
+ OldDist[I]=OldDist[I-1];
+ OldDist[0]=Distance;
+
+ uint LengthNumber=DecodeNumber(Inp,&BlockTables.RD);
+ int Length=LDecode[LengthNumber]+2;
+ if ((Bits=LBits[LengthNumber])>0)
+ {
+ Length+=Inp.getbits()>>(16-Bits);
+ Inp.addbits(Bits);
+ }
+ LastLength=Length;
+ CopyString(Length,Distance);
+ continue;
+ }
+ if (Number<272)
+ {
+ uint Distance=SDDecode[Number-=263]+1;
+ if ((Bits=SDBits[Number])>0)
+ {
+ Distance+=Inp.getbits()>>(16-Bits);
+ Inp.addbits(Bits);
+ }
+ InsertOldDist(Distance);
+ LastLength=2;
+ CopyString(2,Distance);
+ continue;
+ }
+ }
+ UnpWriteBuf30();
+}
+
+
+// Return 'false' to quit unpacking the current file or 'true' to continue.
+bool Unpack::ReadEndOfBlock()
+{
+ uint BitField=Inp.getbits();
+ bool NewTable,NewFile=false;
+
+ // "1" - no new file, new table just here.
+ // "00" - new file, no new table.
+ // "01" - new file, new table (in beginning of next file).
+
+ if ((BitField & 0x8000)!=0)
+ {
+ NewTable=true;
+ Inp.addbits(1);
+ }
+ else
+ {
+ NewFile=true;
+ NewTable=(BitField & 0x4000)!=0;
+ Inp.addbits(2);
+ }
+ TablesRead3=!NewTable;
+
+ // Quit immediately if "new file" flag is set. If "new table" flag
+ // is present, we'll read the table in beginning of next file
+ // based on 'TablesRead3' 'false' value.
+ if (NewFile)
+ return false;
+ return ReadTables30(); // Quit only if we failed to read tables.
+}
+
+
+bool Unpack::ReadVMCode()
+{
+ // Entire VM code is guaranteed to fully present in block defined
+ // by current Huffman table. Compressor checks that VM code does not cross
+ // Huffman block boundaries.
+ uint FirstByte=Inp.getbits()>>8;
+ Inp.addbits(8);
+ uint Length=(FirstByte & 7)+1;
+ if (Length==7)
+ {
+ Length=(Inp.getbits()>>8)+7;
+ Inp.addbits(8);
+ }
+ else
+ if (Length==8)
+ {
+ Length=Inp.getbits();
+ Inp.addbits(16);
+ }
+ if (Length==0)
+ return false;
+ Array<byte> VMCode(Length);
+ for (uint I=0;I<Length;I++)
+ {
+ // Try to read the new buffer if only one byte is left.
+ // But if we read all bytes except the last, one byte is enough.
+ if (Inp.InAddr>=ReadTop-1 && !UnpReadBuf30() && I<Length-1)
+ return false;
+ VMCode[I]=Inp.getbits()>>8;
+ Inp.addbits(8);
+ }
+ return AddVMCode(FirstByte,&VMCode[0],Length);
+}
+
+
+bool Unpack::ReadVMCodePPM()
+{
+ uint FirstByte=SafePPMDecodeChar();
+ if ((int)FirstByte==-1)
+ return false;
+ uint Length=(FirstByte & 7)+1;
+ if (Length==7)
+ {
+ int B1=SafePPMDecodeChar();
+ if (B1==-1)
+ return false;
+ Length=B1+7;
+ }
+ else
+ if (Length==8)
+ {
+ int B1=SafePPMDecodeChar();
+ if (B1==-1)
+ return false;
+ int B2=SafePPMDecodeChar();
+ if (B2==-1)
+ return false;
+ Length=B1*256+B2;
+ }
+ if (Length==0)
+ return false;
+ Array<byte> VMCode(Length);
+ for (uint I=0;I<Length;I++)
+ {
+ int Ch=SafePPMDecodeChar();
+ if (Ch==-1)
+ return false;
+ VMCode[I]=Ch;
+ }
+ return AddVMCode(FirstByte,&VMCode[0],Length);
+}
+
+
+bool Unpack::AddVMCode(uint FirstByte,byte *Code,uint CodeSize)
+{
+ VMCodeInp.InitBitInput();
+ memcpy(VMCodeInp.InBuf,Code,Min(BitInput::MAX_SIZE,CodeSize));
+ VM.Init();
+
+ uint FiltPos;
+ if ((FirstByte & 0x80)!=0)
+ {
+ FiltPos=RarVM::ReadData(VMCodeInp);
+ if (FiltPos==0)
+ InitFilters30(false);
+ else
+ FiltPos--;
+ }
+ else
+ FiltPos=LastFilter; // Use the same filter as last time.
+
+ if (FiltPos>Filters30.Size() || FiltPos>OldFilterLengths.Size())
+ return false;
+ LastFilter=FiltPos;
+ bool NewFilter=(FiltPos==Filters30.Size());
+
+ UnpackFilter30 *StackFilter=new UnpackFilter30; // New filter for PrgStack.
+
+ UnpackFilter30 *Filter;
+ if (NewFilter) // New filter code, never used before since VM reset.
+ {
+ if (FiltPos>MAX3_UNPACK_FILTERS)
+ {
+ // Too many different filters, corrupt archive.
+ delete StackFilter;
+ return false;
+ }
+
+ Filters30.Add(1);
+ Filters30[Filters30.Size()-1]=Filter=new UnpackFilter30;
+ StackFilter->ParentFilter=(uint)(Filters30.Size()-1);
+
+ // Reserve one item to store the data block length of our new filter
+ // entry. We'll set it to real block length below, after reading it.
+ // But we need to initialize it now, because when processing corrupt
+ // data, we can access this item even before we set it to real value.
+ OldFilterLengths.Push(0);
+ }
+ else // Filter was used in the past.
+ {
+ Filter=Filters30[FiltPos];
+ StackFilter->ParentFilter=FiltPos;
+ }
+
+ uint EmptyCount=0;
+ for (uint I=0;I<PrgStack.Size();I++)
+ {
+ PrgStack[I-EmptyCount]=PrgStack[I];
+ if (PrgStack[I]==NULL)
+ EmptyCount++;
+ if (EmptyCount>0)
+ PrgStack[I]=NULL;
+ }
+ if (EmptyCount==0)
+ {
+ if (PrgStack.Size()>MAX3_UNPACK_FILTERS)
+ {
+ delete StackFilter;
+ return false;
+ }
+ PrgStack.Add(1);
+ EmptyCount=1;
+ }
+ size_t StackPos=PrgStack.Size()-EmptyCount;
+ PrgStack[StackPos]=StackFilter;
+
+ uint BlockStart=RarVM::ReadData(VMCodeInp);
+ if ((FirstByte & 0x40)!=0)
+ BlockStart+=258;
+ StackFilter->BlockStart=(uint)((BlockStart+UnpPtr)&MaxWinMask);
+ if ((FirstByte & 0x20)!=0)
+ {
+ StackFilter->BlockLength=RarVM::ReadData(VMCodeInp);
+
+ // Store the last data block length for current filter.
+ OldFilterLengths[FiltPos]=StackFilter->BlockLength;
+ }
+ else
+ {
+ // Set the data block size to same value as the previous block size
+ // for same filter. It is possible for corrupt data to access a new
+ // and not filled yet item of OldFilterLengths array here. This is why
+ // we set new OldFilterLengths items to zero above.
+ StackFilter->BlockLength=FiltPos<OldFilterLengths.Size() ? OldFilterLengths[FiltPos]:0;
+ }
+
+ StackFilter->NextWindow=WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MaxWinMask)<=BlockStart;
+
+// DebugLog("\nNextWindow: UnpPtr=%08x WrPtr=%08x BlockStart=%08x",UnpPtr,WrPtr,BlockStart);
+
+ memset(StackFilter->Prg.InitR,0,sizeof(StackFilter->Prg.InitR));
+ StackFilter->Prg.InitR[4]=StackFilter->BlockLength;
+
+ if ((FirstByte & 0x10)!=0) // Set registers to optional parameters if any.
+ {
+ uint InitMask=VMCodeInp.fgetbits()>>9;
+ VMCodeInp.faddbits(7);
+ for (uint I=0;I<7;I++)
+ if (InitMask & (1<<I))
+ StackFilter->Prg.InitR[I]=RarVM::ReadData(VMCodeInp);
+ }
+
+ if (NewFilter)
+ {
+ uint VMCodeSize=RarVM::ReadData(VMCodeInp);
+ if (VMCodeSize>=0x10000 || VMCodeSize==0 || VMCodeInp.InAddr+VMCodeSize>CodeSize)
+ return false;
+ Array<byte> VMCode(VMCodeSize);
+ for (uint I=0;I<VMCodeSize;I++)
+ {
+ if (VMCodeInp.Overflow(3))
+ return false;
+ VMCode[I]=VMCodeInp.fgetbits()>>8;
+ VMCodeInp.faddbits(8);
+ }
+ VM.Prepare(&VMCode[0],VMCodeSize,&Filter->Prg);
+ }
+ StackFilter->Prg.Type=Filter->Prg.Type;
+
+ return true;
+}
+
+
+bool Unpack::UnpReadBuf30()
+{
+ int DataSize=ReadTop-Inp.InAddr; // Data left to process.
+ if (DataSize<0)
+ return false;
+ if (Inp.InAddr>BitInput::MAX_SIZE/2)
+ {
+ // If we already processed more than half of buffer, let's move
+ // remaining data into beginning to free more space for new data
+ // and ensure that calling function does not cross the buffer border
+ // even if we did not read anything here. Also it ensures that read size
+ // is not less than CRYPT_BLOCK_SIZE, so we can align it without risk
+ // to make it zero.
+ if (DataSize>0)
+ memmove(Inp.InBuf,Inp.InBuf+Inp.InAddr,DataSize);
+ Inp.InAddr=0;
+ ReadTop=DataSize;
+ }
+ else
+ DataSize=ReadTop;
+ int ReadCode=UnpIO->UnpRead(Inp.InBuf+DataSize,BitInput::MAX_SIZE-DataSize);
+ if (ReadCode>0)
+ ReadTop+=ReadCode;
+ ReadBorder=ReadTop-30;
+ return ReadCode!=-1;
+}
+
+
+void Unpack::UnpWriteBuf30()
+{
+ uint WrittenBorder=(uint)WrPtr;
+ uint WriteSize=(uint)((UnpPtr-WrittenBorder)&MaxWinMask);
+ for (size_t I=0;I<PrgStack.Size();I++)
+ {
+ // Here we apply filters to data which we need to write.
+ // We always copy data to virtual machine memory before processing.
+ // We cannot process them just in place in Window buffer, because
+ // these data can be used for future string matches, so we must
+ // preserve them in original form.
+
+ UnpackFilter30 *flt=PrgStack[I];
+ if (flt==NULL)
+ continue;
+ if (flt->NextWindow)
+ {
+ flt->NextWindow=false;
+ continue;
+ }
+ unsigned int BlockStart=flt->BlockStart;
+ unsigned int BlockLength=flt->BlockLength;
+ if (((BlockStart-WrittenBorder)&MaxWinMask)<WriteSize)
+ {
+ if (WrittenBorder!=BlockStart)
+ {
+ UnpWriteArea(WrittenBorder,BlockStart);
+ WrittenBorder=BlockStart;
+ WriteSize=(uint)((UnpPtr-WrittenBorder)&MaxWinMask);
+ }
+ if (BlockLength<=WriteSize)
+ {
+ uint BlockEnd=(BlockStart+BlockLength)&MaxWinMask;
+ if (BlockStart<BlockEnd || BlockEnd==0)
+ VM.SetMemory(0,Window+BlockStart,BlockLength);
+ else
+ {
+ uint FirstPartLength=uint(MaxWinSize-BlockStart);
+ VM.SetMemory(0,Window+BlockStart,FirstPartLength);
+ VM.SetMemory(FirstPartLength,Window,BlockEnd);
+ }
+
+ VM_PreparedProgram *ParentPrg=&Filters30[flt->ParentFilter]->Prg;
+ VM_PreparedProgram *Prg=&flt->Prg;
+
+ ExecuteCode(Prg);
+
+ byte *FilteredData=Prg->FilteredData;
+ unsigned int FilteredDataSize=Prg->FilteredDataSize;
+
+ delete PrgStack[I];
+ PrgStack[I]=NULL;
+ while (I+1<PrgStack.Size())
+ {
+ UnpackFilter30 *NextFilter=PrgStack[I+1];
+ // It is required to check NextWindow here.
+ if (NextFilter==NULL || NextFilter->BlockStart!=BlockStart ||
+ NextFilter->BlockLength!=FilteredDataSize || NextFilter->NextWindow)
+ break;
+
+ // Apply several filters to same data block.
+
+ VM.SetMemory(0,FilteredData,FilteredDataSize);
+
+ VM_PreparedProgram *ParentPrg=&Filters30[NextFilter->ParentFilter]->Prg;
+ VM_PreparedProgram *NextPrg=&NextFilter->Prg;
+
+ ExecuteCode(NextPrg);
+
+ FilteredData=NextPrg->FilteredData;
+ FilteredDataSize=NextPrg->FilteredDataSize;
+ I++;
+ delete PrgStack[I];
+ PrgStack[I]=NULL;
+ }
+ UnpIO->UnpWrite(FilteredData,FilteredDataSize);
+ UnpSomeRead=true;
+ WrittenFileSize+=FilteredDataSize;
+ WrittenBorder=BlockEnd;
+ WriteSize=uint((UnpPtr-WrittenBorder)&MaxWinMask);
+ }
+ else
+ {
+ // Current filter intersects the window write border, so we adjust
+ // the window border to process this filter next time, not now.
+ for (size_t J=I;J<PrgStack.Size();J++)
+ {
+ UnpackFilter30 *flt=PrgStack[J];
+ if (flt!=NULL && flt->NextWindow)
+ flt->NextWindow=false;
+ }
+ WrPtr=WrittenBorder;
+ return;
+ }
+ }
+ }
+
+ UnpWriteArea(WrittenBorder,UnpPtr);
+ WrPtr=UnpPtr;
+}
+
+
+void Unpack::ExecuteCode(VM_PreparedProgram *Prg)
+{
+ Prg->InitR[6]=(uint)WrittenFileSize;
+ VM.Execute(Prg);
+}
+
+
+bool Unpack::ReadTables30()
+{
+ byte BitLength[BC];
+ byte Table[HUFF_TABLE_SIZE30];
+ if (Inp.InAddr>ReadTop-25)
+ if (!UnpReadBuf30())
+ return(false);
+ Inp.faddbits((8-Inp.InBit)&7);
+ uint BitField=Inp.fgetbits();
+ if (BitField & 0x8000)
+ {
+ UnpBlockType=BLOCK_PPM;
+ return(PPM.DecodeInit(this,PPMEscChar));
+ }
+ UnpBlockType=BLOCK_LZ;
+
+ PrevLowDist=0;
+ LowDistRepCount=0;
+
+ if (!(BitField & 0x4000))
+ memset(UnpOldTable,0,sizeof(UnpOldTable));
+ Inp.faddbits(2);
+
+ for (uint I=0;I<BC;I++)
+ {
+ uint Length=(byte)(Inp.fgetbits() >> 12);
+ Inp.faddbits(4);
+ if (Length==15)
+ {
+ uint ZeroCount=(byte)(Inp.fgetbits() >> 12);
+ Inp.faddbits(4);
+ if (ZeroCount==0)
+ BitLength[I]=15;
+ else
+ {
+ ZeroCount+=2;
+ while (ZeroCount-- > 0 && I<ASIZE(BitLength))
+ BitLength[I++]=0;
+ I--;
+ }
+ }
+ else
+ BitLength[I]=Length;
+ }
+ MakeDecodeTables(BitLength,&BlockTables.BD,BC30);
+
+ const uint TableSize=HUFF_TABLE_SIZE30;
+ for (uint I=0;I<TableSize;)
+ {
+ if (Inp.InAddr>ReadTop-5)
+ if (!UnpReadBuf30())
+ return(false);
+ uint Number=DecodeNumber(Inp,&BlockTables.BD);
+ if (Number<16)
+ {
+ Table[I]=(Number+UnpOldTable[I]) & 0xf;
+ I++;
+ }
+ else
+ if (Number<18)
+ {
+ uint N;
+ if (Number==16)
+ {
+ N=(Inp.fgetbits() >> 13)+3;
+ Inp.faddbits(3);
+ }
+ else
+ {
+ N=(Inp.fgetbits() >> 9)+11;
+ Inp.faddbits(7);
+ }
+ if (I==0)
+ return false; // We cannot have "repeat previous" code at the first position.
+ else
+ while (N-- > 0 && I<TableSize)
+ {
+ Table[I]=Table[I-1];
+ I++;
+ }
+ }
+ else
+ {
+ uint N;
+ if (Number==18)
+ {
+ N=(Inp.fgetbits() >> 13)+3;
+ Inp.faddbits(3);
+ }
+ else
+ {
+ N=(Inp.fgetbits() >> 9)+11;
+ Inp.faddbits(7);
+ }
+ while (N-- > 0 && I<TableSize)
+ Table[I++]=0;
+ }
+ }
+ TablesRead3=true;
+ if (Inp.InAddr>ReadTop)
+ return false;
+ MakeDecodeTables(&Table[0],&BlockTables.LD,NC30);
+ MakeDecodeTables(&Table[NC30],&BlockTables.DD,DC30);
+ MakeDecodeTables(&Table[NC30+DC30],&BlockTables.LDD,LDC30);
+ MakeDecodeTables(&Table[NC30+DC30+LDC30],&BlockTables.RD,RC30);
+ memcpy(UnpOldTable,Table,sizeof(UnpOldTable));
+ return true;
+}
+
+
+void Unpack::UnpInitData30(bool Solid)
+{
+ if (!Solid)
+ {
+ TablesRead3=false;
+ memset(UnpOldTable,0,sizeof(UnpOldTable));
+ PPMEscChar=2;
+ UnpBlockType=BLOCK_LZ;
+ }
+ InitFilters30(Solid);
+}
+
+
+void Unpack::InitFilters30(bool Solid)
+{
+ if (!Solid)
+ {
+ OldFilterLengths.SoftReset();
+ LastFilter=0;
+
+ for (size_t I=0;I<Filters30.Size();I++)
+ delete Filters30[I];
+ Filters30.SoftReset();
+ }
+ for (size_t I=0;I<PrgStack.Size();I++)
+ delete PrgStack[I];
+ PrgStack.SoftReset();
+}
diff --git a/unrar/unrar/unpack50.cpp b/unrar/unrar/unpack50.cpp
new file mode 100644
index 0000000..e040907
--- /dev/null
+++ b/unrar/unrar/unpack50.cpp
@@ -0,0 +1,687 @@
+void Unpack::Unpack5(bool Solid)
+{
+ FileExtracted=true;
+
+ if (!Suspended)
+ {
+ UnpInitData(Solid);
+ if (!UnpReadBuf())
+ return;
+
+ // Check TablesRead5 to be sure that we read tables at least once
+ // regardless of current block header TablePresent flag.
+ // So we can safefly use these tables below.
+ if (!ReadBlockHeader(Inp,BlockHeader) ||
+ !ReadTables(Inp,BlockHeader,BlockTables) || !TablesRead5)
+ return;
+ }
+
+ while (true)
+ {
+ UnpPtr&=MaxWinMask;
+
+ if (Inp.InAddr>=ReadBorder)
+ {
+ bool FileDone=false;
+
+ // We use 'while', because for empty block containing only Huffman table,
+ // we'll be on the block border once again just after reading the table.
+ while (Inp.InAddr>BlockHeader.BlockStart+BlockHeader.BlockSize-1 ||
+ Inp.InAddr==BlockHeader.BlockStart+BlockHeader.BlockSize-1 &&
+ Inp.InBit>=BlockHeader.BlockBitSize)
+ {
+ if (BlockHeader.LastBlockInFile)
+ {
+ FileDone=true;
+ break;
+ }
+ if (!ReadBlockHeader(Inp,BlockHeader) || !ReadTables(Inp,BlockHeader,BlockTables))
+ return;
+ }
+ if (FileDone || !UnpReadBuf())
+ break;
+ }
+
+ if (((WriteBorder-UnpPtr) & MaxWinMask)<=MAX_INC_LZ_MATCH && WriteBorder!=UnpPtr)
+ {
+ UnpWriteBuf();
+ if (WrittenFileSize>DestUnpSize)
+ return;
+ if (Suspended)
+ {
+ FileExtracted=false;
+ return;
+ }
+ }
+
+ uint MainSlot=DecodeNumber(Inp,&BlockTables.LD);
+ if (MainSlot<256)
+ {
+ if (Fragmented)
+ FragWindow[UnpPtr++]=(byte)MainSlot;
+ else
+ Window[UnpPtr++]=(byte)MainSlot;
+ continue;
+ }
+ if (MainSlot>=262)
+ {
+ uint Length=SlotToLength(Inp,MainSlot-262);
+
+ uint DBits,Distance=1,DistSlot=DecodeNumber(Inp,&BlockTables.DD);
+ if (DistSlot<4)
+ {
+ DBits=0;
+ Distance+=DistSlot;
+ }
+ else
+ {
+ DBits=DistSlot/2 - 1;
+ Distance+=(2 | (DistSlot & 1)) << DBits;
+ }
+
+ if (DBits>0)
+ {
+ if (DBits>=4)
+ {
+ if (DBits>4)
+ {
+ Distance+=((Inp.getbits32()>>(36-DBits))<<4);
+ Inp.addbits(DBits-4);
+ }
+ uint LowDist=DecodeNumber(Inp,&BlockTables.LDD);
+ Distance+=LowDist;
+ }
+ else
+ {
+ Distance+=Inp.getbits()>>(16-DBits);
+ Inp.addbits(DBits);
+ }
+ }
+
+ if (Distance>0x100)
+ {
+ Length++;
+ if (Distance>0x2000)
+ {
+ Length++;
+ if (Distance>0x40000)
+ Length++;
+ }
+ }
+
+ InsertOldDist(Distance);
+ LastLength=Length;
+ if (Fragmented)
+ FragWindow.CopyString(Length,Distance,UnpPtr,MaxWinMask);
+ else
+ CopyString(Length,Distance);
+ continue;
+ }
+ if (MainSlot==256)
+ {
+ UnpackFilter Filter;
+ if (!ReadFilter(Inp,Filter) || !AddFilter(Filter))
+ break;
+ continue;
+ }
+ if (MainSlot==257)
+ {
+ if (LastLength!=0)
+ if (Fragmented)
+ FragWindow.CopyString(LastLength,OldDist[0],UnpPtr,MaxWinMask);
+ else
+ CopyString(LastLength,OldDist[0]);
+ continue;
+ }
+ if (MainSlot<262)
+ {
+ uint DistNum=MainSlot-258;
+ uint Distance=OldDist[DistNum];
+ for (uint I=DistNum;I>0;I--)
+ OldDist[I]=OldDist[I-1];
+ OldDist[0]=Distance;
+
+ uint LengthSlot=DecodeNumber(Inp,&BlockTables.RD);
+ uint Length=SlotToLength(Inp,LengthSlot);
+ LastLength=Length;
+ if (Fragmented)
+ FragWindow.CopyString(Length,Distance,UnpPtr,MaxWinMask);
+ else
+ CopyString(Length,Distance);
+ continue;
+ }
+ }
+ UnpWriteBuf();
+}
+
+
+uint Unpack::ReadFilterData(BitInput &Inp)
+{
+ uint ByteCount=(Inp.fgetbits()>>14)+1;
+ Inp.addbits(2);
+
+ uint Data=0;
+ for (uint I=0;I<ByteCount;I++)
+ {
+ Data+=(Inp.fgetbits()>>8)<<(I*8);
+ Inp.addbits(8);
+ }
+ return Data;
+}
+
+
+bool Unpack::ReadFilter(BitInput &Inp,UnpackFilter &Filter)
+{
+ if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-16)
+ if (!UnpReadBuf())
+ return false;
+
+ Filter.BlockStart=ReadFilterData(Inp);
+ Filter.BlockLength=ReadFilterData(Inp);
+ if (Filter.BlockLength>MAX_FILTER_BLOCK_SIZE)
+ Filter.BlockLength=0;
+
+ Filter.Type=Inp.fgetbits()>>13;
+ Inp.faddbits(3);
+
+ if (Filter.Type==FILTER_DELTA)
+ {
+ Filter.Channels=(Inp.fgetbits()>>11)+1;
+ Inp.faddbits(5);
+ }
+
+ return true;
+}
+
+
+bool Unpack::AddFilter(UnpackFilter &Filter)
+{
+ if (Filters.Size()>=MAX_UNPACK_FILTERS)
+ {
+ UnpWriteBuf(); // Write data, apply and flush filters.
+ if (Filters.Size()>=MAX_UNPACK_FILTERS)
+ InitFilters(); // Still too many filters, prevent excessive memory use.
+ }
+
+ // If distance to filter start is that large that due to circular dictionary
+ // mode now it points to old not written yet data, then we set 'NextWindow'
+ // flag and process this filter only after processing that older data.
+ Filter.NextWindow=WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MaxWinMask)<=Filter.BlockStart;
+
+ Filter.BlockStart=uint((Filter.BlockStart+UnpPtr)&MaxWinMask);
+ Filters.Push(Filter);
+ return true;
+}
+
+
+bool Unpack::UnpReadBuf()
+{
+ int DataSize=ReadTop-Inp.InAddr; // Data left to process.
+ if (DataSize<0)
+ return false;
+ BlockHeader.BlockSize-=Inp.InAddr-BlockHeader.BlockStart;
+ if (Inp.InAddr>BitInput::MAX_SIZE/2)
+ {
+ // If we already processed more than half of buffer, let's move
+ // remaining data into beginning to free more space for new data
+ // and ensure that calling function does not cross the buffer border
+ // even if we did not read anything here. Also it ensures that read size
+ // is not less than CRYPT_BLOCK_SIZE, so we can align it without risk
+ // to make it zero.
+ if (DataSize>0)
+ memmove(Inp.InBuf,Inp.InBuf+Inp.InAddr,DataSize);
+ Inp.InAddr=0;
+ ReadTop=DataSize;
+ }
+ else
+ DataSize=ReadTop;
+ int ReadCode=0;
+ if (BitInput::MAX_SIZE!=DataSize)
+ ReadCode=UnpIO->UnpRead(Inp.InBuf+DataSize,BitInput::MAX_SIZE-DataSize);
+ if (ReadCode>0) // Can be also -1.
+ ReadTop+=ReadCode;
+ ReadBorder=ReadTop-30;
+ BlockHeader.BlockStart=Inp.InAddr;
+ if (BlockHeader.BlockSize!=-1) // '-1' means not defined yet.
+ {
+ // We may need to quit from main extraction loop and read new block header
+ // and trees earlier than data in input buffer ends.
+ ReadBorder=Min(ReadBorder,BlockHeader.BlockStart+BlockHeader.BlockSize-1);
+ }
+ return ReadCode!=-1;
+}
+
+
+void Unpack::UnpWriteBuf()
+{
+ size_t WrittenBorder=WrPtr;
+ size_t FullWriteSize=(UnpPtr-WrittenBorder)&MaxWinMask;
+ size_t WriteSizeLeft=FullWriteSize;
+ bool NotAllFiltersProcessed=false;
+ for (size_t I=0;I<Filters.Size();I++)
+ {
+ // Here we apply filters to data which we need to write.
+ // We always copy data to another memory block before processing.
+ // We cannot process them just in place in Window buffer, because
+ // these data can be used for future string matches, so we must
+ // preserve them in original form.
+
+ UnpackFilter *flt=&Filters[I];
+ if (flt->Type==FILTER_NONE)
+ continue;
+ if (flt->NextWindow)
+ {
+ // Here we skip filters which have block start in current data range
+ // due to address wrap around in circular dictionary, but actually
+ // belong to next dictionary block. If such filter start position
+ // is included to current write range, then we reset 'NextWindow' flag.
+ // In fact we can reset it even without such check, because current
+ // implementation seems to guarantee 'NextWindow' flag reset after
+ // buffer writing for all existing filters. But let's keep this check
+ // just in case. Compressor guarantees that distance between
+ // filter block start and filter storing position cannot exceed
+ // the dictionary size. So if we covered the filter block start with
+ // our write here, we can safely assume that filter is applicable
+ // to next block on no further wrap arounds is possible.
+ if (((flt->BlockStart-WrPtr)&MaxWinMask)<=FullWriteSize)
+ flt->NextWindow=false;
+ continue;
+ }
+ uint BlockStart=flt->BlockStart;
+ uint BlockLength=flt->BlockLength;
+ if (((BlockStart-WrittenBorder)&MaxWinMask)<WriteSizeLeft)
+ {
+ if (WrittenBorder!=BlockStart)
+ {
+ UnpWriteArea(WrittenBorder,BlockStart);
+ WrittenBorder=BlockStart;
+ WriteSizeLeft=(UnpPtr-WrittenBorder)&MaxWinMask;
+ }
+ if (BlockLength<=WriteSizeLeft)
+ {
+ if (BlockLength>0) // We set it to 0 also for invalid filters.
+ {
+ uint BlockEnd=(BlockStart+BlockLength)&MaxWinMask;
+
+ FilterSrcMemory.Alloc(BlockLength);
+ byte *Mem=&FilterSrcMemory[0];
+ if (BlockStart<BlockEnd || BlockEnd==0)
+ {
+ if (Fragmented)
+ FragWindow.CopyData(Mem,BlockStart,BlockLength);
+ else
+ memcpy(Mem,Window+BlockStart,BlockLength);
+ }
+ else
+ {
+ size_t FirstPartLength=size_t(MaxWinSize-BlockStart);
+ if (Fragmented)
+ {
+ FragWindow.CopyData(Mem,BlockStart,FirstPartLength);
+ FragWindow.CopyData(Mem+FirstPartLength,0,BlockEnd);
+ }
+ else
+ {
+ memcpy(Mem,Window+BlockStart,FirstPartLength);
+ memcpy(Mem+FirstPartLength,Window,BlockEnd);
+ }
+ }
+
+ byte *OutMem=ApplyFilter(Mem,BlockLength,flt);
+
+ Filters[I].Type=FILTER_NONE;
+
+ if (OutMem!=NULL)
+ UnpIO->UnpWrite(OutMem,BlockLength);
+
+ UnpSomeRead=true;
+ WrittenFileSize+=BlockLength;
+ WrittenBorder=BlockEnd;
+ WriteSizeLeft=(UnpPtr-WrittenBorder)&MaxWinMask;
+ }
+ }
+ else
+ {
+ // Current filter intersects the window write border, so we adjust
+ // the window border to process this filter next time, not now.
+ WrPtr=WrittenBorder;
+
+ // Since Filter start position can only increase, we quit processing
+ // all following filters for this data block and reset 'NextWindow'
+ // flag for them.
+ for (size_t J=I;J<Filters.Size();J++)
+ {
+ UnpackFilter *flt=&Filters[J];
+ if (flt->Type!=FILTER_NONE)
+ flt->NextWindow=false;
+ }
+
+ // Do not write data left after current filter now.
+ NotAllFiltersProcessed=true;
+ break;
+ }
+ }
+ }
+
+ // Remove processed filters from queue.
+ size_t EmptyCount=0;
+ for (size_t I=0;I<Filters.Size();I++)
+ {
+ if (EmptyCount>0)
+ Filters[I-EmptyCount]=Filters[I];
+ if (Filters[I].Type==FILTER_NONE)
+ EmptyCount++;
+ }
+ if (EmptyCount>0)
+ Filters.Alloc(Filters.Size()-EmptyCount);
+
+ if (!NotAllFiltersProcessed) // Only if all filters are processed.
+ {
+ // Write data left after last filter.
+ UnpWriteArea(WrittenBorder,UnpPtr);
+ WrPtr=UnpPtr;
+ }
+
+ // We prefer to write data in blocks not exceeding UNPACK_MAX_WRITE
+ // instead of potentially huge MaxWinSize blocks. It also allows us
+ // to keep the size of Filters array reasonable.
+ WriteBorder=(UnpPtr+Min(MaxWinSize,UNPACK_MAX_WRITE))&MaxWinMask;
+
+ // Choose the nearest among WriteBorder and WrPtr actual written border.
+ // If border is equal to UnpPtr, it means that we have MaxWinSize data ahead.
+ if (WriteBorder==UnpPtr ||
+ WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MaxWinMask)<((WriteBorder-UnpPtr)&MaxWinMask))
+ WriteBorder=WrPtr;
+}
+
+
+byte* Unpack::ApplyFilter(byte *Data,uint DataSize,UnpackFilter *Flt)
+{
+ byte *SrcData=Data;
+ switch(Flt->Type)
+ {
+ case FILTER_E8:
+ case FILTER_E8E9:
+ {
+ uint FileOffset=(uint)WrittenFileSize;
+
+ const uint FileSize=0x1000000;
+ byte CmpByte2=Flt->Type==FILTER_E8E9 ? 0xe9:0xe8;
+ // DataSize is unsigned, so we use "CurPos+4" and not "DataSize-4"
+ // to avoid overflow for DataSize<4.
+ for (uint CurPos=0;CurPos+4<DataSize;)
+ {
+ byte CurByte=*(Data++);
+ CurPos++;
+ if (CurByte==0xe8 || CurByte==CmpByte2)
+ {
+ uint Offset=(CurPos+FileOffset)%FileSize;
+ uint Addr=RawGet4(Data);
+
+ // We check 0x80000000 bit instead of '< 0' comparison
+ // not assuming int32 presence or uint size and endianness.
+ if ((Addr & 0x80000000)!=0) // Addr<0
+ {
+ if (((Addr+Offset) & 0x80000000)==0) // Addr+Offset>=0
+ RawPut4(Addr+FileSize,Data);
+ }
+ else
+ if (((Addr-FileSize) & 0x80000000)!=0) // Addr<FileSize
+ RawPut4(Addr-Offset,Data);
+
+ Data+=4;
+ CurPos+=4;
+ }
+ }
+ }
+ return SrcData;
+ case FILTER_ARM:
+ // 2019-11-15: we turned off ARM filter by default when compressing,
+ // mostly because it is inefficient for modern 64 bit ARM binaries.
+ // It was turned on by default in 5.0 - 5.80b3 , so we still need it
+ // here for compatibility with some of previously created archives.
+ {
+ uint FileOffset=(uint)WrittenFileSize;
+ // DataSize is unsigned, so we use "CurPos+3" and not "DataSize-3"
+ // to avoid overflow for DataSize<3.
+ for (uint CurPos=0;CurPos+3<DataSize;CurPos+=4)
+ {
+ byte *D=Data+CurPos;
+ if (D[3]==0xeb) // BL command with '1110' (Always) condition.
+ {
+ uint Offset=D[0]+uint(D[1])*0x100+uint(D[2])*0x10000;
+ Offset-=(FileOffset+CurPos)/4;
+ D[0]=(byte)Offset;
+ D[1]=(byte)(Offset>>8);
+ D[2]=(byte)(Offset>>16);
+ }
+ }
+ }
+ return SrcData;
+ case FILTER_DELTA:
+ {
+ // Unlike RAR3, we do not need to reject excessive channel
+ // values here, since RAR5 uses only 5 bits to store channel.
+ uint Channels=Flt->Channels,SrcPos=0;
+
+ FilterDstMemory.Alloc(DataSize);
+ byte *DstData=&FilterDstMemory[0];
+
+ // Bytes from same channels are grouped to continual data blocks,
+ // so we need to place them back to their interleaving positions.
+ for (uint CurChannel=0;CurChannel<Channels;CurChannel++)
+ {
+ byte PrevByte=0;
+ for (uint DestPos=CurChannel;DestPos<DataSize;DestPos+=Channels)
+ DstData[DestPos]=(PrevByte-=Data[SrcPos++]);
+ }
+ return DstData;
+ }
+
+ }
+ return NULL;
+}
+
+
+void Unpack::UnpWriteArea(size_t StartPtr,size_t EndPtr)
+{
+ if (EndPtr!=StartPtr)
+ UnpSomeRead=true;
+ if (EndPtr<StartPtr)
+ UnpAllBuf=true;
+
+ if (Fragmented)
+ {
+ size_t SizeToWrite=(EndPtr-StartPtr) & MaxWinMask;
+ while (SizeToWrite>0)
+ {
+ size_t BlockSize=FragWindow.GetBlockSize(StartPtr,SizeToWrite);
+ UnpWriteData(&FragWindow[StartPtr],BlockSize);
+ SizeToWrite-=BlockSize;
+ StartPtr=(StartPtr+BlockSize) & MaxWinMask;
+ }
+ }
+ else
+ if (EndPtr<StartPtr)
+ {
+ UnpWriteData(Window+StartPtr,MaxWinSize-StartPtr);
+ UnpWriteData(Window,EndPtr);
+ }
+ else
+ UnpWriteData(Window+StartPtr,EndPtr-StartPtr);
+}
+
+
+void Unpack::UnpWriteData(byte *Data,size_t Size)
+{
+ if (WrittenFileSize>=DestUnpSize)
+ return;
+ size_t WriteSize=Size;
+ int64 LeftToWrite=DestUnpSize-WrittenFileSize;
+ if ((int64)WriteSize>LeftToWrite)
+ WriteSize=(size_t)LeftToWrite;
+ UnpIO->UnpWrite(Data,WriteSize);
+ WrittenFileSize+=Size;
+}
+
+
+void Unpack::UnpInitData50(bool Solid)
+{
+ if (!Solid)
+ TablesRead5=false;
+}
+
+
+bool Unpack::ReadBlockHeader(BitInput &Inp,UnpackBlockHeader &Header)
+{
+ Header.HeaderSize=0;
+
+ if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-7)
+ if (!UnpReadBuf())
+ return false;
+ Inp.faddbits((8-Inp.InBit)&7);
+
+ byte BlockFlags=Inp.fgetbits()>>8;
+ Inp.faddbits(8);
+ uint ByteCount=((BlockFlags>>3)&3)+1; // Block size byte count.
+
+ if (ByteCount==4)
+ return false;
+
+ Header.HeaderSize=2+ByteCount;
+
+ Header.BlockBitSize=(BlockFlags&7)+1;
+
+ byte SavedCheckSum=Inp.fgetbits()>>8;
+ Inp.faddbits(8);
+
+ int BlockSize=0;
+ for (uint I=0;I<ByteCount;I++)
+ {
+ BlockSize+=(Inp.fgetbits()>>8)<<(I*8);
+ Inp.addbits(8);
+ }
+
+ Header.BlockSize=BlockSize;
+ byte CheckSum=byte(0x5a^BlockFlags^BlockSize^(BlockSize>>8)^(BlockSize>>16));
+ if (CheckSum!=SavedCheckSum)
+ return false;
+
+ Header.BlockStart=Inp.InAddr;
+ ReadBorder=Min(ReadBorder,Header.BlockStart+Header.BlockSize-1);
+
+ Header.LastBlockInFile=(BlockFlags & 0x40)!=0;
+ Header.TablePresent=(BlockFlags & 0x80)!=0;
+ return true;
+}
+
+
+bool Unpack::ReadTables(BitInput &Inp,UnpackBlockHeader &Header,UnpackBlockTables &Tables)
+{
+ if (!Header.TablePresent)
+ return true;
+
+ if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-25)
+ if (!UnpReadBuf())
+ return false;
+
+ byte BitLength[BC];
+ for (uint I=0;I<BC;I++)
+ {
+ uint Length=(byte)(Inp.fgetbits() >> 12);
+ Inp.faddbits(4);
+ if (Length==15)
+ {
+ uint ZeroCount=(byte)(Inp.fgetbits() >> 12);
+ Inp.faddbits(4);
+ if (ZeroCount==0)
+ BitLength[I]=15;
+ else
+ {
+ ZeroCount+=2;
+ while (ZeroCount-- > 0 && I<ASIZE(BitLength))
+ BitLength[I++]=0;
+ I--;
+ }
+ }
+ else
+ BitLength[I]=Length;
+ }
+
+ MakeDecodeTables(BitLength,&Tables.BD,BC);
+
+ byte Table[HUFF_TABLE_SIZE];
+ const uint TableSize=HUFF_TABLE_SIZE;
+ for (uint I=0;I<TableSize;)
+ {
+ if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-5)
+ if (!UnpReadBuf())
+ return false;
+ uint Number=DecodeNumber(Inp,&Tables.BD);
+ if (Number<16)
+ {
+ Table[I]=Number;
+ I++;
+ }
+ else
+ if (Number<18)
+ {
+ uint N;
+ if (Number==16)
+ {
+ N=(Inp.fgetbits() >> 13)+3;
+ Inp.faddbits(3);
+ }
+ else
+ {
+ N=(Inp.fgetbits() >> 9)+11;
+ Inp.faddbits(7);
+ }
+ if (I==0)
+ {
+ // We cannot have "repeat previous" code at the first position.
+ // Multiple such codes would shift Inp position without changing I,
+ // which can lead to reading beyond of Inp boundary in mutithreading
+ // mode, where Inp.ExternalBuffer disables bounds check and we just
+ // reserve a lot of buffer space to not need such check normally.
+ return false;
+ }
+ else
+ while (N-- > 0 && I<TableSize)
+ {
+ Table[I]=Table[I-1];
+ I++;
+ }
+ }
+ else
+ {
+ uint N;
+ if (Number==18)
+ {
+ N=(Inp.fgetbits() >> 13)+3;
+ Inp.faddbits(3);
+ }
+ else
+ {
+ N=(Inp.fgetbits() >> 9)+11;
+ Inp.faddbits(7);
+ }
+ while (N-- > 0 && I<TableSize)
+ Table[I++]=0;
+ }
+ }
+ TablesRead5=true;
+ if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop)
+ return false;
+ MakeDecodeTables(&Table[0],&Tables.LD,NC);
+ MakeDecodeTables(&Table[NC],&Tables.DD,DC);
+ MakeDecodeTables(&Table[NC+DC],&Tables.LDD,LDC);
+ MakeDecodeTables(&Table[NC+DC+LDC],&Tables.RD,RC);
+ return true;
+}
+
+
+void Unpack::InitFilters()
+{
+ Filters.SoftReset();
+}
diff --git a/unrar/unrar/unpack50frag.cpp b/unrar/unrar/unpack50frag.cpp
new file mode 100644
index 0000000..3c008ff
--- /dev/null
+++ b/unrar/unrar/unpack50frag.cpp
@@ -0,0 +1,103 @@
+FragmentedWindow::FragmentedWindow()
+{
+ memset(Mem,0,sizeof(Mem));
+ memset(MemSize,0,sizeof(MemSize));
+}
+
+
+FragmentedWindow::~FragmentedWindow()
+{
+ Reset();
+}
+
+
+void FragmentedWindow::Reset()
+{
+ for (uint I=0;I<ASIZE(Mem);I++)
+ if (Mem[I]!=NULL)
+ {
+ free(Mem[I]);
+ Mem[I]=NULL;
+ }
+}
+
+
+void FragmentedWindow::Init(size_t WinSize)
+{
+ Reset();
+
+ uint BlockNum=0;
+ size_t TotalSize=0; // Already allocated.
+ while (TotalSize<WinSize && BlockNum<ASIZE(Mem))
+ {
+ size_t Size=WinSize-TotalSize; // Size needed to allocate.
+
+ // Minimum still acceptable block size. Next allocations cannot be larger
+ // than current, so we do not need blocks if they are smaller than
+ // "size left / attempts left". Also we do not waste time to blocks
+ // smaller than some arbitrary constant.
+ size_t MinSize=Max(Size/(ASIZE(Mem)-BlockNum), 0x400000);
+
+ byte *NewMem=NULL;
+ while (Size>=MinSize)
+ {
+ NewMem=(byte *)malloc(Size);
+ if (NewMem!=NULL)
+ break;
+ Size-=Size/32;
+ }
+ if (NewMem==NULL)
+ throw std::bad_alloc();
+
+ // Clean the window to generate the same output when unpacking corrupt
+ // RAR files, which may access to unused areas of sliding dictionary.
+ memset(NewMem,0,Size);
+
+ Mem[BlockNum]=NewMem;
+ TotalSize+=Size;
+ MemSize[BlockNum]=TotalSize;
+ BlockNum++;
+ }
+ if (TotalSize<WinSize) // Not found enough free blocks.
+ throw std::bad_alloc();
+}
+
+
+byte& FragmentedWindow::operator [](size_t Item)
+{
+ if (Item<MemSize[0])
+ return Mem[0][Item];
+ for (uint I=1;I<ASIZE(MemSize);I++)
+ if (Item<MemSize[I])
+ return Mem[I][Item-MemSize[I-1]];
+ return Mem[0][0]; // Must never happen;
+}
+
+
+void FragmentedWindow::CopyString(uint Length,uint Distance,size_t &UnpPtr,size_t MaxWinMask)
+{
+ size_t SrcPtr=UnpPtr-Distance;
+ while (Length-- > 0)
+ {
+ (*this)[UnpPtr]=(*this)[SrcPtr++ & MaxWinMask];
+ // We need to have masked UnpPtr after quit from loop, so it must not
+ // be replaced with '(*this)[UnpPtr++ & MaxWinMask]'
+ UnpPtr=(UnpPtr+1) & MaxWinMask;
+ }
+}
+
+
+void FragmentedWindow::CopyData(byte *Dest,size_t WinPos,size_t Size)
+{
+ for (size_t I=0;I<Size;I++)
+ Dest[I]=(*this)[WinPos+I];
+}
+
+
+size_t FragmentedWindow::GetBlockSize(size_t StartPos,size_t RequiredSize)
+{
+ for (uint I=0;I<ASIZE(MemSize);I++)
+ if (StartPos<MemSize[I])
+ return Min(MemSize[I]-StartPos,RequiredSize);
+ return 0; // Must never be here.
+}
diff --git a/unrar/unrar/unpack50mt.cpp b/unrar/unrar/unpack50mt.cpp
new file mode 100644
index 0000000..82c9c4a
--- /dev/null
+++ b/unrar/unrar/unpack50mt.cpp
@@ -0,0 +1,655 @@
+#define UNP_READ_SIZE_MT 0x400000
+#define UNP_BLOCKS_PER_THREAD 2
+
+
+struct UnpackThreadDataList
+{
+ UnpackThreadData *D;
+ uint BlockCount;
+};
+
+
+THREAD_PROC(UnpackDecodeThread)
+{
+ UnpackThreadDataList *DL=(UnpackThreadDataList *)Data;
+ for (uint I=0;I<DL->BlockCount;I++)
+ DL->D->UnpackPtr->UnpackDecode(DL->D[I]);
+}
+
+
+void Unpack::InitMT()
+{
+ if (ReadBufMT==NULL)
+ {
+ // Even getbits32 can read up to 3 additional bytes after current
+ // and our block header and table reading code can look much further.
+ // Let's allocate the additional space here, so we do not need to check
+ // bounds for every bit field access.
+ const size_t Overflow=1024;
+
+ ReadBufMT=new byte[UNP_READ_SIZE_MT+Overflow];
+ memset(ReadBufMT,0,UNP_READ_SIZE_MT+Overflow);
+ }
+ if (UnpThreadData==NULL)
+ {
+ uint MaxItems=MaxUserThreads*UNP_BLOCKS_PER_THREAD;
+ UnpThreadData=new UnpackThreadData[MaxItems];
+ memset(UnpThreadData,0,sizeof(UnpackThreadData)*MaxItems);
+
+ for (uint I=0;I<MaxItems;I++)
+ {
+ UnpackThreadData *CurData=UnpThreadData+I;
+ if (CurData->Decoded==NULL)
+ {
+ // Typical number of items in RAR blocks does not exceed 0x4000.
+ CurData->DecodedAllocated=0x4100;
+ // It will be freed in the object destructor, not in this file.
+ CurData->Decoded=(UnpackDecodedItem *)malloc(CurData->DecodedAllocated*sizeof(UnpackDecodedItem));
+ if (CurData->Decoded==NULL)
+ ErrHandler.MemoryError();
+ }
+ }
+ }
+}
+
+
+void Unpack::Unpack5MT(bool Solid)
+{
+ InitMT();
+ UnpInitData(Solid);
+
+ for (uint I=0;I<MaxUserThreads*UNP_BLOCKS_PER_THREAD;I++)
+ {
+ UnpackThreadData *CurData=UnpThreadData+I;
+ CurData->LargeBlock=false;
+ CurData->Incomplete=false;
+ }
+
+ UnpThreadData[0].BlockHeader=BlockHeader;
+ UnpThreadData[0].BlockTables=BlockTables;
+ uint LastBlockNum=0;
+
+ int DataSize=0;
+ int BlockStart=0;
+
+
+ // 'true' if we found a block too large for multithreaded extraction,
+ // so we switched to single threaded mode until the end of file.
+ // Large blocks could cause too high memory use in multithreaded mode.
+ bool LargeBlock=false;
+
+ bool Done=false;
+ while (!Done)
+ {
+ // Data amount, which is guaranteed to fit block header and tables,
+ // so we can safely read them without additional checks.
+ const int TooSmallToProcess=1024;
+
+ int ReadSize=UnpIO->UnpRead(ReadBufMT+DataSize,(UNP_READ_SIZE_MT-DataSize)&~0xf);
+ if (ReadSize<0)
+ break;
+ DataSize+=ReadSize;
+ if (DataSize==0)
+ break;
+
+ // First read chunk can be small if we are near the end of volume
+ // and we want it to fit block header and tables.
+ if (ReadSize>0 && DataSize<TooSmallToProcess)
+ continue;
+
+ while (BlockStart<DataSize && !Done)
+ {
+ uint BlockNumber=0,BlockNumberMT=0;
+ while (BlockNumber<MaxUserThreads*UNP_BLOCKS_PER_THREAD)
+ {
+ UnpackThreadData *CurData=UnpThreadData+BlockNumber;
+ LastBlockNum=BlockNumber;
+ CurData->UnpackPtr=this;
+
+ // 'Incomplete' thread is present. This is a thread processing block
+ // in the end of buffer, split between two read operations.
+ if (CurData->Incomplete)
+ CurData->DataSize=DataSize;
+ else
+ {
+ CurData->Inp.SetExternalBuffer(ReadBufMT+BlockStart);
+ CurData->Inp.InitBitInput();
+ CurData->DataSize=DataSize-BlockStart;
+ if (CurData->DataSize==0)
+ break;
+ CurData->DamagedData=false;
+ CurData->HeaderRead=false;
+ CurData->TableRead=false;
+ }
+
+ // We should not use 'last block in file' block flag here unless
+ // we'll check the block size, because even if block is last in file,
+ // it can exceed the current buffer and require more reading.
+ CurData->NoDataLeft=(ReadSize==0);
+
+ CurData->Incomplete=false;
+ CurData->ThreadNumber=BlockNumber;
+
+ if (!CurData->HeaderRead)
+ {
+ CurData->HeaderRead=true;
+ if (!ReadBlockHeader(CurData->Inp,CurData->BlockHeader) ||
+ !CurData->BlockHeader.TablePresent && !TablesRead5)
+ {
+ Done=true;
+ break;
+ }
+ TablesRead5=true;
+ }
+
+ // To prevent too high memory use we switch to single threaded mode
+ // if block exceeds this size. Typically RAR blocks do not exceed
+ // 64 KB, so this protection should not affect most of valid archives.
+ const int LargeBlockSize=0x20000;
+ if (LargeBlock || CurData->BlockHeader.BlockSize>LargeBlockSize)
+ LargeBlock=CurData->LargeBlock=true;
+ else
+ BlockNumberMT++; // Number of normal blocks processed in MT mode.
+
+ BlockStart+=CurData->BlockHeader.HeaderSize+CurData->BlockHeader.BlockSize;
+
+ BlockNumber++;
+
+ int DataLeft=DataSize-BlockStart;
+ if (DataLeft>=0 && CurData->BlockHeader.LastBlockInFile)
+ break;
+
+ // For second and following threads we move smaller blocks to buffer
+ // start to ensure that we have enough data to fit block header
+ // and tables.
+ if (DataLeft<TooSmallToProcess)
+ break;
+ }
+
+//#undef USE_THREADS
+ UnpackThreadDataList UTDArray[MaxPoolThreads];
+ uint UTDArrayPos=0;
+
+ uint MaxBlockPerThread=BlockNumberMT/MaxUserThreads;
+ if (BlockNumberMT%MaxUserThreads!=0)
+ MaxBlockPerThread++;
+
+ // Decode all normal blocks until the first 'large' if any.
+ for (uint CurBlock=0;CurBlock<BlockNumberMT;CurBlock+=MaxBlockPerThread)
+ {
+ UnpackThreadDataList *UTD=UTDArray+UTDArrayPos++;
+ UTD->D=UnpThreadData+CurBlock;
+ UTD->BlockCount=Min(MaxBlockPerThread,BlockNumberMT-CurBlock);
+
+#ifdef USE_THREADS
+ if (BlockNumber==1)
+ UnpackDecode(*UTD->D);
+ else
+ UnpThreadPool->AddTask(UnpackDecodeThread,(void*)UTD);
+#else
+ for (uint I=0;I<UTD->BlockCount;I++)
+ UnpackDecode(UTD->D[I]);
+#endif
+ }
+
+ if (BlockNumber==0)
+ break;
+
+#ifdef USE_THREADS
+ UnpThreadPool->WaitDone();
+#endif
+
+ bool IncompleteThread=false;
+
+ for (uint Block=0;Block<BlockNumber;Block++)
+ {
+ UnpackThreadData *CurData=UnpThreadData+Block;
+ if (!CurData->LargeBlock && !ProcessDecoded(*CurData) ||
+ CurData->LargeBlock && !UnpackLargeBlock(*CurData) ||
+ CurData->DamagedData)
+ {
+ Done=true;
+ break;
+ }
+ if (CurData->Incomplete)
+ {
+ int BufPos=int(CurData->Inp.InBuf+CurData->Inp.InAddr-ReadBufMT);
+ if (DataSize<=BufPos) // Thread exceeded input buffer boundary.
+ {
+ Done=true;
+ break;
+ }
+ IncompleteThread=true;
+ memmove(ReadBufMT,ReadBufMT+BufPos,DataSize-BufPos);
+ CurData->BlockHeader.BlockSize-=CurData->Inp.InAddr-CurData->BlockHeader.BlockStart;
+ CurData->BlockHeader.HeaderSize=0;
+ CurData->BlockHeader.BlockStart=0;
+ CurData->Inp.InBuf=ReadBufMT;
+ CurData->Inp.InAddr=0;
+
+ if (Block!=0)
+ {
+ // Move the incomplete thread entry to the first position,
+ // so we'll start processing from it. Preserve the original
+ // buffer for decoded data.
+ UnpackDecodedItem *Decoded=UnpThreadData[0].Decoded;
+ uint DecodedAllocated=UnpThreadData[0].DecodedAllocated;
+ UnpThreadData[0]=*CurData;
+ UnpThreadData[0].Decoded=Decoded;
+ UnpThreadData[0].DecodedAllocated=DecodedAllocated;
+ CurData->Incomplete=false;
+ }
+
+ BlockStart=0;
+ DataSize-=BufPos;
+ break;
+ }
+ else
+ if (CurData->BlockHeader.LastBlockInFile)
+ {
+ Done=true;
+ break;
+ }
+ }
+
+ if (IncompleteThread || Done)
+ break; // Current buffer is done, read more data or quit.
+ else
+ {
+ int DataLeft=DataSize-BlockStart;
+ if (DataLeft<TooSmallToProcess)
+ {
+ if (DataLeft<0) // Invalid data, must not happen in valid archive.
+ {
+ Done=true;
+ break;
+ }
+
+ // If we do not have incomplete thread and have some data
+ // in the end of buffer, too small for single thread,
+ // let's move it to beginning of next buffer.
+ if (DataLeft>0)
+ memmove(ReadBufMT,ReadBufMT+BlockStart,DataLeft);
+ DataSize=DataLeft;
+ BlockStart=0;
+ break; // Current buffer is done, try to read more data.
+ }
+ }
+ }
+ }
+ UnpPtr&=MaxWinMask; // ProcessDecoded and maybe others can leave UnpPtr > MaxWinMask here.
+ UnpWriteBuf();
+
+ BlockHeader=UnpThreadData[LastBlockNum].BlockHeader;
+ BlockTables=UnpThreadData[LastBlockNum].BlockTables;
+}
+
+
+// Decode Huffman block and save decoded data to memory.
+void Unpack::UnpackDecode(UnpackThreadData &D)
+{
+ if (!D.TableRead)
+ {
+ D.TableRead=true;
+ if (!ReadTables(D.Inp,D.BlockHeader,D.BlockTables))
+ {
+ D.DamagedData=true;
+ return;
+ }
+ }
+
+ if (D.Inp.InAddr>D.BlockHeader.HeaderSize+D.BlockHeader.BlockSize)
+ {
+ D.DamagedData=true;
+ return;
+ }
+
+ D.DecodedSize=0;
+ int BlockBorder=D.BlockHeader.BlockStart+D.BlockHeader.BlockSize-1;
+
+ // Reserve enough space even for filter entry.
+ int DataBorder=D.DataSize-16;
+ int ReadBorder=Min(BlockBorder,DataBorder);
+
+ while (true)
+ {
+ if (D.Inp.InAddr>=ReadBorder)
+ {
+ if (D.Inp.InAddr>BlockBorder || D.Inp.InAddr==BlockBorder &&
+ D.Inp.InBit>=D.BlockHeader.BlockBitSize)
+ break;
+
+ // If we do not have any more data in file to read, we must process
+ // what we have until last byte. Otherwise we can return and append
+ // more data to unprocessed few bytes.
+ if ((D.Inp.InAddr>=DataBorder) && !D.NoDataLeft || D.Inp.InAddr>=D.DataSize)
+ {
+ D.Incomplete=true;
+ break;
+ }
+ }
+ if (D.DecodedSize>D.DecodedAllocated-8) // Filter can use several slots.
+ {
+ D.DecodedAllocated=D.DecodedAllocated*2;
+ void *Decoded=realloc(D.Decoded,D.DecodedAllocated*sizeof(UnpackDecodedItem));
+ if (Decoded==NULL)
+ ErrHandler.MemoryError(); // D.Decoded will be freed in the destructor.
+ D.Decoded=(UnpackDecodedItem *)Decoded;
+ }
+
+ UnpackDecodedItem *CurItem=D.Decoded+D.DecodedSize++;
+
+ uint MainSlot=DecodeNumber(D.Inp,&D.BlockTables.LD);
+ if (MainSlot<256)
+ {
+ if (D.DecodedSize>1)
+ {
+ UnpackDecodedItem *PrevItem=CurItem-1;
+ if (PrevItem->Type==UNPDT_LITERAL && PrevItem->Length<ASIZE(PrevItem->Literal)-1)
+ {
+ PrevItem->Length++;
+ PrevItem->Literal[PrevItem->Length]=(byte)MainSlot;
+ D.DecodedSize--;
+ continue;
+ }
+ }
+ CurItem->Type=UNPDT_LITERAL;
+ CurItem->Literal[0]=(byte)MainSlot;
+ CurItem->Length=0;
+ continue;
+ }
+ if (MainSlot>=262)
+ {
+ uint Length=SlotToLength(D.Inp,MainSlot-262);
+
+ uint DBits,Distance=1,DistSlot=DecodeNumber(D.Inp,&D.BlockTables.DD);
+ if (DistSlot<4)
+ {
+ DBits=0;
+ Distance+=DistSlot;
+ }
+ else
+ {
+ DBits=DistSlot/2 - 1;
+ Distance+=(2 | (DistSlot & 1)) << DBits;
+ }
+
+ if (DBits>0)
+ {
+ if (DBits>=4)
+ {
+ if (DBits>4)
+ {
+ Distance+=((D.Inp.getbits32()>>(36-DBits))<<4);
+ D.Inp.addbits(DBits-4);
+ }
+ uint LowDist=DecodeNumber(D.Inp,&D.BlockTables.LDD);
+ Distance+=LowDist;
+ }
+ else
+ {
+ Distance+=D.Inp.getbits()>>(16-DBits);
+ D.Inp.addbits(DBits);
+ }
+ }
+
+ if (Distance>0x100)
+ {
+ Length++;
+ if (Distance>0x2000)
+ {
+ Length++;
+ if (Distance>0x40000)
+ Length++;
+ }
+ }
+
+ CurItem->Type=UNPDT_MATCH;
+ CurItem->Length=(ushort)Length;
+ CurItem->Distance=Distance;
+ continue;
+ }
+ if (MainSlot==256)
+ {
+ UnpackFilter Filter;
+ ReadFilter(D.Inp,Filter);
+
+ CurItem->Type=UNPDT_FILTER;
+ CurItem->Length=Filter.Type;
+ CurItem->Distance=Filter.BlockStart;
+
+ CurItem=D.Decoded+D.DecodedSize++;
+
+ CurItem->Type=UNPDT_FILTER;
+ CurItem->Length=Filter.Channels;
+ CurItem->Distance=Filter.BlockLength;
+
+ continue;
+ }
+ if (MainSlot==257)
+ {
+ CurItem->Type=UNPDT_FULLREP;
+ continue;
+ }
+ if (MainSlot<262)
+ {
+ CurItem->Type=UNPDT_REP;
+ CurItem->Distance=MainSlot-258;
+ uint LengthSlot=DecodeNumber(D.Inp,&D.BlockTables.RD);
+ uint Length=SlotToLength(D.Inp,LengthSlot);
+ CurItem->Length=(ushort)Length;
+ continue;
+ }
+ }
+}
+
+
+// Process decoded Huffman block data.
+bool Unpack::ProcessDecoded(UnpackThreadData &D)
+{
+ UnpackDecodedItem *Item=D.Decoded,*Border=D.Decoded+D.DecodedSize;
+ while (Item<Border)
+ {
+ UnpPtr&=MaxWinMask;
+ if (((WriteBorder-UnpPtr) & MaxWinMask)<=MAX_INC_LZ_MATCH && WriteBorder!=UnpPtr)
+ {
+ UnpWriteBuf();
+ if (WrittenFileSize>DestUnpSize)
+ return false;
+ }
+
+ if (Item->Type==UNPDT_LITERAL)
+ {
+#if defined(LITTLE_ENDIAN) && defined(ALLOW_MISALIGNED)
+ if (Item->Length==7 && UnpPtr<MaxWinSize-8)
+ {
+ *(uint64 *)(Window+UnpPtr)=*(uint64 *)(Item->Literal);
+ UnpPtr+=8;
+ }
+ else
+#endif
+ for (uint I=0;I<=Item->Length;I++)
+ Window[UnpPtr++ & MaxWinMask]=Item->Literal[I];
+ }
+ else
+ if (Item->Type==UNPDT_MATCH)
+ {
+ InsertOldDist(Item->Distance);
+ LastLength=Item->Length;
+ CopyString(Item->Length,Item->Distance);
+ }
+ else
+ if (Item->Type==UNPDT_REP)
+ {
+ uint Distance=OldDist[Item->Distance];
+ for (uint I=Item->Distance;I>0;I--)
+ OldDist[I]=OldDist[I-1];
+ OldDist[0]=Distance;
+ LastLength=Item->Length;
+ CopyString(Item->Length,Distance);
+ }
+ else
+ if (Item->Type==UNPDT_FULLREP)
+ {
+ if (LastLength!=0)
+ CopyString(LastLength,OldDist[0]);
+ }
+ else
+ if (Item->Type==UNPDT_FILTER)
+ {
+ UnpackFilter Filter;
+
+ Filter.Type=(byte)Item->Length;
+ Filter.BlockStart=Item->Distance;
+
+ Item++;
+
+ Filter.Channels=(byte)Item->Length;
+ Filter.BlockLength=Item->Distance;
+
+ AddFilter(Filter);
+ }
+ Item++;
+ }
+ return true;
+}
+
+
+// For large blocks we decode and process in same function in single threaded
+// mode, so we do not need to store intermediate data in memory.
+bool Unpack::UnpackLargeBlock(UnpackThreadData &D)
+{
+ if (!D.TableRead)
+ {
+ D.TableRead=true;
+ if (!ReadTables(D.Inp,D.BlockHeader,D.BlockTables))
+ {
+ D.DamagedData=true;
+ return false;
+ }
+ }
+
+ if (D.Inp.InAddr>D.BlockHeader.HeaderSize+D.BlockHeader.BlockSize)
+ {
+ D.DamagedData=true;
+ return false;
+ }
+
+ int BlockBorder=D.BlockHeader.BlockStart+D.BlockHeader.BlockSize-1;
+
+ // Reserve enough space even for filter entry.
+ int DataBorder=D.DataSize-16;
+ int ReadBorder=Min(BlockBorder,DataBorder);
+
+ while (true)
+ {
+ UnpPtr&=MaxWinMask;
+ if (D.Inp.InAddr>=ReadBorder)
+ {
+ if (D.Inp.InAddr>BlockBorder || D.Inp.InAddr==BlockBorder &&
+ D.Inp.InBit>=D.BlockHeader.BlockBitSize)
+ break;
+
+ // If we do not have any more data in file to read, we must process
+ // what we have until last byte. Otherwise we can return and append
+ // more data to unprocessed few bytes.
+ if ((D.Inp.InAddr>=DataBorder) && !D.NoDataLeft || D.Inp.InAddr>=D.DataSize)
+ {
+ D.Incomplete=true;
+ break;
+ }
+ }
+ if (((WriteBorder-UnpPtr) & MaxWinMask)<=MAX_INC_LZ_MATCH && WriteBorder!=UnpPtr)
+ {
+ UnpWriteBuf();
+ if (WrittenFileSize>DestUnpSize)
+ return false;
+ }
+
+ uint MainSlot=DecodeNumber(D.Inp,&D.BlockTables.LD);
+ if (MainSlot<256)
+ {
+ Window[UnpPtr++]=(byte)MainSlot;
+ continue;
+ }
+ if (MainSlot>=262)
+ {
+ uint Length=SlotToLength(D.Inp,MainSlot-262);
+
+ uint DBits,Distance=1,DistSlot=DecodeNumber(D.Inp,&D.BlockTables.DD);
+ if (DistSlot<4)
+ {
+ DBits=0;
+ Distance+=DistSlot;
+ }
+ else
+ {
+ DBits=DistSlot/2 - 1;
+ Distance+=(2 | (DistSlot & 1)) << DBits;
+ }
+
+ if (DBits>0)
+ {
+ if (DBits>=4)
+ {
+ if (DBits>4)
+ {
+ Distance+=((D.Inp.getbits32()>>(36-DBits))<<4);
+ D.Inp.addbits(DBits-4);
+ }
+ uint LowDist=DecodeNumber(D.Inp,&D.BlockTables.LDD);
+ Distance+=LowDist;
+ }
+ else
+ {
+ Distance+=D.Inp.getbits32()>>(32-DBits);
+ D.Inp.addbits(DBits);
+ }
+ }
+
+ if (Distance>0x100)
+ {
+ Length++;
+ if (Distance>0x2000)
+ {
+ Length++;
+ if (Distance>0x40000)
+ Length++;
+ }
+ }
+
+ InsertOldDist(Distance);
+ LastLength=Length;
+ CopyString(Length,Distance);
+ continue;
+ }
+ if (MainSlot==256)
+ {
+ UnpackFilter Filter;
+ if (!ReadFilter(D.Inp,Filter) || !AddFilter(Filter))
+ break;
+ continue;
+ }
+ if (MainSlot==257)
+ {
+ if (LastLength!=0)
+ CopyString(LastLength,OldDist[0]);
+ continue;
+ }
+ if (MainSlot<262)
+ {
+ uint DistNum=MainSlot-258;
+ uint Distance=OldDist[DistNum];
+ for (uint I=DistNum;I>0;I--)
+ OldDist[I]=OldDist[I-1];
+ OldDist[0]=Distance;
+
+ uint LengthSlot=DecodeNumber(D.Inp,&D.BlockTables.RD);
+ uint Length=SlotToLength(D.Inp,LengthSlot);
+ LastLength=Length;
+ CopyString(Length,Distance);
+ continue;
+ }
+ }
+ return true;
+}
diff --git a/unrar/unrar/unpackinline.cpp b/unrar/unrar/unpackinline.cpp
new file mode 100644
index 0000000..04c3d1f
--- /dev/null
+++ b/unrar/unrar/unpackinline.cpp
@@ -0,0 +1,147 @@
+_forceinline void Unpack::InsertOldDist(uint Distance)
+{
+ OldDist[3]=OldDist[2];
+ OldDist[2]=OldDist[1];
+ OldDist[1]=OldDist[0];
+ OldDist[0]=Distance;
+}
+
+#ifdef _MSC_VER
+#define FAST_MEMCPY
+#endif
+
+_forceinline void Unpack::CopyString(uint Length,uint Distance)
+{
+ size_t SrcPtr=UnpPtr-Distance;
+ if (SrcPtr<MaxWinSize-MAX_INC_LZ_MATCH && UnpPtr<MaxWinSize-MAX_INC_LZ_MATCH)
+ {
+ // If we are not close to end of window, we do not need to waste time
+ // to "& MaxWinMask" pointer protection.
+
+ byte *Src=Window+SrcPtr;
+ byte *Dest=Window+UnpPtr;
+ UnpPtr+=Length;
+
+#ifdef FAST_MEMCPY
+ if (Distance<Length) // Overlapping strings
+#endif
+ while (Length>=8)
+ {
+ Dest[0]=Src[0];
+ Dest[1]=Src[1];
+ Dest[2]=Src[2];
+ Dest[3]=Src[3];
+ Dest[4]=Src[4];
+ Dest[5]=Src[5];
+ Dest[6]=Src[6];
+ Dest[7]=Src[7];
+
+ Src+=8;
+ Dest+=8;
+ Length-=8;
+ }
+#ifdef FAST_MEMCPY
+ else
+ while (Length>=8)
+ {
+ // In theory we still could overlap here.
+ // Supposing Distance == MaxWinSize - 1 we have memcpy(Src, Src + 1, 8).
+ // But for real RAR archives Distance <= MaxWinSize - MAX_INC_LZ_MATCH
+ // always, so overlap here is impossible.
+
+ // This memcpy expanded inline by MSVC. We could also use uint64
+ // assignment, which seems to provide about the same speed.
+ memcpy(Dest,Src,8);
+
+ Src+=8;
+ Dest+=8;
+ Length-=8;
+ }
+#endif
+
+ // Unroll the loop for 0 - 7 bytes left. Note that we use nested "if"s.
+ if (Length>0) { Dest[0]=Src[0];
+ if (Length>1) { Dest[1]=Src[1];
+ if (Length>2) { Dest[2]=Src[2];
+ if (Length>3) { Dest[3]=Src[3];
+ if (Length>4) { Dest[4]=Src[4];
+ if (Length>5) { Dest[5]=Src[5];
+ if (Length>6) { Dest[6]=Src[6]; } } } } } } } // Close all nested "if"s.
+ }
+ else
+ while (Length-- > 0) // Slow copying with all possible precautions.
+ {
+ Window[UnpPtr]=Window[SrcPtr++ & MaxWinMask];
+ // We need to have masked UnpPtr after quit from loop, so it must not
+ // be replaced with 'Window[UnpPtr++ & MaxWinMask]'
+ UnpPtr=(UnpPtr+1) & MaxWinMask;
+ }
+}
+
+
+_forceinline uint Unpack::DecodeNumber(BitInput &Inp,DecodeTable *Dec)
+{
+ // Left aligned 15 bit length raw bit field.
+ uint BitField=Inp.getbits() & 0xfffe;
+
+ if (BitField<Dec->DecodeLen[Dec->QuickBits])
+ {
+ uint Code=BitField>>(16-Dec->QuickBits);
+ Inp.addbits(Dec->QuickLen[Code]);
+ return Dec->QuickNum[Code];
+ }
+
+ // Detect the real bit length for current code.
+ uint Bits=15;
+ for (uint I=Dec->QuickBits+1;I<15;I++)
+ if (BitField<Dec->DecodeLen[I])
+ {
+ Bits=I;
+ break;
+ }
+
+ Inp.addbits(Bits);
+
+ // Calculate the distance from the start code for current bit length.
+ uint Dist=BitField-Dec->DecodeLen[Bits-1];
+
+ // Start codes are left aligned, but we need the normal right aligned
+ // number. So we shift the distance to the right.
+ Dist>>=(16-Bits);
+
+ // Now we can calculate the position in the code list. It is the sum
+ // of first position for current bit length and right aligned distance
+ // between our bit field and start code for current bit length.
+ uint Pos=Dec->DecodePos[Bits]+Dist;
+
+ // Out of bounds safety check required for damaged archives.
+ if (Pos>=Dec->MaxNum)
+ Pos=0;
+
+ // Convert the position in the code list to position in alphabet
+ // and return it.
+ return Dec->DecodeNum[Pos];
+}
+
+
+_forceinline uint Unpack::SlotToLength(BitInput &Inp,uint Slot)
+{
+ uint LBits,Length=2;
+ if (Slot<8)
+ {
+ LBits=0;
+ Length+=Slot;
+ }
+ else
+ {
+ LBits=Slot/4-1;
+ Length+=(4 | (Slot & 3)) << LBits;
+ }
+
+ if (LBits>0)
+ {
+ Length+=Inp.getbits()>>(16-LBits);
+ Inp.addbits(LBits);
+ }
+ return Length;
+}
diff --git a/unrar/unrar/uowners.cpp b/unrar/unrar/uowners.cpp
index 38471e0..5eb1279 100644
--- a/unrar/unrar/uowners.cpp
+++ b/unrar/unrar/uowners.cpp
@@ -1,80 +1,97 @@
-void ExtractUnixOwner(Archive &Arc,char *FileName)
+void ExtractUnixOwner30(Archive &Arc,const wchar *FileName)
{
- if (Arc.HeaderCRC!=Arc.UOHead.HeadCRC)
- {
- Log(Arc.FileName,St(MOwnersBroken),FileName);
- ErrHandler.SetErrorCode(CRC_ERROR);
+ char NameA[NM];
+ WideToChar(FileName,NameA,ASIZE(NameA));
+
+ if (memchr(&Arc.SubHead.SubData[0],0,Arc.SubHead.SubData.Size())==NULL)
return;
- }
+
+ char *OwnerName=(char *)&Arc.SubHead.SubData[0];
+ int OwnerSize=strlen(OwnerName)+1;
+ int GroupSize=Arc.SubHead.SubData.Size()-OwnerSize;
+ char GroupName[NM];
+ strncpy(GroupName,(char *)&Arc.SubHead.SubData[OwnerSize],GroupSize);
+ GroupName[GroupSize]=0;
struct passwd *pw;
- if ((pw=getpwnam(Arc.UOHead.OwnerName))==NULL)
+ if ((pw=getpwnam(OwnerName))==NULL)
{
- Log(Arc.FileName,St(MErrGetOwnerID),Arc.UOHead.OwnerName);
- ErrHandler.SetErrorCode(WARNING);
+ uiMsg(UIERROR_UOWNERGETOWNERID,Arc.FileName,GetWide(OwnerName));
+ ErrHandler.SetErrorCode(RARX_WARNING);
return;
}
uid_t OwnerID=pw->pw_uid;
struct group *gr;
- if ((gr=getgrnam(Arc.UOHead.GroupName))==NULL)
+ if ((gr=getgrnam(GroupName))==NULL)
{
- Log(Arc.FileName,St(MErrGetGroupID),Arc.UOHead.GroupName);
- ErrHandler.SetErrorCode(CRC_ERROR);
+ uiMsg(UIERROR_UOWNERGETGROUPID,Arc.FileName,GetWide(GroupName));
+ ErrHandler.SetErrorCode(RARX_WARNING);
return;
}
- uint Attr=GetFileAttr(FileName,NULL);
+ uint Attr=GetFileAttr(FileName);
gid_t GroupID=gr->gr_gid;
#if defined(SAVE_LINKS) && !defined(_APPLE)
- if (lchown(FileName,OwnerID,GroupID)!=0)
+ if (lchown(NameA,OwnerID,GroupID)!=0)
#else
- if (chown(FileName,OwnerID,GroupID)!=0)
+ if (chown(NameA,OwnerID,GroupID)!=0)
#endif
{
- Log(Arc.FileName,St(MSetOwnersError),FileName);
- ErrHandler.SetErrorCode(CREATE_ERROR);
+ uiMsg(UIERROR_UOWNERSET,Arc.FileName,FileName);
+ ErrHandler.SetErrorCode(RARX_CREATE);
}
- SetFileAttr(FileName,NULL,Attr);
+ SetFileAttr(FileName,Attr);
}
-void ExtractUnixOwnerNew(Archive &Arc,char *FileName)
+void SetUnixOwner(Archive &Arc,const wchar *FileName)
{
- char *OwnerName=(char *)&Arc.SubHead.SubData[0];
- int OwnerSize=strlen(OwnerName)+1;
- int GroupSize=Arc.SubHead.SubData.Size()-OwnerSize;
- char GroupName[NM];
- strncpy(GroupName,(char *)&Arc.SubHead.SubData[OwnerSize],GroupSize);
- GroupName[GroupSize]=0;
+ char NameA[NM];
+ WideToChar(FileName,NameA,ASIZE(NameA));
- struct passwd *pw;
- if ((pw=getpwnam(OwnerName))==NULL)
+ // First, we try to resolve symbolic names. If they are missing or cannot
+ // be resolved, we try to use numeric values if any. If numeric values
+ // are missing too, function fails.
+ FileHeader &hd=Arc.FileHead;
+ if (*hd.UnixOwnerName!=0)
{
- Log(Arc.FileName,St(MErrGetOwnerID),OwnerName);
- ErrHandler.SetErrorCode(WARNING);
- return;
+ struct passwd *pw;
+ if ((pw=getpwnam(hd.UnixOwnerName))==NULL)
+ {
+ if (!hd.UnixOwnerNumeric)
+ {
+ uiMsg(UIERROR_UOWNERGETOWNERID,Arc.FileName,GetWide(hd.UnixOwnerName));
+ ErrHandler.SetErrorCode(RARX_WARNING);
+ return;
+ }
+ }
+ else
+ hd.UnixOwnerID=pw->pw_uid;
}
- uid_t OwnerID=pw->pw_uid;
-
- struct group *gr;
- if ((gr=getgrnam(GroupName))==NULL)
+ if (*hd.UnixGroupName!=0)
{
- Log(Arc.FileName,St(MErrGetGroupID),GroupName);
- ErrHandler.SetErrorCode(CRC_ERROR);
- return;
+ struct group *gr;
+ if ((gr=getgrnam(hd.UnixGroupName))==NULL)
+ {
+ if (!hd.UnixGroupNumeric)
+ {
+ uiMsg(UIERROR_UOWNERGETGROUPID,Arc.FileName,GetWide(hd.UnixGroupName));
+ ErrHandler.SetErrorCode(RARX_WARNING);
+ return;
+ }
+ }
+ else
+ hd.UnixGroupID=gr->gr_gid;
}
- uint Attr=GetFileAttr(FileName,NULL);
- gid_t GroupID=gr->gr_gid;
#if defined(SAVE_LINKS) && !defined(_APPLE)
- if (lchown(FileName,OwnerID,GroupID)!=0)
+ if (lchown(NameA,hd.UnixOwnerID,hd.UnixGroupID)!=0)
#else
- if (chown(FileName,OwnerID,GroupID)!=0)
+ if (chown(NameA,hd.UnixOwnerID,hd.UnixGroupID)!=0)
#endif
{
- Log(Arc.FileName,St(MSetOwnersError),FileName);
- ErrHandler.SetErrorCode(CREATE_ERROR);
+ uiMsg(UIERROR_UOWNERSET,Arc.FileName,FileName);
+ ErrHandler.SetErrorCode(RARX_CREATE);
}
- SetFileAttr(FileName,NULL,Attr);
}
diff --git a/unrar/unrar/version.hpp b/unrar/unrar/version.hpp
index 616d34d..c9fffca 100644
--- a/unrar/unrar/version.hpp
+++ b/unrar/unrar/version.hpp
@@ -1,6 +1,6 @@
-#define RARVER_MAJOR 3
-#define RARVER_MINOR 80
-#define RARVER_BETA 2
-#define RARVER_DAY 15
-#define RARVER_MONTH 6
-#define RARVER_YEAR 2008
+#define RARVER_MAJOR 6
+#define RARVER_MINOR 24
+#define RARVER_BETA 0
+#define RARVER_DAY 3
+#define RARVER_MONTH 10
+#define RARVER_YEAR 2023
diff --git a/unrar/unrar/volume.cpp b/unrar/unrar/volume.cpp
index 4c138db..4924d8d 100644
--- a/unrar/unrar/volume.cpp
+++ b/unrar/unrar/volume.cpp
@@ -1,203 +1,187 @@
#include "rar.hpp"
+#ifdef RARDLL
+static bool DllVolChange(CommandData *Cmd,wchar *NextName,size_t NameSize);
+static bool DllVolNotify(CommandData *Cmd,wchar *NextName);
+#endif
-bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,char Command)
+bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,wchar Command)
{
- RAROptions *Cmd=Arc.GetRAROptions();
+ CommandData *Cmd=Arc.GetCommandData();
- int HeaderType=Arc.GetHeaderType();
- FileHeader *hd=HeaderType==NEWSUB_HEAD ? &Arc.SubHead:&Arc.NewLhd;
- bool SplitHeader=(HeaderType==FILE_HEAD || HeaderType==NEWSUB_HEAD) &&
- (hd->Flags & LHD_SPLIT_AFTER)!=0;
+ HEADER_TYPE HeaderType=Arc.GetHeaderType();
+ FileHeader *hd=HeaderType==HEAD_SERVICE ? &Arc.SubHead:&Arc.FileHead;
+ bool SplitHeader=(HeaderType==HEAD_FILE || HeaderType==HEAD_SERVICE) &&
+ hd->SplitAfter;
- if (DataIO!=NULL && SplitHeader && hd->UnpVer>=20 &&
- hd->FileCRC!=0xffffffff && DataIO->PackedCRC!=~hd->FileCRC)
+ if (DataIO!=NULL && SplitHeader)
{
- Log(Arc.FileName,St(MDataBadCRC),hd->FileName,Arc.FileName);
+ bool PackedHashPresent=Arc.Format==RARFMT50 ||
+ hd->UnpVer>=20 && hd->FileHash.CRC32!=0xffffffff;
+ if (PackedHashPresent &&
+ !DataIO->PackedDataHash.Cmp(&hd->FileHash,hd->UseHashKey ? hd->HashKey:NULL))
+ uiMsg(UIERROR_CHECKSUMPACKED, Arc.FileName, hd->FileName);
}
- Int64 PosBeforeClose=Arc.Tell();
+ bool PrevVolEncrypted=Arc.Encrypted;
+
+ int64 PosBeforeClose=Arc.Tell();
if (DataIO!=NULL)
- DataIO->ProcessedArcSize+=Arc.FileLength();
+ DataIO->ProcessedArcSize+=DataIO->LastArcSize;
+
Arc.Close();
- char NextName[NM];
- wchar NextNameW[NM];
- strcpy(NextName,Arc.FileName);
- strcpyw(NextNameW,Arc.FileNameW);
- NextVolumeName(NextName,NextNameW,ASIZE(NextName),(Arc.NewMhd.Flags & MHD_NEWNUMBERING)==0 || Arc.OldFormat);
+ wchar NextName[NM];
+ wcsncpyz(NextName,Arc.FileName,ASIZE(NextName));
+ NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering);
#if !defined(SFX_MODULE) && !defined(RARDLL)
bool RecoveryDone=false;
#endif
- bool FailedOpen=false,OldSchemeTested=false;
+ bool OldSchemeTested=false;
- while (!Arc.Open(NextName,NextNameW))
- {
- // We need to open a new volume which size was not calculated
- // in total size before, so we cannot calculate the total progress
- // anymore. Let's reset the total size to zero and stop
- // the total progress.
- if (DataIO!=NULL)
- DataIO->TotalArcSize=0;
-
- if (!OldSchemeTested)
+ bool FailedOpen=false; // No more next volume open attempts if true.
+#if !defined(SILENT)
+ // In -vp mode we force the pause before next volume even if it is present
+ // and even if we are on the hard disk. It is important when user does not
+ // want to process partially downloaded volumes preliminary.
+ // 2022.01.11: In WinRAR 6.10 beta versions we tried to ignore VolumePause
+ // if we could open the next volume with FMF_OPENEXCLUSIVE. But another
+ // developer asked us to return the previous behavior and always prompt
+ // for confirmation. They want to control when unrar continues, because
+ // the next file might not be fully decoded yet. They write chunks of data
+ // and then close the file again until the next chunk comes in.
+
+ if (Cmd->VolumePause && !uiAskNextVolume(NextName,ASIZE(NextName)))
+ FailedOpen=true;
+#endif
+
+ uint OpenMode = Cmd->OpenShared ? FMF_OPENSHARED : 0;
+
+ if (!FailedOpen)
+ while (!Arc.Open(NextName,OpenMode))
{
- // Checking for new style volumes renamed by user to old style
- // name format. Some users did it for unknown reason.
- char AltNextName[NM];
- wchar AltNextNameW[NM];
- strcpy(AltNextName,Arc.FileName);
- strcpyw(AltNextNameW,Arc.FileNameW);
- NextVolumeName(AltNextName,AltNextNameW,ASIZE(AltNextName),true);
- OldSchemeTested=true;
- if (Arc.Open(AltNextName,AltNextNameW))
+ // We need to open a new volume which size was not calculated
+ // in total size before, so we cannot calculate the total progress
+ // anymore. Let's reset the total size to zero and stop
+ // the total progress.
+ if (DataIO!=NULL)
+ DataIO->TotalArcSize=0;
+
+ if (!OldSchemeTested)
{
- strcpy(NextName,AltNextName);
- strcpyw(NextNameW,AltNextNameW);
- break;
+ // Checking for new style volumes renamed by user to old style
+ // name format. Some users did it for unknown reason.
+ wchar AltNextName[NM];
+ wcsncpyz(AltNextName,Arc.FileName,ASIZE(AltNextName));
+ NextVolumeName(AltNextName,ASIZE(AltNextName),true);
+ OldSchemeTested=true;
+ if (Arc.Open(AltNextName,OpenMode))
+ {
+ wcsncpyz(NextName,AltNextName,ASIZE(NextName));
+ break;
+ }
}
- }
#ifdef RARDLL
- if (Cmd->Callback==NULL && Cmd->ChangeVolProc==NULL ||
- Cmd->Callback!=NULL && Cmd->Callback(UCM_CHANGEVOLUME,Cmd->UserData,(LPARAM)NextName,RAR_VOL_ASK)==-1)
- {
- Cmd->DllError=ERAR_EOPEN;
- FailedOpen=true;
- break;
- }
- if (Cmd->ChangeVolProc!=NULL)
- {
-#if defined(_WIN_32) && !defined(_MSC_VER) && !defined(__MINGW32__)
- _EBX=_ESP;
-#endif
- int RetCode=Cmd->ChangeVolProc(NextName,RAR_VOL_ASK);
-#if defined(_WIN_32) && !defined(_MSC_VER) && !defined(__MINGW32__)
- _ESP=_EBX;
-#endif
- if (RetCode==0)
+ if (!DllVolChange(Cmd,NextName,ASIZE(NextName)))
{
- Cmd->DllError=ERAR_EOPEN;
FailedOpen=true;
break;
}
- }
-#else // RARDLL
+#else // !RARDLL
-#if !defined(SFX_MODULE) && !defined(_WIN_CE)
- if (!RecoveryDone)
- {
- RecVolumes RecVol;
- RecVol.Restore(Cmd,Arc.FileName,Arc.FileNameW,true);
- RecoveryDone=true;
- continue;
- }
+#ifndef SFX_MODULE
+ if (!RecoveryDone)
+ {
+ RecVolumesRestore(Cmd,Arc.FileName,true);
+ RecoveryDone=true;
+ continue;
+ }
#endif
-#ifndef GUI
- if (!Cmd->VolumePause && !IsRemovable(NextName))
- {
- FailedOpen=true;
- break;
- }
-#endif
+ if (!Cmd->VolumePause && !IsRemovable(NextName))
+ {
+ FailedOpen=true;
+ break;
+ }
#ifndef SILENT
- if (Cmd->AllYes || !AskNextVol(NextName))
+ if (Cmd->AllYes || !uiAskNextVolume(NextName,ASIZE(NextName)))
#endif
- {
- FailedOpen=true;
- break;
- }
+ {
+ FailedOpen=true;
+ break;
+ }
#endif // RARDLL
- *NextNameW=0;
- }
+ }
+
if (FailedOpen)
{
-#if !defined(SILENT) && !defined(_WIN_CE)
- Log(Arc.FileName,St(MAbsNextVol),NextName);
-#endif
- Arc.Open(Arc.FileName,Arc.FileNameW);
+ uiMsg(UIERROR_MISSINGVOL,NextName);
+ Arc.Open(Arc.FileName,OpenMode);
Arc.Seek(PosBeforeClose,SEEK_SET);
- return(false);
+ return false;
}
+
+ if (Command=='T' || Command=='X' || Command=='E')
+ mprintf(St(Command=='T' ? MTestVol:MExtrVol),Arc.FileName);
+
+
Arc.CheckArc(true);
#ifdef RARDLL
- if (Cmd->Callback!=NULL &&
- Cmd->Callback(UCM_CHANGEVOLUME,Cmd->UserData,(LPARAM)NextName,RAR_VOL_NOTIFY)==-1)
- return(false);
- if (Cmd->ChangeVolProc!=NULL)
- {
-#if defined(_WIN_32) && !defined(_MSC_VER) && !defined(__MINGW32__)
- _EBX=_ESP;
-#endif
- int RetCode=Cmd->ChangeVolProc(NextName,RAR_VOL_NOTIFY);
-#if defined(_WIN_32) && !defined(_MSC_VER) && !defined(__MINGW32__)
- _ESP=_EBX;
+ if (!DllVolNotify(Cmd,NextName))
+ return false;
#endif
- if (RetCode==0)
- return(false);
+
+ if (Arc.Encrypted!=PrevVolEncrypted)
+ {
+ // There is no legitimate reason for encrypted header state to be
+ // changed in the middle of volume sequence. So we abort here to prevent
+ // replacing an encrypted header volume to unencrypted and adding
+ // unexpected files by third party to encrypted extraction.
+ uiMsg(UIERROR_BADARCHIVE,Arc.FileName);
+ ErrHandler.Exit(RARX_FATAL);
}
-#endif
- if (Command=='T' || Command=='X' || Command=='E')
- mprintf(St(Command=='T' ? MTestVol:MExtrVol),Arc.FileName);
if (SplitHeader)
Arc.SearchBlock(HeaderType);
else
Arc.ReadHeader();
- if (Arc.GetHeaderType()==FILE_HEAD)
+ if (Arc.GetHeaderType()==HEAD_FILE)
{
Arc.ConvertAttributes();
- Arc.Seek(Arc.NextBlockPos-Arc.NewLhd.FullPackSize,SEEK_SET);
+ Arc.Seek(Arc.NextBlockPos-Arc.FileHead.PackSize,SEEK_SET);
}
-#ifndef GUI
- if (ShowFileName)
+ if (ShowFileName && !Cmd->DisableNames)
{
- char OutName[NM];
- IntToExt(Arc.NewLhd.FileName,OutName);
-#ifdef UNICODE_SUPPORTED
- bool WideName=(Arc.NewLhd.Flags & LHD_UNICODE) && UnicodeEnabled();
- if (WideName)
- {
- wchar NameW[NM];
- ConvertPath(Arc.NewLhd.FileNameW,NameW);
- char Name[NM];
- if (WideToChar(NameW,Name) && IsNameUsable(Name))
- strcpy(OutName,Name);
- }
-#endif
- mprintf(St(MExtrPoints),OutName);
+ mprintf(St(MExtrPoints),Arc.FileHead.FileName);
if (!Cmd->DisablePercentage)
- mprintf(" ");
+ mprintf(L" ");
}
-#endif
if (DataIO!=NULL)
{
- if (HeaderType==ENDARC_HEAD)
+ if (HeaderType==HEAD_ENDARC)
DataIO->UnpVolume=false;
else
{
- DataIO->UnpVolume=(hd->Flags & LHD_SPLIT_AFTER);
- DataIO->SetPackedSizeToRead(hd->FullPackSize);
+ DataIO->UnpVolume=hd->SplitAfter;
+ DataIO->SetPackedSizeToRead(hd->PackSize);
}
-#ifdef SFX_MODULE
- DataIO->UnpArcSize=Arc.FileLength();
-#endif
-
+
+ DataIO->AdjustTotalArcSize(&Arc);
+
// Reset the size of packed data read from current volume. It is used
// to display the total progress and preceding volumes are already
// compensated with ProcessedArcSize, so we need to reset this variable.
DataIO->CurUnpRead=0;
- DataIO->PackedCRC=0xffffffff;
-// DataIO->SetFiles(&Arc,NULL);
+ DataIO->PackedDataHash.Init(hd->FileHash.Type,Cmd->Threads);
}
- return(true);
+ return true;
}
@@ -205,12 +189,80 @@ bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,char Comman
-#ifndef SILENT
-bool AskNextVol(char *ArcName)
+#ifdef RARDLL
+bool DllVolChange(CommandData *Cmd,wchar *NextName,size_t NameSize)
+{
+ bool DllVolChanged=false,DllVolAborted=false;
+
+ if (Cmd->Callback!=NULL)
+ {
+ wchar OrgNextName[NM];
+ wcsncpyz(OrgNextName,NextName,ASIZE(OrgNextName));
+ if (Cmd->Callback(UCM_CHANGEVOLUMEW,Cmd->UserData,(LPARAM)NextName,RAR_VOL_ASK)==-1)
+ DllVolAborted=true;
+ else
+ if (wcscmp(OrgNextName,NextName)!=0)
+ DllVolChanged=true;
+ else
+ {
+ char NextNameA[NM],OrgNextNameA[NM];
+ WideToChar(NextName,NextNameA,ASIZE(NextNameA));
+ strncpyz(OrgNextNameA,NextNameA,ASIZE(OrgNextNameA));
+ if (Cmd->Callback(UCM_CHANGEVOLUME,Cmd->UserData,(LPARAM)NextNameA,RAR_VOL_ASK)==-1)
+ DllVolAborted=true;
+ else
+ if (strcmp(OrgNextNameA,NextNameA)!=0)
+ {
+ // We can damage some Unicode characters by U->A->U conversion,
+ // so set Unicode name only if we see that ANSI name is changed.
+ CharToWide(NextNameA,NextName,NameSize);
+ DllVolChanged=true;
+ }
+ }
+ }
+ if (!DllVolChanged && Cmd->ChangeVolProc!=NULL)
+ {
+ char NextNameA[NM];
+ WideToChar(NextName,NextNameA,ASIZE(NextNameA));
+ int RetCode=Cmd->ChangeVolProc(NextNameA,RAR_VOL_ASK);
+ if (RetCode==0)
+ DllVolAborted=true;
+ else
+ CharToWide(NextNameA,NextName,NameSize);
+ }
+
+ // We quit only on 'abort' condition, but not on 'name not changed'.
+ // It is legitimate for program to return the same name when waiting
+ // for currently non-existent volume.
+ // Also we quit to prevent an infinite loop if no callback is defined.
+ if (DllVolAborted || Cmd->Callback==NULL && Cmd->ChangeVolProc==NULL)
+ {
+ Cmd->DllError=ERAR_EOPEN;
+ return false;
+ }
+ return true;
+}
+#endif
+
+
+#ifdef RARDLL
+bool DllVolNotify(CommandData *Cmd,wchar *NextName)
{
- eprintf(St(MAskNextVol),ArcName);
- if (Ask(St(MContinueQuit))==2)
- return(false);
- return(true);
+ char NextNameA[NM];
+ WideToChar(NextName,NextNameA,ASIZE(NextNameA));
+ if (Cmd->Callback!=NULL)
+ {
+ if (Cmd->Callback(UCM_CHANGEVOLUMEW,Cmd->UserData,(LPARAM)NextName,RAR_VOL_NOTIFY)==-1)
+ return false;
+ if (Cmd->Callback(UCM_CHANGEVOLUME,Cmd->UserData,(LPARAM)NextNameA,RAR_VOL_NOTIFY)==-1)
+ return false;
+ }
+ if (Cmd->ChangeVolProc!=NULL)
+ {
+ int RetCode=Cmd->ChangeVolProc(NextNameA,RAR_VOL_NOTIFY);
+ if (RetCode==0)
+ return false;
+ }
+ return true;
}
#endif
diff --git a/unrar/unrar/volume.hpp b/unrar/unrar/volume.hpp
index 6465340..4ada109 100644
--- a/unrar/unrar/volume.hpp
+++ b/unrar/unrar/volume.hpp
@@ -1,11 +1,7 @@
#ifndef _RAR_VOLUME_
#define _RAR_VOLUME_
-void SplitArchive(Archive &Arc,FileHeader *fh,Int64 *HeaderPos,
- ComprDataIO *DataIO);
bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,
- char Command);
-void SetVolWrite(Archive &Dest,Int64 VolSize);
-bool AskNextVol(char *ArcName);
+ wchar Command);
#endif
diff --git a/unrar/unrar/win32acl.cpp b/unrar/unrar/win32acl.cpp
index 37fb22b..d4797bd 100644
--- a/unrar/unrar/win32acl.cpp
+++ b/unrar/unrar/win32acl.cpp
@@ -1,47 +1,45 @@
-static void SetPrivileges();
+static void SetACLPrivileges();
static bool ReadSacl=false;
#ifndef SFX_MODULE
-void ExtractACL(Archive &Arc,char *FileName,wchar *FileNameW)
+void ExtractACL20(Archive &Arc,const wchar *FileName)
{
- if (!WinNT())
- return;
-
- SetPrivileges();
+ SetACLPrivileges();
- if (Arc.HeaderCRC!=Arc.EAHead.HeadCRC)
+ if (Arc.BrokenHeader)
{
- Log(Arc.FileName,St(MACLBroken),FileName);
- ErrHandler.SetErrorCode(CRC_ERROR);
+ uiMsg(UIERROR_ACLBROKEN,Arc.FileName,FileName);
+ ErrHandler.SetErrorCode(RARX_CRC);
return;
}
- if (Arc.EAHead.Method<0x31 || Arc.EAHead.Method>0x35 || Arc.EAHead.UnpVer>PACK_VER)
+ if (Arc.EAHead.Method<0x31 || Arc.EAHead.Method>0x35 || Arc.EAHead.UnpVer>VER_PACK)
{
- Log(Arc.FileName,St(MACLUnknown),FileName);
- ErrHandler.SetErrorCode(WARNING);
+ uiMsg(UIERROR_ACLUNKNOWN,Arc.FileName,FileName);
+ ErrHandler.SetErrorCode(RARX_WARNING);
return;
}
ComprDataIO DataIO;
Unpack Unpack(&DataIO);
- Unpack.Init();
+ Unpack.Init(0x10000,false);
- Array<unsigned char> UnpData(Arc.EAHead.UnpSize);
+ Array<byte> UnpData(Arc.EAHead.UnpSize);
DataIO.SetUnpackToMemory(&UnpData[0],Arc.EAHead.UnpSize);
DataIO.SetPackedSizeToRead(Arc.EAHead.DataSize);
DataIO.EnableShowProgress(false);
DataIO.SetFiles(&Arc,NULL);
+ DataIO.UnpHash.Init(HASH_CRC32,1);
Unpack.SetDestSize(Arc.EAHead.UnpSize);
Unpack.DoUnpack(Arc.EAHead.UnpVer,false);
- if (Arc.EAHead.EACRC!=~DataIO.UnpFileCRC)
+ if (Arc.EAHead.EACRC!=DataIO.UnpHash.GetCRC32())
{
- Log(Arc.FileName,St(MACLBroken),FileName);
- ErrHandler.SetErrorCode(CRC_ERROR);
+ uiMsg(UIERROR_ACLBROKEN,Arc.FileName,FileName);
+ ErrHandler.SetErrorCode(RARX_CRC);
return;
}
@@ -51,77 +49,87 @@ void ExtractACL(Archive &Arc,char *FileName,wchar *FileNameW)
si|=SACL_SECURITY_INFORMATION;
SECURITY_DESCRIPTOR *sd=(SECURITY_DESCRIPTOR *)&UnpData[0];
- int SetCode;
- if (FileNameW!=NULL)
- SetCode=SetFileSecurityW(FileNameW,si,sd);
- else
- SetCode=SetFileSecurity(FileName,si,sd);
+ int SetCode=SetFileSecurity(FileName,si,sd);
if (!SetCode)
{
- Log(Arc.FileName,St(MACLSetError),FileName);
+ uiMsg(UIERROR_ACLSET,Arc.FileName,FileName);
+ DWORD LastError=GetLastError();
ErrHandler.SysErrMsg();
- ErrHandler.SetErrorCode(WARNING);
+ if (LastError==ERROR_ACCESS_DENIED && !IsUserAdmin())
+ uiMsg(UIERROR_NEEDADMIN);
+ ErrHandler.SetErrorCode(RARX_WARNING);
}
}
#endif
-void ExtractACLNew(Archive &Arc,char *FileName,wchar *FileNameW)
+void ExtractACL(Archive &Arc,const wchar *FileName)
{
- if (!WinNT())
- return;
-
Array<byte> SubData;
- if (!Arc.ReadSubData(&SubData,NULL))
+ if (!Arc.ReadSubData(&SubData,NULL,false))
return;
- SetPrivileges();
+ SetACLPrivileges();
- SECURITY_INFORMATION si=OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION|
- DACL_SECURITY_INFORMATION;
+ SECURITY_INFORMATION si=OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION|
+ DACL_SECURITY_INFORMATION;
if (ReadSacl)
si|=SACL_SECURITY_INFORMATION;
SECURITY_DESCRIPTOR *sd=(SECURITY_DESCRIPTOR *)&SubData[0];
- int SetCode;
- if (FileNameW!=NULL)
- SetCode=SetFileSecurityW(FileNameW,si,sd);
- else
- SetCode=SetFileSecurity(FileName,si,sd);
+ int SetCode=SetFileSecurity(FileName,si,sd);
+ if (!SetCode)
+ {
+ wchar LongName[NM];
+ if (GetWinLongPath(FileName,LongName,ASIZE(LongName)))
+ SetCode=SetFileSecurity(LongName,si,sd);
+ }
if (!SetCode)
{
- Log(Arc.FileName,St(MACLSetError),FileName);
+ uiMsg(UIERROR_ACLSET,Arc.FileName,FileName);
+ DWORD LastError=GetLastError();
ErrHandler.SysErrMsg();
- ErrHandler.SetErrorCode(WARNING);
+ if (LastError==ERROR_ACCESS_DENIED && !IsUserAdmin())
+ uiMsg(UIERROR_NEEDADMIN);
+ ErrHandler.SetErrorCode(RARX_WARNING);
}
}
-void SetPrivileges()
+void SetACLPrivileges()
{
static bool InitDone=false;
if (InitDone)
return;
+
+ if (SetPrivilege(SE_SECURITY_NAME))
+ ReadSacl=true;
+ SetPrivilege(SE_RESTORE_NAME);
+
InitDone=true;
+}
- HANDLE hToken;
- if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
- return;
+bool SetPrivilege(LPCTSTR PrivName)
+{
+ bool Success=false;
- TOKEN_PRIVILEGES tp;
- tp.PrivilegeCount = 1;
- tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+ HANDLE hToken;
+ if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
+ {
+ TOKEN_PRIVILEGES tp;
+ tp.PrivilegeCount = 1;
+ tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
- if (LookupPrivilegeValue(NULL,SE_SECURITY_NAME,&tp.Privileges[0].Luid))
- if (AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL) &&
+ if (LookupPrivilegeValue(NULL,PrivName,&tp.Privileges[0].Luid) &&
+ AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL) &&
GetLastError() == ERROR_SUCCESS)
- ReadSacl=true;
+ Success=true;
- if (LookupPrivilegeValue(NULL,SE_RESTORE_NAME,&tp.Privileges[0].Luid))
- AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL);
+ CloseHandle(hToken);
+ }
- CloseHandle(hToken);
+ return Success;
}
diff --git a/unrar/unrar/win32lnk.cpp b/unrar/unrar/win32lnk.cpp
new file mode 100644
index 0000000..759c490
--- /dev/null
+++ b/unrar/unrar/win32lnk.cpp
@@ -0,0 +1,194 @@
+#define SYMLINK_FLAG_RELATIVE 1
+
+typedef struct _REPARSE_DATA_BUFFER {
+ ULONG ReparseTag;
+ USHORT ReparseDataLength;
+ USHORT Reserved;
+ union {
+ struct {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ ULONG Flags;
+ WCHAR PathBuffer[1];
+ } SymbolicLinkReparseBuffer;
+ struct {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ WCHAR PathBuffer[1];
+ } MountPointReparseBuffer;
+ struct {
+ UCHAR DataBuffer[1];
+ } GenericReparseBuffer;
+ };
+} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
+
+
+
+
+bool CreateReparsePoint(CommandData *Cmd,const wchar *Name,FileHeader *hd)
+{
+ static bool PrivSet=false;
+ if (!PrivSet)
+ {
+ SetPrivilege(SE_RESTORE_NAME);
+ // Not sure if we really need it, but let's request anyway.
+ SetPrivilege(SE_CREATE_SYMBOLIC_LINK_NAME);
+ PrivSet=true;
+ }
+
+ const DWORD BufSize=sizeof(REPARSE_DATA_BUFFER)+2*NM*sizeof(wchar)+1024;
+ Array<byte> Buf(BufSize);
+ REPARSE_DATA_BUFFER *rdb=(REPARSE_DATA_BUFFER *)&Buf[0];
+
+ wchar SubstName[NM];
+ wcsncpyz(SubstName,hd->RedirName,ASIZE(SubstName));
+ size_t SubstLength=wcslen(SubstName);
+
+ wchar PrintName[NM],*PrintNameSrc=SubstName,*PrintNameDst=PrintName;
+ bool WinPrefix=wcsncmp(PrintNameSrc,L"\\??\\",4)==0;
+ if (WinPrefix)
+ PrintNameSrc+=4;
+ if (WinPrefix && wcsncmp(PrintNameSrc,L"UNC\\",4)==0)
+ {
+ *(PrintNameDst++)='\\'; // Insert second \ in beginning of share name.
+ PrintNameSrc+=3;
+ }
+ wcscpy(PrintNameDst,PrintNameSrc);
+
+ size_t PrintLength=wcslen(PrintName);
+
+ bool AbsPath=WinPrefix;
+ // IsFullPath is not really needed here, AbsPath check is enough.
+ // We added it just for extra safety, in case some Windows version would
+ // allow to create absolute targets with SYMLINK_FLAG_RELATIVE.
+ // Use hd->FileName instead of Name, since Name can include the destination
+ // path as a prefix, which can confuse IsRelativeSymlinkSafe algorithm.
+ if (!Cmd->AbsoluteLinks && (AbsPath || IsFullPath(hd->RedirName) ||
+ !IsRelativeSymlinkSafe(Cmd,hd->FileName,Name,hd->RedirName)))
+ return false;
+
+ CreatePath(Name,true,Cmd->DisableNames);
+
+ // Overwrite prompt was already issued and confirmed earlier, so we can
+ // remove existing symlink or regular file here. PrepareToDelete was also
+ // called earlier inside of uiAskReplaceEx.
+ if (FileExist(Name))
+ if (IsDir(GetFileAttr(Name)))
+ DelDir(Name);
+ else
+ DelFile(Name);
+
+ // 'DirTarget' check is important for Unix symlinks to directories.
+ // Unix symlinks do not have their own 'directory' attribute.
+ if (hd->Dir || hd->DirTarget)
+ {
+ if (!CreateDirectory(Name,NULL))
+ {
+ uiMsg(UIERROR_DIRCREATE,UINULL,Name);
+ ErrHandler.SetErrorCode(RARX_CREATE);
+ return false;
+ }
+ }
+ else
+ {
+ HANDLE hFile=CreateFile(Name,GENERIC_WRITE,0,NULL,CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL);
+ if (hFile == INVALID_HANDLE_VALUE)
+ {
+ ErrHandler.CreateErrorMsg(Name);
+ return false;
+ }
+ CloseHandle(hFile);
+ }
+
+
+ if (hd->RedirType==FSREDIR_JUNCTION)
+ {
+ rdb->ReparseTag=IO_REPARSE_TAG_MOUNT_POINT;
+ rdb->ReparseDataLength=USHORT(
+ sizeof(rdb->MountPointReparseBuffer.SubstituteNameOffset)+
+ sizeof(rdb->MountPointReparseBuffer.SubstituteNameLength)+
+ sizeof(rdb->MountPointReparseBuffer.PrintNameOffset)+
+ sizeof(rdb->MountPointReparseBuffer.PrintNameLength)+
+ (SubstLength+1)*sizeof(WCHAR)+(PrintLength+1)*sizeof(WCHAR));
+ rdb->Reserved=0;
+
+ rdb->MountPointReparseBuffer.SubstituteNameOffset=0;
+ rdb->MountPointReparseBuffer.SubstituteNameLength=USHORT(SubstLength*sizeof(WCHAR));
+ wcscpy(rdb->MountPointReparseBuffer.PathBuffer,SubstName);
+
+ rdb->MountPointReparseBuffer.PrintNameOffset=USHORT((SubstLength+1)*sizeof(WCHAR));
+ rdb->MountPointReparseBuffer.PrintNameLength=USHORT(PrintLength*sizeof(WCHAR));
+ wcscpy(rdb->MountPointReparseBuffer.PathBuffer+SubstLength+1,PrintName);
+ }
+ else
+ if (hd->RedirType==FSREDIR_WINSYMLINK || hd->RedirType==FSREDIR_UNIXSYMLINK)
+ {
+ rdb->ReparseTag=IO_REPARSE_TAG_SYMLINK;
+ rdb->ReparseDataLength=USHORT(
+ sizeof(rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset)+
+ sizeof(rdb->SymbolicLinkReparseBuffer.SubstituteNameLength)+
+ sizeof(rdb->SymbolicLinkReparseBuffer.PrintNameOffset)+
+ sizeof(rdb->SymbolicLinkReparseBuffer.PrintNameLength)+
+ sizeof(rdb->SymbolicLinkReparseBuffer.Flags)+
+ (SubstLength+1)*sizeof(WCHAR)+(PrintLength+1)*sizeof(WCHAR));
+ rdb->Reserved=0;
+
+ rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset=0;
+ rdb->SymbolicLinkReparseBuffer.SubstituteNameLength=USHORT(SubstLength*sizeof(WCHAR));
+ wcscpy(rdb->SymbolicLinkReparseBuffer.PathBuffer,SubstName);
+
+ rdb->SymbolicLinkReparseBuffer.PrintNameOffset=USHORT((SubstLength+1)*sizeof(WCHAR));
+ rdb->SymbolicLinkReparseBuffer.PrintNameLength=USHORT(PrintLength*sizeof(WCHAR));
+ wcscpy(rdb->SymbolicLinkReparseBuffer.PathBuffer+SubstLength+1,PrintName);
+
+ rdb->SymbolicLinkReparseBuffer.Flags=AbsPath ? 0:SYMLINK_FLAG_RELATIVE;
+ }
+ else
+ return false;
+
+ HANDLE hFile=CreateFile(Name,GENERIC_READ|GENERIC_WRITE,0,NULL,
+ OPEN_EXISTING,FILE_FLAG_OPEN_REPARSE_POINT|
+ FILE_FLAG_BACKUP_SEMANTICS,NULL);
+ if (hFile==INVALID_HANDLE_VALUE)
+ {
+ ErrHandler.CreateErrorMsg(Name);
+ ErrHandler.SetErrorCode(RARX_CREATE);
+ return false;
+ }
+
+ DWORD Returned;
+ if (!DeviceIoControl(hFile,FSCTL_SET_REPARSE_POINT,rdb,
+ FIELD_OFFSET(REPARSE_DATA_BUFFER,GenericReparseBuffer)+
+ rdb->ReparseDataLength,NULL,0,&Returned,NULL))
+ {
+ CloseHandle(hFile);
+ uiMsg(UIERROR_SLINKCREATE,UINULL,Name);
+
+ DWORD LastError=GetLastError();
+ if ((LastError==ERROR_ACCESS_DENIED || LastError==ERROR_PRIVILEGE_NOT_HELD) &&
+ !IsUserAdmin())
+ uiMsg(UIERROR_NEEDADMIN);
+ ErrHandler.SysErrMsg();
+ ErrHandler.SetErrorCode(RARX_CREATE);
+
+ if (hd->Dir)
+ RemoveDirectory(Name);
+ else
+ DeleteFile(Name);
+ return false;
+ }
+ File LinkFile;
+ LinkFile.SetHandle(hFile);
+ LinkFile.SetOpenFileTime(
+ Cmd->xmtime==EXTTIME_NONE ? NULL:&hd->mtime,
+ Cmd->xctime==EXTTIME_NONE ? NULL:&hd->ctime,
+ Cmd->xatime==EXTTIME_NONE ? NULL:&hd->atime);
+ LinkFile.Close();
+ if (!Cmd->IgnoreGeneralAttr)
+ SetFileAttr(Name,hd->FileAttr);
+ return true;
+}
diff --git a/unrar/unrar/win32stm.cpp b/unrar/unrar/win32stm.cpp
index 7909291..3b77d2a 100644
--- a/unrar/unrar/win32stm.cpp
+++ b/unrar/unrar/win32stm.cpp
@@ -1,154 +1,184 @@
-
-#ifndef SFX_MODULE
-void ExtractStreams(Archive &Arc,char *FileName,wchar *FileNameW)
+#ifdef _WIN_ALL
+// StreamName must include the leading ':'.
+static bool IsNtfsReservedStream(const wchar *StreamName)
{
- if (!WinNT())
- return;
+ const wchar *Reserved[]{
+ L"::$ATTRIBUTE_LIST",L"::$BITMAP",L"::$DATA",L"::$EA",L"::$EA_INFORMATION",
+ L"::$FILE_NAME",L"::$INDEX_ALLOCATION",L":$I30:$INDEX_ALLOCATION",
+ L"::$INDEX_ROOT",L"::$LOGGED_UTILITY_STREAM",L":$EFS:$LOGGED_UTILITY_STREAM",
+ L":$TXF_DATA:$LOGGED_UTILITY_STREAM",L"::$OBJECT_ID",L"::$REPARSE_POINT"
+ };
+ for (const wchar *Name : Reserved)
+ if (wcsicomp(StreamName,Name)==0)
+ return true;
+ return false;
+}
+#endif
+
- if (Arc.HeaderCRC!=Arc.StreamHead.HeadCRC)
+#if !defined(SFX_MODULE) && defined(_WIN_ALL)
+void ExtractStreams20(Archive &Arc,const wchar *FileName)
+{
+ if (Arc.BrokenHeader)
{
-#ifndef SILENT
- Log(Arc.FileName,St(MStreamBroken),FileName);
-#endif
- ErrHandler.SetErrorCode(CRC_ERROR);
+ uiMsg(UIERROR_STREAMBROKEN,Arc.FileName,FileName);
+ ErrHandler.SetErrorCode(RARX_CRC);
return;
}
- if (Arc.StreamHead.Method<0x31 || Arc.StreamHead.Method>0x35 || Arc.StreamHead.UnpVer>PACK_VER)
+ if (Arc.StreamHead.Method<0x31 || Arc.StreamHead.Method>0x35 || Arc.StreamHead.UnpVer>VER_PACK)
{
-#ifndef SILENT
- Log(Arc.FileName,St(MStreamUnknown),FileName);
-#endif
- ErrHandler.SetErrorCode(WARNING);
+ uiMsg(UIERROR_STREAMUNKNOWN,Arc.FileName,FileName);
+ ErrHandler.SetErrorCode(RARX_WARNING);
return;
}
- char StreamName[NM+2];
+ wchar StreamName[NM+2];
if (FileName[0]!=0 && FileName[1]==0)
{
- strcpy(StreamName,".\\");
- strcpy(StreamName+2,FileName);
+ // Convert single character names like f:stream to .\f:stream to
+ // resolve the ambiguity with drive letters.
+ wcsncpyz(StreamName,L".\\",ASIZE(StreamName));
+ wcsncatz(StreamName,FileName,ASIZE(StreamName));
}
else
- strcpy(StreamName,FileName);
- if (strlen(StreamName)+strlen((char *)Arc.StreamHead.StreamName)>=sizeof(StreamName) ||
+ wcsncpyz(StreamName,FileName,ASIZE(StreamName));
+ if (wcslen(StreamName)+strlen(Arc.StreamHead.StreamName)>=ASIZE(StreamName) ||
Arc.StreamHead.StreamName[0]!=':')
{
-#ifndef SILENT
- Log(Arc.FileName,St(MStreamBroken),FileName);
-#endif
- ErrHandler.SetErrorCode(CRC_ERROR);
+ uiMsg(UIERROR_STREAMBROKEN,Arc.FileName,FileName);
+ ErrHandler.SetErrorCode(RARX_CRC);
return;
}
- ConvertPath((char *)Arc.StreamHead.StreamName+1,(char *)Arc.StreamHead.StreamName+1);
+ wchar StoredName[NM];
+ CharToWide(Arc.StreamHead.StreamName,StoredName,ASIZE(StoredName));
+ ConvertPath(StoredName+1,StoredName+1,ASIZE(StoredName)-1);
+
- strcat(StreamName,(char *)Arc.StreamHead.StreamName);
+ if (IsNtfsReservedStream(StoredName))
+ return;
+
+ wcsncatz(StreamName,StoredName,ASIZE(StreamName));
FindData fd;
- bool Found=FindFile::FastFind(FileName,FileNameW,&fd);
+ bool Found=FindFile::FastFind(FileName,&fd);
- if (fd.FileAttr & FILE_ATTRIBUTE_READONLY)
- SetFileAttr(FileName,FileNameW,fd.FileAttr & ~FILE_ATTRIBUTE_READONLY);
+ if ((fd.FileAttr & FILE_ATTRIBUTE_READONLY)!=0)
+ SetFileAttr(FileName,fd.FileAttr & ~FILE_ATTRIBUTE_READONLY);
File CurFile;
if (CurFile.WCreate(StreamName))
{
ComprDataIO DataIO;
Unpack Unpack(&DataIO);
- Unpack.Init();
+ Unpack.Init(0x10000,false);
- Array<unsigned char> UnpData(Arc.StreamHead.UnpSize);
DataIO.SetPackedSizeToRead(Arc.StreamHead.DataSize);
DataIO.EnableShowProgress(false);
DataIO.SetFiles(&Arc,&CurFile);
+ DataIO.UnpHash.Init(HASH_CRC32,1);
Unpack.SetDestSize(Arc.StreamHead.UnpSize);
Unpack.DoUnpack(Arc.StreamHead.UnpVer,false);
- if (Arc.StreamHead.StreamCRC!=~DataIO.UnpFileCRC)
+ if (Arc.StreamHead.StreamCRC!=DataIO.UnpHash.GetCRC32())
{
-#ifndef SILENT
- Log(Arc.FileName,St(MStreamBroken),StreamName);
-#endif
- ErrHandler.SetErrorCode(CRC_ERROR);
+ uiMsg(UIERROR_STREAMBROKEN,Arc.FileName,StreamName);
+ ErrHandler.SetErrorCode(RARX_CRC);
}
else
CurFile.Close();
}
File HostFile;
- if (Found && HostFile.Open(FileName,FileNameW,true,true))
+ if (Found && HostFile.Open(FileName,FMF_OPENSHARED|FMF_UPDATE))
SetFileTime(HostFile.GetHandle(),&fd.ftCreationTime,&fd.ftLastAccessTime,
&fd.ftLastWriteTime);
- if (fd.FileAttr & FILE_ATTRIBUTE_READONLY)
- SetFileAttr(FileName,FileNameW,fd.FileAttr);
+ if ((fd.FileAttr & FILE_ATTRIBUTE_READONLY)!=0)
+ SetFileAttr(FileName,fd.FileAttr);
}
#endif
-void ExtractStreamsNew(Archive &Arc,char *FileName,wchar *FileNameW)
+#ifdef _WIN_ALL
+void ExtractStreams(Archive &Arc,const wchar *FileName,bool TestMode)
{
- if (!WinNT())
- return;
-
- wchar NameW[NM];
- if (FileNameW!=NULL && *FileNameW!=0)
- strcpyw(NameW,FileNameW);
- else
- CharToWide(FileName,NameW);
- wchar StreamNameW[NM+2];
- if (NameW[0]!=0 && NameW[1]==0)
+ wchar FullName[NM+2];
+ if (FileName[0]!=0 && FileName[1]==0)
{
- strcpyw(StreamNameW,L".\\");
- strcpyw(StreamNameW+2,NameW);
+ // Convert single character names like f:stream to .\f:stream to
+ // resolve the ambiguity with drive letters.
+ wcsncpyz(FullName,L".\\",ASIZE(FullName));
+ wcsncatz(FullName,FileName,ASIZE(FullName));
}
else
- strcpyw(StreamNameW,NameW);
-
- wchar *DestName=StreamNameW+strlenw(StreamNameW);
- byte *SrcName=&Arc.SubHead.SubData[0];
- int DestSize=Arc.SubHead.SubData.Size()/2;
+ wcsncpyz(FullName,FileName,ASIZE(FullName));
- if (strlenw(StreamNameW)+DestSize>=sizeof(StreamNameW)/sizeof(StreamNameW[0]))
+ wchar StreamName[NM];
+ GetStreamNameNTFS(Arc,StreamName,ASIZE(StreamName));
+ if (*StreamName!=':')
{
-#if !defined(SILENT) && !defined(SFX_MODULE)
- Log(Arc.FileName,St(MStreamBroken),FileName);
-#endif
- ErrHandler.SetErrorCode(CRC_ERROR);
+ uiMsg(UIERROR_STREAMBROKEN,Arc.FileName,FileName);
+ ErrHandler.SetErrorCode(RARX_CRC);
return;
}
- RawToWide(SrcName,DestName,DestSize);
- DestName[DestSize]=0;
-
- if (*DestName!=':')
+ if (TestMode)
{
-#if !defined(SILENT) && !defined(SFX_MODULE)
- Log(Arc.FileName,St(MStreamBroken),FileName);
-#endif
- ErrHandler.SetErrorCode(CRC_ERROR);
+ File CurFile;
+ Arc.ReadSubData(NULL,&CurFile,true);
return;
}
- ConvertPath(DestName+1,DestName+1);
+ wcsncatz(FullName,StreamName,ASIZE(FullName));
+
+
+ if (IsNtfsReservedStream(StreamName))
+ return;
FindData fd;
- bool Found=FindFile::FastFind(FileName,FileNameW,&fd);
+ bool HostFound=FindFile::FastFind(FileName,&fd);
- if (fd.FileAttr & FILE_ATTRIBUTE_READONLY)
- SetFileAttr(FileName,FileNameW,fd.FileAttr & ~FILE_ATTRIBUTE_READONLY);
- char StreamName[NM];
- WideToChar(StreamNameW,StreamName);
+ if ((fd.FileAttr & FILE_ATTRIBUTE_READONLY)!=0)
+ SetFileAttr(FileName,fd.FileAttr & ~FILE_ATTRIBUTE_READONLY);
File CurFile;
- if (CurFile.WCreate(StreamName,StreamNameW) && Arc.ReadSubData(NULL,&CurFile))
- CurFile.Close();
+
+ if (CurFile.WCreate(FullName))
+ {
+ if (Arc.ReadSubData(NULL,&CurFile,false))
+ CurFile.Close();
+ }
+
+ // Restoring original file timestamps.
File HostFile;
- if (Found && HostFile.Open(FileName,FileNameW,true,true))
+ if (HostFound && HostFile.Open(FileName,FMF_OPENSHARED|FMF_UPDATE))
SetFileTime(HostFile.GetHandle(),&fd.ftCreationTime,&fd.ftLastAccessTime,
&fd.ftLastWriteTime);
// Restoring original file attributes. Important if file was read only
// or did not have "Archive" attribute
- SetFileAttr(FileName,FileNameW,fd.FileAttr);
+ SetFileAttr(FileName,fd.FileAttr);
+}
+#endif
+
+
+void GetStreamNameNTFS(Archive &Arc,wchar *StreamName,size_t MaxSize)
+{
+ byte *Data=&Arc.SubHead.SubData[0];
+ size_t DataSize=Arc.SubHead.SubData.Size();
+ if (Arc.Format==RARFMT15)
+ {
+ size_t DestSize=Min(DataSize/2,MaxSize-1);
+ RawToWide(Data,StreamName,DestSize);
+ StreamName[DestSize]=0;
+ }
+ else
+ {
+ char UtfString[NM*4];
+ size_t DestSize=Min(DataSize,ASIZE(UtfString)-1);
+ memcpy(UtfString,Data,DestSize);
+ UtfString[DestSize]=0;
+ UtfToWide(UtfString,StreamName,MaxSize);
+ }
}